@shopgate/pwa-common 7.30.0-alpha.8 → 7.30.0-alpha.9
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/components/CountdownTimer/spec.js +37 -37
- package/components/HtmlSanitizer/spec.js +15 -9
- package/components/Swiper/components/SwiperItem/spec.js +4 -0
- package/components/Widgets/components/Widget/spec.js +1 -1
- package/components/Widgets/components/WidgetGrid/spec.js +1 -1
- package/components/Widgets/spec.js +22 -7
- package/package.json +3 -4
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { shallow } from 'enzyme';
|
|
3
|
+
import { act } from 'react-dom/test-utils';
|
|
3
4
|
import CountdownTimer, { getFormattedTimeString } from "./index";
|
|
4
5
|
describe('<CountdownTimer>', () => {
|
|
5
6
|
jest.useFakeTimers();
|
|
@@ -36,24 +37,23 @@ describe('<CountdownTimer>', () => {
|
|
|
36
37
|
*/
|
|
37
38
|
const performFormatCheck = (remainingDays, remainingHours, remainingMinutes, remainingSeconds) => {
|
|
38
39
|
jest.clearAllTimers();
|
|
39
|
-
|
|
40
|
+
const intervalSpy = jest.spyOn(global, 'setInterval');
|
|
40
41
|
const wrapper = createTimerElement(remainingDays, remainingHours, remainingMinutes, remainingSeconds, null);
|
|
41
|
-
const expectedTimeFormat = getFormattedTimeString(remainingDays, remainingHours, remainingMinutes, remainingSeconds - 1
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
jest.runTimersToTime(1000);
|
|
42
|
+
const expectedTimeFormat = getFormattedTimeString(remainingDays, remainingHours, remainingMinutes, remainingSeconds - 1);
|
|
43
|
+
expect(intervalSpy).toHaveBeenCalledTimes(1);
|
|
44
|
+
act(() => {
|
|
45
|
+
jest.advanceTimersByTime(1000);
|
|
46
|
+
});
|
|
47
47
|
wrapper.update();
|
|
48
48
|
const {
|
|
49
49
|
params,
|
|
50
50
|
string
|
|
51
51
|
} = wrapper.props();
|
|
52
|
-
|
|
52
|
+
expect({
|
|
53
53
|
params,
|
|
54
54
|
string
|
|
55
|
-
};
|
|
56
|
-
|
|
55
|
+
}).toEqual(expectedTimeFormat);
|
|
56
|
+
intervalSpy.mockRestore();
|
|
57
57
|
};
|
|
58
58
|
it('should render the correct time for < 24h', () => performFormatCheck(0, 0, 0, 5));
|
|
59
59
|
it('should render the correct time for 24h - 48h', () => performFormatCheck(1, 12, 6, 5));
|
|
@@ -62,46 +62,44 @@ describe('<CountdownTimer>', () => {
|
|
|
62
62
|
jest.clearAllTimers();
|
|
63
63
|
const wrapper = createTimerElement(-1, -2, -3, -5, null);
|
|
64
64
|
const expectedTimeFormat = getFormattedTimeString(0, 0, 0, 0);
|
|
65
|
-
|
|
65
|
+
act(() => {
|
|
66
|
+
jest.advanceTimersByTime(1000);
|
|
67
|
+
});
|
|
66
68
|
const {
|
|
67
69
|
params,
|
|
68
70
|
string
|
|
69
71
|
} = wrapper.props();
|
|
70
|
-
|
|
72
|
+
expect({
|
|
71
73
|
params,
|
|
72
74
|
string
|
|
73
|
-
};
|
|
74
|
-
expect(renderedTimeFormat).toEqual(expectedTimeFormat);
|
|
75
|
+
}).toEqual(expectedTimeFormat);
|
|
75
76
|
});
|
|
76
77
|
it('should stop at 00:00:00 when the timer expires', () => {
|
|
77
78
|
const wrapper = createTimerElement(0, 0, 0, 1, null);
|
|
78
79
|
const expectedTimeFormat = getFormattedTimeString(0, 0, 0, 0);
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
jest.runTimersToTime(1000);
|
|
80
|
+
act(() => {
|
|
81
|
+
jest.advanceTimersByTime(1000);
|
|
82
|
+
});
|
|
83
83
|
wrapper.update();
|
|
84
84
|
let {
|
|
85
85
|
params,
|
|
86
86
|
string
|
|
87
87
|
} = wrapper.props();
|
|
88
|
-
|
|
88
|
+
expect({
|
|
89
89
|
params,
|
|
90
90
|
string
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
jest.runTimersToTime(1000);
|
|
91
|
+
}).toEqual(expectedTimeFormat);
|
|
92
|
+
act(() => {
|
|
93
|
+
jest.advanceTimersByTime(1000);
|
|
94
|
+
});
|
|
96
95
|
({
|
|
97
96
|
params,
|
|
98
97
|
string
|
|
99
98
|
} = wrapper.props());
|
|
100
|
-
|
|
99
|
+
expect({
|
|
101
100
|
params,
|
|
102
101
|
string
|
|
103
|
-
};
|
|
104
|
-
expect(renderedTimeFormat).toEqual(expectedTimeFormat);
|
|
102
|
+
}).toEqual(expectedTimeFormat);
|
|
105
103
|
});
|
|
106
104
|
it('should invoke the callback when the timer expires', () => {
|
|
107
105
|
let timesCallbackInvoked = 0;
|
|
@@ -112,17 +110,17 @@ describe('<CountdownTimer>', () => {
|
|
|
112
110
|
timesCallbackInvoked += 1;
|
|
113
111
|
};
|
|
114
112
|
createTimerElement(0, 0, 0, 2, callback);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
113
|
+
act(() => {
|
|
114
|
+
jest.advanceTimersByTime(1000);
|
|
115
|
+
});
|
|
118
116
|
expect(timesCallbackInvoked).toBe(0);
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
act(() => {
|
|
118
|
+
jest.advanceTimersByTime(1000);
|
|
119
|
+
});
|
|
122
120
|
expect(timesCallbackInvoked).toBe(1);
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
121
|
+
act(() => {
|
|
122
|
+
jest.advanceTimersByTime(1000);
|
|
123
|
+
});
|
|
126
124
|
expect(timesCallbackInvoked).toBe(1);
|
|
127
125
|
});
|
|
128
126
|
it('should not invoke the callback when the timeout is already expired.', () => {
|
|
@@ -134,7 +132,9 @@ describe('<CountdownTimer>', () => {
|
|
|
134
132
|
timesCallbackInvoked += 1;
|
|
135
133
|
};
|
|
136
134
|
createTimerElement(0, 0, 0, 0, callback);
|
|
137
|
-
|
|
135
|
+
act(() => {
|
|
136
|
+
jest.advanceTimersByTime(1000);
|
|
137
|
+
});
|
|
138
138
|
expect(timesCallbackInvoked).toBe(0);
|
|
139
139
|
});
|
|
140
140
|
});
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import _extends from "@babel/runtime/helpers/extends";
|
|
2
|
+
/** @jest-environment jsdom */
|
|
3
|
+
|
|
2
4
|
import React from 'react';
|
|
3
5
|
import { mount } from 'enzyme';
|
|
4
|
-
import { JSDOM } from 'jsdom';
|
|
5
6
|
import { embeddedMedia } from '@shopgate/pwa-common/collections';
|
|
6
7
|
import HtmlSanitizer from "./index";
|
|
7
8
|
jest.mock("../EmbeddedMedia", () => ({
|
|
@@ -12,7 +13,7 @@ jest.mock("./connector", () => Cmp => Cmp);
|
|
|
12
13
|
/**
|
|
13
14
|
* @param {string} html HTML markup.
|
|
14
15
|
* @param {Object} props Component props.
|
|
15
|
-
* @returns {JSX}
|
|
16
|
+
* @returns {JSX.Element}
|
|
16
17
|
*/
|
|
17
18
|
const createWrapper = (html, props = {}) => mount(/*#__PURE__*/React.createElement(HtmlSanitizer, _extends({
|
|
18
19
|
navigate: () => {}
|
|
@@ -125,7 +126,9 @@ describe('<HtmlSanitizer />', () => {
|
|
|
125
126
|
mockedHandleClick.mockClear();
|
|
126
127
|
});
|
|
127
128
|
it('follows a link from a plain <a>', () => {
|
|
128
|
-
|
|
129
|
+
// Create a real container element in the current document
|
|
130
|
+
const attachNode = document.createElement('div');
|
|
131
|
+
document.body.appendChild(attachNode);
|
|
129
132
|
const html = '<a id="link" href="#follow-me-and-everything-is-alright">Plain Link</a>';
|
|
130
133
|
const wrapper = mount(/*#__PURE__*/React.createElement(HtmlSanitizer, {
|
|
131
134
|
decode: true,
|
|
@@ -134,9 +137,9 @@ describe('<HtmlSanitizer />', () => {
|
|
|
134
137
|
},
|
|
135
138
|
navigate: () => {}
|
|
136
139
|
}, html), {
|
|
137
|
-
attachTo:
|
|
140
|
+
attachTo: attachNode
|
|
138
141
|
});
|
|
139
|
-
const aTag =
|
|
142
|
+
const aTag = attachNode.getElementsByTagName('a')[0];
|
|
140
143
|
aTag.closest = () => aTag;
|
|
141
144
|
const event = {
|
|
142
145
|
target: aTag,
|
|
@@ -145,9 +148,12 @@ describe('<HtmlSanitizer />', () => {
|
|
|
145
148
|
wrapper.instance().handleTap(event);
|
|
146
149
|
expect(mockedHandleClick).toHaveBeenCalledTimes(1);
|
|
147
150
|
expect(mockedHandleClick).toHaveBeenCalledWith('#follow-me-and-everything-is-alright', '');
|
|
151
|
+
wrapper.unmount();
|
|
152
|
+
attachNode.remove();
|
|
148
153
|
});
|
|
149
154
|
it('follows a link from a <a> with other HTML inside', () => {
|
|
150
|
-
const
|
|
155
|
+
const attachNode = document.createElement('div');
|
|
156
|
+
document.body.appendChild(attachNode);
|
|
151
157
|
const html = '<a id="link" target="_blank" href="#I-ll-be-the-one-to-tuck-you-in-at-night"><span>Span Link</span></a>';
|
|
152
158
|
const wrapper = mount(/*#__PURE__*/React.createElement(HtmlSanitizer, {
|
|
153
159
|
decode: true,
|
|
@@ -156,10 +162,10 @@ describe('<HtmlSanitizer />', () => {
|
|
|
156
162
|
},
|
|
157
163
|
navigate: () => {}
|
|
158
164
|
}, html), {
|
|
159
|
-
attachTo:
|
|
165
|
+
attachTo: attachNode
|
|
160
166
|
});
|
|
161
|
-
const aTag =
|
|
162
|
-
const spanTag =
|
|
167
|
+
const aTag = attachNode.getElementsByTagName('a')[0];
|
|
168
|
+
const spanTag = attachNode.getElementsByTagName('span')[0];
|
|
163
169
|
spanTag.closest = () => aTag;
|
|
164
170
|
const event = {
|
|
165
171
|
target: spanTag,
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { shallow } from 'enzyme';
|
|
3
3
|
import SwiperItem from '.';
|
|
4
|
+
jest.mock('react', () => ({
|
|
5
|
+
...jest.requireActual('react'),
|
|
6
|
+
useLayoutEffect: jest.requireActual('react').useEffect
|
|
7
|
+
}));
|
|
4
8
|
describe('<SwiperItem />', () => {
|
|
5
9
|
it('should not render without children', () => {
|
|
6
10
|
const wrapper = shallow(/*#__PURE__*/React.createElement(SwiperItem, null, /*#__PURE__*/React.createElement("div", null)));
|
|
@@ -137,10 +137,16 @@ describe('<Widgets />', () => {
|
|
|
137
137
|
expect(wrapper.find('img').length).toBe(1);
|
|
138
138
|
});
|
|
139
139
|
it('should schedule a re-render when widget is scheduled', () => {
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
// Use a fixed point in time so "next full hour" is deterministic.
|
|
141
|
+
// Pick a time that's not exactly on the hour.
|
|
142
|
+
const base = new Date('2023-01-01T10:37:00.000Z');
|
|
143
|
+
jest.setSystemTime(base);
|
|
144
|
+
const minutesToNextFullHour = 60 - base.getMinutes(); // 23
|
|
145
|
+
const msToNextFullHour = minutesToNextFullHour * 60000; // 23 * 60_000
|
|
146
|
+
|
|
142
147
|
const scheduledFromMs = Date.now() + msToNextFullHour - 1;
|
|
143
148
|
const scheduledToMs = Date.now() + minutesToNextFullHour + 1000;
|
|
149
|
+
|
|
144
150
|
/* eslint-disable camelcase */
|
|
145
151
|
const widgets = [{
|
|
146
152
|
col: 0,
|
|
@@ -159,18 +165,27 @@ describe('<Widgets />', () => {
|
|
|
159
165
|
type: '@shopgate/commerce-widgets/image'
|
|
160
166
|
}];
|
|
161
167
|
/* eslint-enable camelcase */
|
|
168
|
+
|
|
162
169
|
const wrapper = createWrapper(widgets);
|
|
163
170
|
const instance = wrapper.find('Widgets').instance();
|
|
171
|
+
const clearSpy = jest.spyOn(global, 'clearTimeout');
|
|
164
172
|
instance.forceUpdate = jest.fn();
|
|
173
|
+
|
|
174
|
+
// Before the schedule hits, the image should not render yet.
|
|
165
175
|
expect(wrapper.find(Image).exists()).toBe(false);
|
|
176
|
+
|
|
177
|
+
// 1) Advance to the next full hour -> first forced update.
|
|
166
178
|
jest.advanceTimersByTime(msToNextFullHour);
|
|
167
179
|
expect(instance.forceUpdate).toHaveBeenCalledTimes(1);
|
|
168
|
-
|
|
169
|
-
//
|
|
170
|
-
jest.advanceTimersByTime(
|
|
180
|
+
|
|
181
|
+
// 2) The component should schedule the next tick for +60 min.
|
|
182
|
+
jest.advanceTimersByTime(60 * 60000);
|
|
171
183
|
expect(instance.forceUpdate).toHaveBeenCalledTimes(2);
|
|
172
|
-
|
|
173
|
-
|
|
184
|
+
|
|
185
|
+
// Unmount triggers cleanup of any pending timeouts.
|
|
186
|
+
wrapper.unmount();
|
|
187
|
+
expect(clearSpy).toHaveBeenCalled();
|
|
188
|
+
clearSpy.mockRestore();
|
|
174
189
|
});
|
|
175
190
|
it('should render only wrapper when widgets array is empty', () => {
|
|
176
191
|
const widgets = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shopgate/pwa-common",
|
|
3
|
-
"version": "7.30.0-alpha.
|
|
3
|
+
"version": "7.30.0-alpha.9",
|
|
4
4
|
"description": "Common library for the Shopgate Connect PWA.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Shopgate <support@shopgate.com>",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@redux-devtools/extension": "^3.3.0",
|
|
19
19
|
"@sentry/browser": "6.0.1",
|
|
20
|
-
"@shopgate/pwa-benchmark": "7.30.0-alpha.
|
|
20
|
+
"@shopgate/pwa-benchmark": "7.30.0-alpha.9",
|
|
21
21
|
"@virtuous/conductor": "~2.5.0",
|
|
22
22
|
"@virtuous/react-conductor": "~2.5.0",
|
|
23
23
|
"@virtuous/redux-persister": "1.1.0-beta.7",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"gsap": "^3.6.0",
|
|
28
28
|
"history": "^4.9.0",
|
|
29
29
|
"intl-messageformat": "^7.8.3",
|
|
30
|
-
"jsdom": "11.12.0",
|
|
31
30
|
"path-match": "^1.2.4",
|
|
32
31
|
"react-dotdotdot": "~1.3.0",
|
|
33
32
|
"react-helmet": "^6.1.0",
|
|
@@ -42,7 +41,7 @@
|
|
|
42
41
|
"url-search-params": "^0.10.0"
|
|
43
42
|
},
|
|
44
43
|
"devDependencies": {
|
|
45
|
-
"@shopgate/pwa-core": "7.30.0-alpha.
|
|
44
|
+
"@shopgate/pwa-core": "7.30.0-alpha.9",
|
|
46
45
|
"@types/react-portal": "^3.0.9",
|
|
47
46
|
"lodash": "^4.17.21",
|
|
48
47
|
"prop-types": "~15.8.1",
|