@salesforce/pwa-kit-react-sdk 3.0.0-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +14 -0
  3. package/README.md +37 -0
  4. package/package.json +82 -0
  5. package/scripts/file-utils.js +80 -0
  6. package/scripts/file-utils.test.js +189 -0
  7. package/scripts/setup-jsdom.js +20 -0
  8. package/scripts/version.js +22 -0
  9. package/ssr/browser/main.js +122 -0
  10. package/ssr/browser/main.test.js +54 -0
  11. package/ssr/server/react-rendering.js +405 -0
  12. package/ssr/server/react-rendering.test.js +708 -0
  13. package/ssr/universal/compatibility.js +31 -0
  14. package/ssr/universal/components/_app/index.js +35 -0
  15. package/ssr/universal/components/_app/index.test.js +20 -0
  16. package/ssr/universal/components/_app-config/index.js +88 -0
  17. package/ssr/universal/components/_app-config/index.test.js +21 -0
  18. package/ssr/universal/components/_document/index.js +93 -0
  19. package/ssr/universal/components/_document/index.test.js +58 -0
  20. package/ssr/universal/components/_error/index.js +56 -0
  21. package/ssr/universal/components/_error/index.test.js +28 -0
  22. package/ssr/universal/components/app-error-boundary/index.js +115 -0
  23. package/ssr/universal/components/app-error-boundary/index.test.js +109 -0
  24. package/ssr/universal/components/fetch-strategy/index.js +42 -0
  25. package/ssr/universal/components/route-component/index.js +409 -0
  26. package/ssr/universal/components/route-component/index.test.js +375 -0
  27. package/ssr/universal/components/switch/index.js +63 -0
  28. package/ssr/universal/components/throw-404/index.js +37 -0
  29. package/ssr/universal/components/throw-404/index.test.js +26 -0
  30. package/ssr/universal/components/with-correlation-id/index.js +36 -0
  31. package/ssr/universal/components/with-legacy-get-props/index.js +86 -0
  32. package/ssr/universal/components/with-legacy-get-props/index.test.js +35 -0
  33. package/ssr/universal/components/with-react-query/index.js +103 -0
  34. package/ssr/universal/components/with-react-query/index.test.js +44 -0
  35. package/ssr/universal/contexts/index.js +71 -0
  36. package/ssr/universal/contexts/index.test.js +101 -0
  37. package/ssr/universal/errors.js +34 -0
  38. package/ssr/universal/errors.test.js +20 -0
  39. package/ssr/universal/events.js +40 -0
  40. package/ssr/universal/events.test.js +39 -0
  41. package/ssr/universal/hooks/index.js +52 -0
  42. package/ssr/universal/routes.js +16 -0
  43. package/ssr/universal/utils.client.test.js +46 -0
  44. package/ssr/universal/utils.js +60 -0
  45. package/ssr/universal/utils.server.test.js +24 -0
  46. package/utils/assets.js +120 -0
  47. package/utils/assets.test.js +106 -0
  48. package/utils/url.js +39 -0
  49. package/utils/url.test.js +47 -0
  50. package/utils/uuidv4.client.js +21 -0
  51. package/utils/uuidv4.client.test.js +27 -0
  52. package/utils/warnings.js +81 -0
  53. package/utils/warnings.test.js +48 -0
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.getAppConfig = void 0;
7
+ var _appConfig2 = _interopRequireDefault(require("./components/_app-config"));
8
+ var _withLegacyGetProps = require("./components/with-legacy-get-props");
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /*
11
+ * Copyright (c) 2022, Salesforce, Inc.
12
+ * All rights reserved.
13
+ * SPDX-License-Identifier: BSD-3-Clause
14
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
15
+ */
16
+
17
+ let _appConfig = _appConfig2.default;
18
+
19
+ /**
20
+ * If a user hasn't opted into a fetchStrategy, automatically
21
+ * opt them into getProps – this maintains backward compatibility.
22
+ *
23
+ * @private
24
+ */
25
+ const getAppConfig = () => {
26
+ if (!_appConfig.initAppState) {
27
+ _appConfig = (0, _withLegacyGetProps.withLegacyGetProps)(_appConfig);
28
+ }
29
+ return _appConfig;
30
+ };
31
+ exports.getAppConfig = getAppConfig;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /*
11
+ * Copyright (c) 2021, salesforce.com, inc.
12
+ * All rights reserved.
13
+ * SPDX-License-Identifier: BSD-3-Clause
14
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
15
+ */
16
+
17
+ /**
18
+ * @module progressive-web-sdk/ssr/universal/components/_app
19
+ */
20
+ /**
21
+ * The PWAApp is a special component that will be overridden in your project. Its
22
+ * main purpose is to provide the layout for your application as well as set up any
23
+ * global actions and data fetching via the `getProps` method. Typically this is
24
+ * where you will set up components like your application header, footer and
25
+ * navigation. The contents of this component are project specific, but the rule
26
+ * of thumb is that anything outside of your pages will be added here.
27
+ */
28
+ const App = props => /*#__PURE__*/_react.default.createElement("div", {
29
+ id: "app"
30
+ }, props.children);
31
+ App.propTypes = {
32
+ children: _propTypes.default.element.isRequired
33
+ };
34
+ var _default = App;
35
+ exports.default = _default;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+ var _react2 = require("@testing-library/react");
5
+ var _index = _interopRequireDefault(require("./index"));
6
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
7
+ /*
8
+ * Copyright (c) 2021, salesforce.com, inc.
9
+ * All rights reserved.
10
+ * SPDX-License-Identifier: BSD-3-Clause
11
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
12
+ */
13
+
14
+ describe('App', () => {
15
+ test('Renders correctly', () => {
16
+ const body = /*#__PURE__*/_react.default.createElement("p", null, "Hello world");
17
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, null, body));
18
+ expect(_react2.screen.getByText(/hello world/i)).toBeInTheDocument();
19
+ });
20
+ });
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /*
11
+ * Copyright (c) 2021, salesforce.com, inc.
12
+ * All rights reserved.
13
+ * SPDX-License-Identifier: BSD-3-Clause
14
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
15
+ */
16
+
17
+ /**
18
+ * @module progressive-web-sdk/ssr/universal/components/_app-config
19
+ */
20
+
21
+ /**
22
+ * This is a special component that can be overridden in a project. It supports
23
+ * two things:
24
+ *
25
+ * 1) Working with state management libraries such as Redux that need to set
26
+ * up <Provider> instances at the root of an application.
27
+ *
28
+ * 2) Injecting properties (such as Redux stores, API clients) into getProps()
29
+ * methods across an application. We can't use React's context to do this
30
+ * because getProps() and co. need to be static methods.
31
+ */
32
+ class AppConfig extends _react.default.Component {
33
+ /**
34
+ * Restore a state management backend from frozen state that was embedded
35
+ * into the page HTML.
36
+ *
37
+ * You can also use this to initialize a state-management library, and should
38
+ * save the instance onto `locals`.
39
+ *
40
+ * @param locals - res.locals on the server, an empty object as a substitute on
41
+ * the client.
42
+ * @param frozen - the application state, restored from JSON in the HTML.
43
+ */
44
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
45
+ static restore(locals, frozen = {}) {
46
+ // No-op by default, should be overridden in a project.
47
+ }
48
+
49
+ /**
50
+ * Freeze a state management backend for embedding into the page HTML.
51
+ *
52
+ * @param locals - res.locals on the server, an empty object as a substitute on
53
+ * the client.
54
+ * @return {Object} - the application state as an object, which will be
55
+ * serialized into JSON and embedded in the page HTML.
56
+ */
57
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
58
+ static freeze(locals) {
59
+ return undefined;
60
+ }
61
+
62
+ /**
63
+ * Return any extra arguments to be injected into `getProps` methods across
64
+ * the entire app, such as a Redux store.
65
+ *
66
+ * @param locals - res.locals on the server, an empty object as a substitute on
67
+ * the client.
68
+ * @return {Object}
69
+ */
70
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
71
+ static extraGetPropsArgs(locals) {
72
+ return {};
73
+ }
74
+
75
+ /**
76
+ * This class is a React Component in order to provide this hook, which lets
77
+ * you set up context Providers for a state-management library such as Redux.
78
+ */
79
+ render() {
80
+ return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, this.props.children);
81
+ }
82
+ }
83
+ AppConfig.propTypes = {
84
+ children: _propTypes.default.node,
85
+ locals: _propTypes.default.object
86
+ };
87
+ var _default = AppConfig;
88
+ exports.default = _default;
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+
3
+ var _index = _interopRequireDefault(require("./index"));
4
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
5
+ /*
6
+ * Copyright (c) 2021, salesforce.com, inc.
7
+ * All rights reserved.
8
+ * SPDX-License-Identifier: BSD-3-Clause
9
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
10
+ */
11
+
12
+ describe('The default AppConfig', () => {
13
+ test('methods are all no-ops', () => {
14
+ expect(_index.default.restore()).toBeUndefined();
15
+ expect(_index.default.restore({
16
+ frozen: 'any values here'
17
+ })).toBeUndefined();
18
+ expect(_index.default.freeze()).toBeUndefined();
19
+ expect(_index.default.extraGetPropsArgs()).toEqual({});
20
+ });
21
+ });
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /*
11
+ * Copyright (c) 2021, salesforce.com, inc.
12
+ * All rights reserved.
13
+ * SPDX-License-Identifier: BSD-3-Clause
14
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
15
+ */ /**
16
+ * @module progressive-web-sdk/ssr/universal/components/_document
17
+ */
18
+ /**
19
+ * The Document is a special component that can be overridden in a project, it
20
+ * provides the default document foundation for your PWA. This includes
21
+ * defaults for lang, charset, viewport, theme-color, script locations, etc.
22
+ * We do not recommend overriding this component, but if you do require fine grained control of
23
+ * your application we recommend using this implementation as a starting point to ensure
24
+ * the correct functionality of your PWA.
25
+ *
26
+ * The properties for this component will be set in the server-side rendering pipeline.
27
+ * Failure to use all these properties will most likely result in a non-functioning
28
+ * PWA.
29
+ *
30
+ * @param {Array.<Object>} props.beforeBodyEnd - The renderable elements to be placed directly before
31
+ * the end of the body tag.
32
+ * @param {object} [props.bodyAttributes] - The attributes to be applied to the documents body tag.
33
+ * <br/>
34
+ * <br/>
35
+ * <i>These are set by using `react-helmet`. Please refer to their {@link https://github.com/nfl/react-helmet/tree/5.2.0#readme|docs}
36
+ * for its usage.</i>
37
+ * @param {Array.<Object>} props.head - The elements to be placed inside the documents head tag.
38
+ * @param {string} [props.html] - The HTML to be rendered in your documents html tag.
39
+ * @param {object} [props.htmlAttributes] - The attributes to be applied to the documents html tag.
40
+ * <br/>
41
+ * <br/>
42
+ * <i>These are set by using `react-helmet`. Please refer to their {@link https://github.com/nfl/react-helmet/tree/5.2.0#readme|docs}
43
+ * for its usage.</i>
44
+ */
45
+ const Document = props => {
46
+ const {
47
+ head,
48
+ html,
49
+ afterBodyStart,
50
+ beforeBodyEnd,
51
+ htmlAttributes,
52
+ bodyAttributes
53
+ } = props;
54
+ return /*#__PURE__*/_react.default.createElement("html", _extends({
55
+ lang: "en-US"
56
+ }, htmlAttributes), /*#__PURE__*/_react.default.createElement("head", null, /*#__PURE__*/_react.default.createElement("meta", {
57
+ name: "charset",
58
+ content: "utf-8"
59
+ }), /*#__PURE__*/_react.default.createElement("meta", {
60
+ name: "viewport",
61
+ content: "width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=5.0"
62
+ }), /*#__PURE__*/_react.default.createElement("meta", {
63
+ name: "format-detection",
64
+ content: "telephone=no"
65
+ }), head.map(child => child)), /*#__PURE__*/_react.default.createElement("body", bodyAttributes, afterBodyStart.map((child, i) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
66
+ key: i
67
+ }, child)), /*#__PURE__*/_react.default.createElement("div", {
68
+ className: "react-target",
69
+ dangerouslySetInnerHTML: {
70
+ __html: html
71
+ }
72
+ }), beforeBodyEnd.map((child, i) => /*#__PURE__*/_react.default.createElement(_react.default.Fragment, {
73
+ key: i
74
+ }, child))));
75
+ };
76
+ Document.propTypes = {
77
+ afterBodyStart: _propTypes.default.arrayOf(_propTypes.default.node).isRequired,
78
+ beforeBodyEnd: _propTypes.default.arrayOf(_propTypes.default.node).isRequired,
79
+ head: _propTypes.default.arrayOf(_propTypes.default.node).isRequired,
80
+ html: _propTypes.default.string.isRequired,
81
+ htmlAttributes: _propTypes.default.object,
82
+ bodyAttributes: _propTypes.default.object
83
+ };
84
+ Document.defaultProps = {
85
+ afterBodyStart: [],
86
+ beforeBodyEnd: [],
87
+ head: [],
88
+ html: '',
89
+ htmlAttributes: {},
90
+ bodyAttributes: {}
91
+ };
92
+ var _default = Document;
93
+ exports.default = _default;
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+ var _react2 = require("@testing-library/react");
5
+ var _index = _interopRequireDefault(require("./index"));
6
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
7
+ /*
8
+ * Copyright (c) 2021, salesforce.com, inc.
9
+ * All rights reserved.
10
+ * SPDX-License-Identifier: BSD-3-Clause
11
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
12
+ */
13
+
14
+ describe('Document', () => {
15
+ test('Renders correctly', () => {
16
+ const style = /*#__PURE__*/_react.default.createElement("link", {
17
+ key: "stylesheet",
18
+ rel: "stylesheet",
19
+ href: "style.css"
20
+ });
21
+ const script = /*#__PURE__*/_react.default.createElement("script", {
22
+ key: "script",
23
+ src: "script.js"
24
+ });
25
+ const sprite = /*#__PURE__*/_react.default.createElement("svg", {
26
+ id: "__SVG_SPRITE_NODE__"
27
+ });
28
+ const html = '<p>Hello world</p>';
29
+ const bodyAttributes = {
30
+ className: 'root'
31
+ };
32
+ const htmlAttributes = {
33
+ lang: 'en'
34
+ };
35
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
36
+ head: [style],
37
+ html: html,
38
+ afterBodyStart: [sprite],
39
+ beforeBodyEnd: [script],
40
+ htmlAttributes: htmlAttributes,
41
+ bodyAttributes: bodyAttributes
42
+ }));
43
+ const scriptTag = document.querySelector('script');
44
+ const svgTag = document.querySelector('svg');
45
+ const styleTag = document.querySelector('link');
46
+ // by default, React Testing Library append the test component into a body tag, since our component is a full DOM
47
+ // there will be two body tags in the DOM, we only want to check the second one
48
+ const bodyTag = document.querySelectorAll('body')[1];
49
+ // it looks like it returns two html collection, the first one is the React Testing library, the second one is our component we are testing
50
+ const htmlTag = document.getElementsByTagName('html')[1];
51
+ expect(svgTag).toBeInTheDocument();
52
+ expect(scriptTag).toBeInTheDocument();
53
+ expect(styleTag).toBeInTheDocument();
54
+ expect(_react2.screen.getByText(/hello world/i)).toBeInTheDocument();
55
+ expect(bodyTag).toHaveAttribute('class', 'root');
56
+ expect(htmlTag).toHaveAttribute('lang', 'en');
57
+ });
58
+ });
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _propTypes = _interopRequireDefault(require("prop-types"));
9
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
10
+ /*
11
+ * Copyright (c) 2021, salesforce.com, inc.
12
+ * All rights reserved.
13
+ * SPDX-License-Identifier: BSD-3-Clause
14
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
15
+ */
16
+
17
+ /**
18
+ * @module progressive-web-sdk/ssr/universal/components/_error
19
+ */
20
+
21
+ /**
22
+ * This is a special component that can be overridden in a project. We recommend
23
+ * doing this in your project in order to cutomize its behaviour. You can accomplish
24
+ * things such as, making the error page brand specific, redirecting when errors
25
+ * occur, or adding reporting.
26
+ *
27
+ * The default Error component provides a basic layout for displaying errors
28
+ * that have occured during server-side rendering or client-side execution.
29
+ *
30
+ * @param {string} props.message - The errors message.
31
+ * @param {string} props.stack - The errors stack trace.
32
+ * <br/>
33
+ * <br/>
34
+ * <i>This property is only defined in non-production environments.</i>
35
+ * @param {number} props.status - The errors HTTP status code.
36
+ * <br/>
37
+ * <br/>
38
+ * <i>This property is typically used to distinguish 404 errors from other types.</i>
39
+ * @param {string} props.correlationId
40
+ */
41
+ const Error = ({
42
+ message,
43
+ stack,
44
+ status,
45
+ correlationId
46
+ }) => {
47
+ return /*#__PURE__*/_react.default.createElement("div", null, /*#__PURE__*/_react.default.createElement("h1", null, "Error Status: ", status), /*#__PURE__*/_react.default.createElement("div", null, "CorrelationId: ", correlationId), /*#__PURE__*/_react.default.createElement("pre", null, stack), /*#__PURE__*/_react.default.createElement("pre", null, message));
48
+ };
49
+ Error.propTypes = {
50
+ message: _propTypes.default.string.isRequired,
51
+ status: _propTypes.default.number.isRequired,
52
+ stack: _propTypes.default.string,
53
+ correlationId: _propTypes.default.string
54
+ };
55
+ var _default = Error;
56
+ exports.default = _default;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+ var _react2 = require("@testing-library/react");
5
+ var _index = _interopRequireDefault(require("./index"));
6
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
7
+ /*
8
+ * Copyright (c) 2021, salesforce.com, inc.
9
+ * All rights reserved.
10
+ * SPDX-License-Identifier: BSD-3-Clause
11
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
12
+ */
13
+
14
+ describe('Error Page', () => {
15
+ const stack = 'Error Stack';
16
+ const status = 500;
17
+ const message = 'Error message';
18
+ test('Renders correctly', () => {
19
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.default, {
20
+ message: message,
21
+ stack: stack,
22
+ status: status
23
+ }));
24
+ expect(_react2.screen.getByText(message)).toBeInTheDocument();
25
+ expect(_react2.screen.getByText(stack)).toBeInTheDocument();
26
+ expect(_react2.screen.getByText(`Error Status: ${status}`)).toBeInTheDocument();
27
+ });
28
+ });
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = exports.AppErrorContext = exports.AppErrorBoundaryWithoutRouter = void 0;
7
+ var _react = _interopRequireDefault(require("react"));
8
+ var _reactRouterDom = require("react-router-dom");
9
+ var _propTypes = _interopRequireDefault(require("prop-types"));
10
+ var _error = _interopRequireDefault(require("../../components/_error"));
11
+ var _errors = require("../../errors");
12
+ var _withCorrelationId = require("../with-correlation-id");
13
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
14
+ function _extends() { _extends = Object.assign ? Object.assign.bind() : function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } /*
15
+ * Copyright (c) 2021, salesforce.com, inc.
16
+ * All rights reserved.
17
+ * SPDX-License-Identifier: BSD-3-Clause
18
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
19
+ */
20
+ const AppErrorContext = /*#__PURE__*/_react.default.createContext();
21
+ exports.AppErrorContext = AppErrorContext;
22
+ const isProduction = process.env.NODE_ENV === 'production';
23
+
24
+ /**
25
+ * @private
26
+ */
27
+ class AppErrorBoundary extends _react.default.Component {
28
+ constructor(props) {
29
+ super(props);
30
+ this.state = {
31
+ error: props.error
32
+ };
33
+ this.onGetPropsError = this.onGetPropsError.bind(this);
34
+ }
35
+ componentDidMount() {
36
+ const {
37
+ history
38
+ } = this.props;
39
+ if (history) {
40
+ this.unlisten = history.listen(() => {
41
+ // Clear error state on location change. This is used when a user
42
+ // clicks the back button after encountering a page with an error.
43
+ if (this.state.error) {
44
+ this.setState({
45
+ error: undefined
46
+ });
47
+ }
48
+ });
49
+ }
50
+ }
51
+ componentWillUnmount() {
52
+ if (this.unlisten) {
53
+ this.unlisten();
54
+ }
55
+ }
56
+
57
+ // React's client side error boundaries
58
+ static getDerivedStateFromError(err) {
59
+ // Update state so the next render will show the fallback UI
60
+ return {
61
+ error: {
62
+ message: err.toString(),
63
+ stack: err.stack
64
+ }
65
+ };
66
+ }
67
+ onGetPropsError(err) {
68
+ if (err instanceof _errors.HTTPError) {
69
+ this.setState({
70
+ error: {
71
+ message: err.message,
72
+ status: err.status,
73
+ stack: err.stack
74
+ }
75
+ });
76
+ } else {
77
+ this.setState({
78
+ error: {
79
+ message: err ? err.toString() : '',
80
+ status: 500,
81
+ stack: err ? err.stack : ''
82
+ }
83
+ });
84
+ }
85
+ }
86
+ render() {
87
+ const {
88
+ children
89
+ } = this.props;
90
+ const error = this.state.error ? {
91
+ message: this.state.error.message,
92
+ status: this.state.error.status,
93
+ stack: isProduction ? undefined : this.state.error.stack
94
+ } : undefined;
95
+ return /*#__PURE__*/_react.default.createElement(AppErrorContext.Provider, {
96
+ value: {
97
+ onGetPropsError: this.onGetPropsError
98
+ }
99
+ }, error ? /*#__PURE__*/_react.default.createElement(_error.default, _extends({}, error, {
100
+ correlationId: this.props.correlationId
101
+ })) : children);
102
+ }
103
+ }
104
+ exports.AppErrorBoundaryWithoutRouter = AppErrorBoundary;
105
+ AppErrorBoundary.propTypes = {
106
+ children: _propTypes.default.node,
107
+ error: _propTypes.default.shape({
108
+ message: _propTypes.default.string.isRequired,
109
+ status: _propTypes.default.number.isRequired
110
+ }),
111
+ correlationId: _propTypes.default.string,
112
+ history: _propTypes.default.object
113
+ };
114
+ var _default = (0, _reactRouterDom.withRouter)((0, _withCorrelationId.withCorrelationId)(AppErrorBoundary));
115
+ exports.default = _default;
@@ -0,0 +1,109 @@
1
+ "use strict";
2
+
3
+ var _react = _interopRequireDefault(require("react"));
4
+ var _react2 = require("@testing-library/react");
5
+ var _index = require("./index");
6
+ var errors = _interopRequireWildcard(require("../../errors"));
7
+ var _sinon = _interopRequireDefault(require("sinon"));
8
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
9
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
10
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
11
+ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }
12
+ function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; } /*
13
+ * Copyright (c) 2021, salesforce.com, inc.
14
+ * All rights reserved.
15
+ * SPDX-License-Identifier: BSD-3-Clause
16
+ * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
17
+ */
18
+ describe('AppErrorBoundary', () => {
19
+ const cases = [{
20
+ content: 'test 1',
21
+ errorFactory: () => new errors.HTTPNotFound('Not found'),
22
+ afterErrorAssertions: () => {
23
+ expect(_react2.screen.getByText('Error Status: 404')).toBeInTheDocument();
24
+ expect(_react2.screen.getByText('Not found')).toBeInTheDocument();
25
+ },
26
+ variation: 'SDK HTTP Errors'
27
+ }, {
28
+ content: 'test 2',
29
+ errorFactory: () => new Error('Some other error'),
30
+ afterErrorAssertions: function () {
31
+ var _ref = _asyncToGenerator(function* () {
32
+ expect(_react2.screen.getByText('Error Status: 500')).toBeInTheDocument();
33
+ expect(_react2.screen.getByText('Error: Some other error')).toBeInTheDocument();
34
+ });
35
+ return function afterErrorAssertions() {
36
+ return _ref.apply(this, arguments);
37
+ };
38
+ }(),
39
+ variation: 'Generic Javascript Errors'
40
+ }, {
41
+ content: 'test 3',
42
+ errorFactory: () => 'Some string error',
43
+ afterErrorAssertions: () => {
44
+ expect(_react2.screen.getByText('Error Status: 500')).toBeInTheDocument();
45
+ expect(_react2.screen.getByText('Some string error')).toBeInTheDocument();
46
+ },
47
+ variation: 'Error Strings'
48
+ }, {
49
+ content: 'test 4',
50
+ errorFactory: () => undefined,
51
+ afterErrorAssertions: () => {
52
+ expect(_react2.screen.getByText('Error Status: 500')).toBeInTheDocument();
53
+ expect(document.querySelector('pre').innerHTML).toBe('');
54
+ },
55
+ variation: 'Check for message value to be empty if undefined'
56
+ }];
57
+ cases.forEach(({
58
+ content,
59
+ errorFactory,
60
+ afterErrorAssertions,
61
+ variation
62
+ }) => {
63
+ test(`Displays errors correctly (variation: ${variation})`, () => {
64
+ const ref = /*#__PURE__*/_react.default.createRef();
65
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.AppErrorBoundaryWithoutRouter, {
66
+ ref: ref
67
+ }, /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, content)));
68
+ expect(_react2.screen.getByText(content)).toBeInTheDocument();
69
+ (0, _react2.act)(() => {
70
+ ref.current.onGetPropsError(errorFactory());
71
+ });
72
+ expect(_react2.screen.queryByText(content)).toBeNull();
73
+ afterErrorAssertions();
74
+ });
75
+ test(`Watches history, when provided (variation: ${variation})`, () => {
76
+ const history = {
77
+ listen: _sinon.default.stub().returns(_sinon.default.stub())
78
+ };
79
+ const ref = /*#__PURE__*/_react.default.createRef();
80
+ (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.AppErrorBoundaryWithoutRouter, {
81
+ ref: ref,
82
+ history: history
83
+ }, /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, content)));
84
+ expect(_react2.screen.getByText(content)).toBeInTheDocument();
85
+ (0, _react2.act)(() => {
86
+ ref.current.onGetPropsError(errorFactory());
87
+ });
88
+ expect(_react2.screen.queryByText(content)).toBeNull();
89
+ afterErrorAssertions();
90
+ expect(history.listen.called).toBe(true);
91
+ });
92
+ });
93
+ test(`Display Error message from getDerivedStateFromError`, () => {
94
+ const error = new Error('test');
95
+ const result = _index.AppErrorBoundaryWithoutRouter.getDerivedStateFromError(error);
96
+ expect(result.error.message).toEqual(error.toString());
97
+ });
98
+ test(`componentWillUnmount unlistens to history`, () => {
99
+ const unlisten = jest.fn();
100
+ const history = {
101
+ listen: jest.fn().mockReturnValue(unlisten)
102
+ };
103
+ const wrapper = (0, _react2.render)( /*#__PURE__*/_react.default.createElement(_index.AppErrorBoundaryWithoutRouter, {
104
+ history: history
105
+ }, "test"));
106
+ wrapper.unmount();
107
+ expect(unlisten).toHaveBeenCalled();
108
+ });
109
+ });