Skip to content

Commit

Permalink
Add Backtrace functionality. (#99)
Browse files Browse the repository at this point in the history
* chore: update package-lock to v2

* add @backtrace-labs/react package and initailize it

* add error_user username and guard for it

* add errors for error user and sending messages

* add ErrorBoundary example

* update @backtrace-labs/react to ^0.0.3

* add global user attributes to BacktraceClient

* fix unit tests, fix double description in InventoryItem

* fix missing comma in BacktraceClient.initialize

* add backtrace-js CLI and sourcemap upload support

* add tests for 100% coverage

* update @backtrace-labs/react to 0.0.5

---------

Co-authored-by: Sebastian Alex <[email protected]>
Co-authored-by: Diego Molina <[email protected]>
  • Loading branch information
3 people authored Oct 10, 2023
1 parent 73c930c commit 7e65375
Show file tree
Hide file tree
Showing 20 changed files with 35,016 additions and 110 deletions.
11 changes: 11 additions & 0 deletions .backtracejsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"path": "./build/static/js",
"run": {
"upload": true,
"process": true
},
"upload": {
"url": "https://submit.backtrace.io/UNIVERSE/TOKEN/sourcemap",
"include-sources": true
}
}
34,729 changes: 34,682 additions & 47 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"version": "3.0.0",
"private": true,
"dependencies": {
"@backtrace-labs/react": "^0.0.5",
"@fortawesome/fontawesome-svg-core": "^1.2.35",
"@fortawesome/free-solid-svg-icons": "^5.15.3",
"@fortawesome/react-fontawesome": "^0.1.14",
Expand All @@ -28,6 +29,7 @@
"@babel/plugin-proposal-private-methods": "^7.13.0",
"@babel/preset-env": "^7.13.12",
"@babel/register": "^7.13.14",
"@backtrace-labs/javascript-cli": "^0.1.2",
"@storybook/addon-actions": "^6.2.5",
"@storybook/addon-essentials": "^6.2.5",
"@storybook/addon-links": "^6.2.5",
Expand All @@ -52,7 +54,7 @@
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"build": "react-scripts build && backtrace-js run",
"test": "react-scripts test",
"test.coverage": "react-scripts test --coverage --watchAll=false",
"test.coverage.watch": "react-scripts test --coverage --watchAll",
Expand Down
5 changes: 5 additions & 0 deletions src/components/BrokenComponent.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
const BrokenComponent = () => {
throw new Error('This component failed to render!');
};

export default BrokenComponent;
12 changes: 11 additions & 1 deletion src/components/InventoryListItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React, { useState } from "react";
import { withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { ShoppingCart } from "../utils/shopping-cart";
import { isProblemUser } from "../utils/Credentials";
import { isErrorUser, isProblemUser } from "../utils/Credentials";
import "./InventoryListItem.css";
import { ROUTES } from "../utils/Constants";
import Button, { BUTTON_SIZES, BUTTON_TYPES } from "./Button";
Expand All @@ -24,6 +24,11 @@ const InventoryListItem = (props) => {
if (itemId % 2 === 1) {
return;
}
} else if (isErrorUser()) {
// Throw an exception. This will be reported to Backtrace
if (itemId % 2 === 1) {
throw new Error('Failed to add item to the cart.');
}
}

ShoppingCart.addItem(itemId);
Expand All @@ -43,6 +48,11 @@ const InventoryListItem = (props) => {
if (itemId % 2 === 0) {
return;
}
} else if (isErrorUser()) {
// Throw an exception. This will be reported to Backtrace
if (itemId % 2 === 0) {
throw new Error('Failed to remove item from cart.');
}
}

ShoppingCart.removeItem(itemId);
Expand Down
8 changes: 8 additions & 0 deletions src/components/__tests__/BrokenComponent.tests.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { shallow } from "enzyme";
import BrokenComponent from "../BrokenComponent";

describe("BrokenComponent", () => {
it("should throw an error on render", () => {
expect(() => shallow(<BrokenComponent />)).toThrowError("This component failed to render!");
});
});
43 changes: 29 additions & 14 deletions src/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,46 @@
import 'react-app-polyfill/ie11';
import 'react-app-polyfill/stable';

import { BacktraceClient, ErrorBoundary } from '@backtrace-labs/react';
import React from 'react';
import ReactDOM from 'react-dom';
import { Route, BrowserRouter as Router } from 'react-router-dom';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import PrivateRoute from './components/PrivateRoute';
import './index.css';
import Login from './pages/Login';
import Inventory from './pages/Inventory';
import InventoryItem from './pages/InventoryItem';
import Cart from './pages/Cart';
import CheckOutStepOne from './pages/CheckOutStepOne';
import CheckOutStepTwo from './pages/CheckOutStepTwo';
import Finish from './pages/Finish';
import Inventory from './pages/Inventory';
import InventoryItem from './pages/InventoryItem';
import Login from './pages/Login';
import * as serviceWorkerRegistration from './serviceWorkerRegistration';
import { ROUTES } from './utils/Constants';
import PrivateRoute from './components/PrivateRoute';
import { currentUser } from './utils/Credentials';
import { ShoppingCart } from './utils/shopping-cart';

BacktraceClient.initialize({
name: 'Swag Store',
version: '3.0.0',
url: 'https://submit.backtrace.io/yolo/ebc14541a963ceeabccbe5ef6b8b01d12c788a7a71832e19f6f6ffe30087299c/json',
userAttributes: () => ({
user: currentUser(),
shoppingCart: ShoppingCart.getCartContents()
})
})

const routing = (
<Router>
<Route exact path={ROUTES.LOGIN} component={Login} />
<PrivateRoute path={ROUTES.INVENTORY} component={Inventory} />
<PrivateRoute path={ROUTES.INVENTORY_LIST} component={InventoryItem} />
<PrivateRoute path={ROUTES.CART} component={Cart} />
<PrivateRoute path={ROUTES.CHECKOUT_STEP_ONE} component={CheckOutStepOne} />
<PrivateRoute path={ROUTES.CHECKOUT_STEP_TWO} component={CheckOutStepTwo} />
<PrivateRoute path={ROUTES.CHECKOUT_COMPLETE} component={Finish} />
</Router>
<ErrorBoundary>
<Router>
<Route exact path={ROUTES.LOGIN} component={Login} />
<PrivateRoute path={ROUTES.INVENTORY} component={Inventory} />
<PrivateRoute path={ROUTES.INVENTORY_LIST} component={InventoryItem} />
<PrivateRoute path={ROUTES.CART} component={Cart} />
<PrivateRoute path={ROUTES.CHECKOUT_STEP_ONE} component={CheckOutStepOne} />
<PrivateRoute path={ROUTES.CHECKOUT_STEP_TWO} component={CheckOutStepTwo} />
<PrivateRoute path={ROUTES.CHECKOUT_COMPLETE} component={Finish} />
</Router>
</ErrorBoundary>
);

ReactDOM.render(routing, document.getElementById('root'));
Expand Down
8 changes: 6 additions & 2 deletions src/pages/CheckOutStepOne.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useState } from "react";
import { withRouter } from "react-router-dom";
import PropTypes from "prop-types";
import { isProblemUser } from "../utils/Credentials";
import { isProblemUser, isErrorUser } from "../utils/Credentials";
import { ROUTES } from "../utils/Constants";
import SwagLabsFooter from "../components/Footer";
import HeaderContainer from "../components/HeaderContainer";
Expand All @@ -26,6 +26,9 @@ const CheckOutStepOne = ({ history }) => {
if (isProblemUser()) {
// Overwrite the firstname also
return setFirstName(evt.target.value);
} else if (isErrorUser()) {
// Fail here with TypeError. This will be reported to Backtrace
return setLastName(evt.totallyUndefined.value)
}

setLastName(evt.target.value);
Expand All @@ -40,7 +43,8 @@ const CheckOutStepOne = ({ history }) => {
return setError("First Name is required");
}

if (!lastName) {
// Allow to pass for error_user without lastName, as it is impossible to set (errors are thrown)
if (!lastName && !isErrorUser()) {
return setError("Last Name is required");
}

Expand Down
13 changes: 9 additions & 4 deletions src/pages/CheckOutStepTwo.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import { withRouter } from 'react-router-dom';
import PropTypes from 'prop-types';
import { isProblemUser } from '../utils/Credentials';
import { isErrorUser, isProblemUser } from '../utils/Credentials';
import { ROUTES } from '../utils/Constants';
import { ShoppingCart } from '../utils/shopping-cart';
import { InventoryData } from '../utils/InventoryData';
Expand All @@ -15,10 +15,15 @@ const CheckOutStepTwo = ({ history }) => {
const clearCart = () => {
/* istanbul ignore else */
// No cart clear on order complete for the problem user
if (!isProblemUser()) {
// Wipe out our shopping cart
ShoppingCart.resetCart();
if (isProblemUser()) {
return;
} else if (isErrorUser()) {
// An unfortunate typo! This will be reported to Backtrace
ShoppingCart.cesetRart();
return;
}
// Wipe out our shopping cart
ShoppingCart.resetCart();
};
const contents = ShoppingCart.getCartContents();
let orderTotal = 0;
Expand Down
10 changes: 9 additions & 1 deletion src/pages/Inventory.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React, { useState } from "react";
import { withRouter } from "react-router-dom";
import { isPerformanceGlitchUser, isProblemUser } from "../utils/Credentials";
import { isErrorUser, isPerformanceGlitchUser, isProblemUser } from "../utils/Credentials";
import { InventoryData } from "../utils/InventoryData.js";
import InventoryListItem from "../components/InventoryListItem";
import SwagLabsFooter from "../components/Footer";
import HeaderContainer from "../components/HeaderContainer";
import { sortAsc, sortDesc, sortHiLo, sortLoHi } from "../utils/Sorting";
import Select from "../components/Select";
import "./Inventory.css";
import { BacktraceClient } from '@backtrace-labs/react';

const Inventory = () => {
const [inventoryList, setInventoryList] = useState(
Expand Down Expand Up @@ -40,6 +41,13 @@ const Inventory = () => {
// Bail out now if we're problem user so that we have a behaviour which is broken in Chrome only for sort.
// select option onclick is not supported in Chrome but works in IE and FF
return;
} else if (isErrorUser()) {
// Send an error with custom attributes to Backtrace
BacktraceClient.instance.send('Sorting is broken!', {
sortOption: event.target.value,
InventoryData
});
return alert('Sorting is broken! This error has been reported to Backtrace.');
}

setActiveOption(event.target.value);
Expand Down
38 changes: 34 additions & 4 deletions src/pages/InventoryItem.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import React, { useEffect, useState } from "react";
import { withRouter } from "react-router-dom";
import { isProblemUser } from "../utils/Credentials";
import { isProblemUser, isErrorUser } from "../utils/Credentials";
import { ROUTES } from "../utils/Constants";
import { ShoppingCart } from "../utils/shopping-cart";
import { InventoryData } from "../utils/InventoryData";
import HeaderContainer from "../components/HeaderContainer";
import Button, { BUTTON_SIZES, BUTTON_TYPES } from "../components/Button";
import SwagLabsFooter from "../components/Footer";
import "./InventoryItem.css";
import BrokenComponent from "../components/BrokenComponent";
import { ErrorBoundary } from "@backtrace-labs/react";

const InventoryItem = (props) => {
useEffect(() => {
Expand Down Expand Up @@ -69,6 +71,11 @@ const InventoryItem = (props) => {
if (itemId % 2 === 1) {
return;
}
} else if (isErrorUser()) {
// Throw an exception. This will be reported to Backtrace
if (itemId % 2 === 1) {
throw new Error('Failed to add item to the cart.');
}
}

ShoppingCart.addItem(itemId);
Expand All @@ -88,6 +95,11 @@ const InventoryItem = (props) => {
if (itemId % 2 === 0) {
return;
}
} else if (isErrorUser()) {
// Throw an exception. This will be reported to Backtrace
if (itemId % 2 === 0) {
throw new Error('Failed to remove item from cart.');
}
}

ShoppingCart.removeItem(itemId);
Expand Down Expand Up @@ -148,9 +160,27 @@ const InventoryItem = (props) => {
<div className="inventory_details_name large_size">
{item.name}
</div>
<div className="inventory_details_desc large_size">
{item.desc}
</div>

{/*
This error boundary will catch any failing renders and display fallback if anything fails inside.
The error will also be reported to Backtrace.
*/}
<ErrorBoundary
name="description-boundary"
fallback={
<div className="inventory_details_desc large_size">
A description should be here, but it failed to render! This error has been reported to Backtrace.
</div>
}
>
{
!isErrorUser() ?
<div className="inventory_details_desc large_size">
{item.desc}
</div> : <BrokenComponent />
}
</ErrorBoundary>

<div className="inventory_details_price">${item.price}</div>
<ButtonType
id={item.id}
Expand Down
20 changes: 9 additions & 11 deletions src/pages/Login.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, Fragment } from 'react';
import { withRouter } from 'react-router-dom';
import { useState } from 'react';
import './Login.css';
Expand All @@ -7,10 +7,11 @@ import {
setCredentials,
verifyCredentials,
} from '../utils/Credentials';
import { ROUTES } from '../utils/Constants';
import { ROUTES, VALID_USERNAMES, VALID_PASSWORD } from '../utils/Constants';
import InputError, { INPUT_TYPES } from '../components/InputError';
import SubmitButton from '../components/SubmitButton';
import ErrorMessage from '../components/ErrorMessage';
import { BacktraceClient } from '@backtrace-labs/react';

function Login(props) {
const { history, location } = props;
Expand Down Expand Up @@ -46,12 +47,16 @@ function Login(props) {
setCredentials(username, password);
// Catch our locked-out user and bail out
if (isLockedOutUser()) {
// Send an error with custom attributes to Backtrace
BacktraceClient.instance.send(new Error('Locked out user tried to log in.'), { username });
return setError('Sorry, this user has been locked out.');
}

// Redirect!
history.push(ROUTES.INVENTORY);
} else {
// Send an error with custom attributes to Backtrace
BacktraceClient.instance.send('Someone tried to login with invalid credentials.', { username });
return setError(
'Username and password do not match any user in this service'
);
Expand Down Expand Up @@ -121,18 +126,11 @@ function Login(props) {
<div className="login_credentials_wrap-inner">
<div id="login_credentials" className="login_credentials">
<h4>Accepted usernames are:</h4>
standard_user
<br />
locked_out_user
<br />
problem_user
<br />
performance_glitch_user
<br />
{VALID_USERNAMES.map((u, i) => <Fragment key={i}>{u}<br /></Fragment>)}
</div>
<div className="login_password">
<h4>Password for all users:</h4>
secret_sauce
{VALID_PASSWORD}
</div>
</div>
</div>
Expand Down
Loading

0 comments on commit 7e65375

Please sign in to comment.