@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.
- package/CHANGELOG.md +86 -0
- package/LICENSE +14 -0
- package/README.md +37 -0
- package/package.json +82 -0
- package/scripts/file-utils.js +80 -0
- package/scripts/file-utils.test.js +189 -0
- package/scripts/setup-jsdom.js +20 -0
- package/scripts/version.js +22 -0
- package/ssr/browser/main.js +122 -0
- package/ssr/browser/main.test.js +54 -0
- package/ssr/server/react-rendering.js +405 -0
- package/ssr/server/react-rendering.test.js +708 -0
- package/ssr/universal/compatibility.js +31 -0
- package/ssr/universal/components/_app/index.js +35 -0
- package/ssr/universal/components/_app/index.test.js +20 -0
- package/ssr/universal/components/_app-config/index.js +88 -0
- package/ssr/universal/components/_app-config/index.test.js +21 -0
- package/ssr/universal/components/_document/index.js +93 -0
- package/ssr/universal/components/_document/index.test.js +58 -0
- package/ssr/universal/components/_error/index.js +56 -0
- package/ssr/universal/components/_error/index.test.js +28 -0
- package/ssr/universal/components/app-error-boundary/index.js +115 -0
- package/ssr/universal/components/app-error-boundary/index.test.js +109 -0
- package/ssr/universal/components/fetch-strategy/index.js +42 -0
- package/ssr/universal/components/route-component/index.js +409 -0
- package/ssr/universal/components/route-component/index.test.js +375 -0
- package/ssr/universal/components/switch/index.js +63 -0
- package/ssr/universal/components/throw-404/index.js +37 -0
- package/ssr/universal/components/throw-404/index.test.js +26 -0
- package/ssr/universal/components/with-correlation-id/index.js +36 -0
- package/ssr/universal/components/with-legacy-get-props/index.js +86 -0
- package/ssr/universal/components/with-legacy-get-props/index.test.js +35 -0
- package/ssr/universal/components/with-react-query/index.js +103 -0
- package/ssr/universal/components/with-react-query/index.test.js +44 -0
- package/ssr/universal/contexts/index.js +71 -0
- package/ssr/universal/contexts/index.test.js +101 -0
- package/ssr/universal/errors.js +34 -0
- package/ssr/universal/errors.test.js +20 -0
- package/ssr/universal/events.js +40 -0
- package/ssr/universal/events.test.js +39 -0
- package/ssr/universal/hooks/index.js +52 -0
- package/ssr/universal/routes.js +16 -0
- package/ssr/universal/utils.client.test.js +46 -0
- package/ssr/universal/utils.js +60 -0
- package/ssr/universal/utils.server.test.js +24 -0
- package/utils/assets.js +120 -0
- package/utils/assets.test.js +106 -0
- package/utils/url.js +39 -0
- package/utils/url.test.js +47 -0
- package/utils/uuidv4.client.js +21 -0
- package/utils/uuidv4.client.test.js +27 -0
- package/utils/warnings.js +81 -0
- 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
|
+
});
|