@webkrafters/react-observable-context 5.0.0-rc.2 → 5.0.0-rc.4
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/package.json +17 -4
- package/dist/main/hooks/use-prehooks-ref/index.test.d.ts +0 -1
- package/dist/main/hooks/use-prehooks-ref/index.test.js +0 -21
- package/dist/main/hooks/use-render-key-provider/index.test.d.ts +0 -1
- package/dist/main/hooks/use-render-key-provider/index.test.js +0 -72
- package/dist/main/hooks/use-store/index.test.d.ts +0 -1
- package/dist/main/hooks/use-store/index.test.js +0 -456
- package/dist/main/index.test.d.ts +0 -11
- package/dist/main/index.test.js +0 -1298
- package/dist/main/test-apps/normal.d.ts +0 -36
- package/dist/main/test-apps/normal.js +0 -243
- package/dist/main/test-apps/with-connected-children.d.ts +0 -25
- package/dist/main/test-apps/with-connected-children.js +0 -229
- package/dist/main/test-apps/with-pure-children.d.ts +0 -15
- package/dist/main/test-apps/with-pure-children.js +0 -127
- package/dist/model/storage/index.test.d.ts +0 -1
- package/dist/model/storage/index.test.js +0 -139
- package/dist/test-artifacts/data/create-state-obj.d.ts +0 -58
- package/dist/test-artifacts/data/create-state-obj.js +0 -95
- package/dist/test-artifacts/suppress-render-compat.d.ts +0 -1
- package/dist/test-artifacts/suppress-render-compat.js +0 -7
package/dist/main/index.test.js
DELETED
|
@@ -1,1298 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const react_1 = __importDefault(require("react"));
|
|
7
|
-
const react_2 = require("@testing-library/react");
|
|
8
|
-
require("@testing-library/jest-dom");
|
|
9
|
-
const _1 = require(".");
|
|
10
|
-
const normal_1 = require("./test-apps/normal");
|
|
11
|
-
beforeAll(() => {
|
|
12
|
-
jest.spyOn(console, 'log').mockImplementation(() => { });
|
|
13
|
-
jest.spyOn(console, 'error').mockImplementation(() => { });
|
|
14
|
-
});
|
|
15
|
-
afterAll(() => jest.resetAllMocks());
|
|
16
|
-
afterEach(react_2.cleanup);
|
|
17
|
-
const tranformRenderCount = (renderCount, baseRenderCount = {}) => {
|
|
18
|
-
const netCount = {};
|
|
19
|
-
for (const k of new Set([
|
|
20
|
-
...Object.keys(renderCount.current),
|
|
21
|
-
...Object.keys(baseRenderCount)
|
|
22
|
-
])) {
|
|
23
|
-
netCount[k] = (renderCount.current[k]?.value || 0) - (baseRenderCount[k] || 0);
|
|
24
|
-
}
|
|
25
|
-
return netCount;
|
|
26
|
-
};
|
|
27
|
-
describe('ReactObservableContext', () => {
|
|
28
|
-
test('throws usage error on attempts to use context store outside of the Provider component tree', () => {
|
|
29
|
-
// note: TallyDisplay component utilizes the ReactObservableContext store
|
|
30
|
-
expect(() => (0, react_2.render)(react_1.default.createElement(normal_1.TallyDisplay, null))).toThrow(_1.UsageError);
|
|
31
|
-
});
|
|
32
|
-
// describe( 'store updates from within the Provider tree', () => {
|
|
33
|
-
// describe( 'updates only subscribed components', () => {
|
|
34
|
-
// describe( 'using connected store subscribers', () => {
|
|
35
|
-
// test( 'scenario 1', async () => {
|
|
36
|
-
// const { renderCount } = perf( React );
|
|
37
|
-
// render( <AppWithConnectedChildren /> );
|
|
38
|
-
// let baseRenderCount;
|
|
39
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
40
|
-
// fireEvent.change( screen.getByLabelText( 'New Price:' ), { target: { value: '123' } } );
|
|
41
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update price' } ) );
|
|
42
|
-
// await wait(() => {
|
|
43
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
44
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for price data
|
|
45
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for price data
|
|
46
|
-
// expect( netCount.PriceSticker ).toBe( 1 );
|
|
47
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no use for price data
|
|
48
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for price data
|
|
49
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
50
|
-
// });
|
|
51
|
-
// cleanupPerfTest();
|
|
52
|
-
// } );
|
|
53
|
-
// test( 'scenario 2', async () => {
|
|
54
|
-
// const { renderCount } = perf( React );
|
|
55
|
-
// render( <AppWithConnectedChildren /> );
|
|
56
|
-
// let baseRenderCount;
|
|
57
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
58
|
-
// fireEvent.change( screen.getByLabelText( 'New Color:' ), { target: { value: 'Navy' } } );
|
|
59
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update color' } ) );
|
|
60
|
-
// await wait(() => {
|
|
61
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
62
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product color data
|
|
63
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product color data
|
|
64
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product color data
|
|
65
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
66
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product color data
|
|
67
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
68
|
-
// });
|
|
69
|
-
// cleanupPerfTest();
|
|
70
|
-
// } );
|
|
71
|
-
// test( 'scenario 3', async () => {
|
|
72
|
-
// const { renderCount } = perf( React );
|
|
73
|
-
// render( <AppWithConnectedChildren /> );
|
|
74
|
-
// let baseRenderCount;
|
|
75
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
76
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
77
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
78
|
-
// await wait(() => {
|
|
79
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
80
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product type data
|
|
81
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
82
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
83
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
84
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product type data
|
|
85
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
86
|
-
// });
|
|
87
|
-
// cleanupPerfTest();
|
|
88
|
-
// } );
|
|
89
|
-
// test( 'does not render subscribed components for resubmitted changes', async () => {
|
|
90
|
-
// const { renderCount } = perf( React );
|
|
91
|
-
// render( <AppWithConnectedChildren /> );
|
|
92
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
93
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
94
|
-
// let baseRenderCount;
|
|
95
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
96
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
97
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
98
|
-
// await wait(() => {
|
|
99
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
100
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no new product type data
|
|
101
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no new product type data
|
|
102
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no new product type data
|
|
103
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no new product type data
|
|
104
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no new product type data
|
|
105
|
-
// expect( netCount.TallyDisplay ).toBe( 0 ); // unaffected: no new product type data
|
|
106
|
-
// });
|
|
107
|
-
// cleanupPerfTest();
|
|
108
|
-
// } );
|
|
109
|
-
// } );
|
|
110
|
-
// describe( 'using pure-component store subscribers', () => {
|
|
111
|
-
// test( 'scenario 1', async () => {
|
|
112
|
-
// const { renderCount } = perf( React );
|
|
113
|
-
// render( <AppWithPureChildren /> );
|
|
114
|
-
// let baseRenderCount;
|
|
115
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
116
|
-
// fireEvent.change( screen.getByLabelText( 'New Price:' ), { target: { value: '123' } } );
|
|
117
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update price' } ) );
|
|
118
|
-
// await wait(() => {
|
|
119
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
120
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for price data
|
|
121
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for price data
|
|
122
|
-
// expect( netCount.PriceSticker ).toBe( 1 );
|
|
123
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no use for price data
|
|
124
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for price data
|
|
125
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
126
|
-
// });
|
|
127
|
-
// cleanupPerfTest();
|
|
128
|
-
// } );
|
|
129
|
-
// test( 'scenario 2', async () => {
|
|
130
|
-
// const { renderCount } = perf( React );
|
|
131
|
-
// render( <AppWithPureChildren /> );
|
|
132
|
-
// let baseRenderCount;
|
|
133
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
134
|
-
// fireEvent.change( screen.getByLabelText( 'New Color:' ), { target: { value: 'Navy' } } );
|
|
135
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update color' } ) );
|
|
136
|
-
// await wait(() => {
|
|
137
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
138
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product color data
|
|
139
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product color data
|
|
140
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product color data
|
|
141
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
142
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product color data
|
|
143
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
144
|
-
// });
|
|
145
|
-
// cleanupPerfTest();
|
|
146
|
-
// } );
|
|
147
|
-
// test( 'scenario 3', async () => {
|
|
148
|
-
// const { renderCount } = perf( React );
|
|
149
|
-
// render( <AppWithPureChildren /> );
|
|
150
|
-
// let baseRenderCount;
|
|
151
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
152
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
153
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
154
|
-
// await wait(() => {
|
|
155
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
156
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product type data
|
|
157
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
158
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
159
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
160
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product type data
|
|
161
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
162
|
-
// });
|
|
163
|
-
// cleanupPerfTest();
|
|
164
|
-
// } );
|
|
165
|
-
// test( 'does not render subscribed components for resubmitted changes', async () => {
|
|
166
|
-
// const { renderCount } = perf( React );
|
|
167
|
-
// render( <AppWithPureChildren /> );
|
|
168
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
169
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
170
|
-
// let baseRenderCount;
|
|
171
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
172
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
173
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
174
|
-
// await wait(() => {
|
|
175
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
176
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 );
|
|
177
|
-
// expect( netCount.Editor ).toBe( 0 );
|
|
178
|
-
// expect( netCount.PriceSticker ).toBe( 0 );
|
|
179
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no new product type data
|
|
180
|
-
// expect( netCount.Reset ).toBe( 0 );
|
|
181
|
-
// expect( netCount.TallyDisplay ).toBe( 0 ); // unaffected: no new product type data
|
|
182
|
-
// });
|
|
183
|
-
// cleanupPerfTest();
|
|
184
|
-
// } );
|
|
185
|
-
// } );
|
|
186
|
-
// describe( 'using non pure-component store subscribers', () => {
|
|
187
|
-
// test( 'scenario 1', async () => {
|
|
188
|
-
// const { renderCount } = perf( React );
|
|
189
|
-
// render( <AppNormal /> );
|
|
190
|
-
// let baseRenderCount;
|
|
191
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
192
|
-
// fireEvent.change( screen.getByLabelText( 'New Price:' ), { target: { value: '123' } } );
|
|
193
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update price' } ) );
|
|
194
|
-
// await wait(() => {
|
|
195
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
196
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
197
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product price data
|
|
198
|
-
// expect( netCount.PriceSticker ).toBe( 1 );
|
|
199
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no use for product price data
|
|
200
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
201
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
202
|
-
// });
|
|
203
|
-
// cleanupPerfTest();
|
|
204
|
-
// } );
|
|
205
|
-
// test( 'scenario 2', async () => {
|
|
206
|
-
// const { renderCount } = perf( React );
|
|
207
|
-
// render( <AppNormal /> );
|
|
208
|
-
// let baseRenderCount;
|
|
209
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
210
|
-
// fireEvent.change( screen.getByLabelText( 'New Color:' ), { target: { value: 'Navy' } } );
|
|
211
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update color' } ) );
|
|
212
|
-
// await wait(() => {
|
|
213
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
214
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
215
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product price data
|
|
216
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product price data
|
|
217
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
218
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
219
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
220
|
-
// });
|
|
221
|
-
// cleanupPerfTest();
|
|
222
|
-
// } );
|
|
223
|
-
// test( 'scenario 3', async () => {
|
|
224
|
-
// const { renderCount } = perf( React );
|
|
225
|
-
// render( <AppNormal /> );
|
|
226
|
-
// let baseRenderCount;
|
|
227
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
228
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
229
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
230
|
-
// await wait(() => {
|
|
231
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
232
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
233
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
234
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
235
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
236
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
237
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
238
|
-
// });
|
|
239
|
-
// cleanupPerfTest();
|
|
240
|
-
// } );
|
|
241
|
-
// test( 'does not render resubmitted changes', async () => {
|
|
242
|
-
// const { renderCount } = perf( React );
|
|
243
|
-
// render( <AppNormal /> );
|
|
244
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
245
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
246
|
-
// let baseRenderCount;
|
|
247
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
248
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
249
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
250
|
-
// await wait(() => {
|
|
251
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
252
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 );
|
|
253
|
-
// expect( netCount.Editor ).toBe( 0 );
|
|
254
|
-
// expect( netCount.PriceSticker ).toBe( 0 );
|
|
255
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no new product type data
|
|
256
|
-
// expect( netCount.Reset ).toBe( 0 );
|
|
257
|
-
// expect( netCount.TallyDisplay ).toBe( 0 ); // unaffected: no new product type data
|
|
258
|
-
// });
|
|
259
|
-
// cleanupPerfTest();
|
|
260
|
-
// } );
|
|
261
|
-
// } );
|
|
262
|
-
// } );
|
|
263
|
-
// } );
|
|
264
|
-
// describe( 'store updates from outside the Provider tree', () => {
|
|
265
|
-
// describe( 'with connected component children', () => {
|
|
266
|
-
// test( 'only re-renders Provider children affected by the Provider parent prop change', async () => {
|
|
267
|
-
// const { renderCount } = perf( React );
|
|
268
|
-
// render( <AppWithConnectedChildren /> );
|
|
269
|
-
// let baseRenderCount;
|
|
270
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ); });
|
|
271
|
-
// fireEvent.keyUp( screen.getByLabelText( 'Type:' ), { target: { value: 'A' } } );
|
|
272
|
-
// await wait(() => {
|
|
273
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
274
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product type data
|
|
275
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
276
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
277
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
278
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product type data
|
|
279
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
280
|
-
// });
|
|
281
|
-
// cleanupPerfTest();
|
|
282
|
-
// } );
|
|
283
|
-
// test( 'only re-renders parts of the Provider tree directly affected by the Provider parent state update', async () => {
|
|
284
|
-
// const { renderCount } = perf( React );
|
|
285
|
-
// render( <AppWithConnectedChildren /> );
|
|
286
|
-
// let baseRenderCount;
|
|
287
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
288
|
-
// fireEvent.keyUp( screen.getByLabelText( '$', {
|
|
289
|
-
// key: '5',
|
|
290
|
-
// code: 'Key5'
|
|
291
|
-
// } as SelectorMatcherOptions ) );
|
|
292
|
-
// await wait(() => {
|
|
293
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
294
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product price data
|
|
295
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product price data
|
|
296
|
-
// expect( netCount.PriceSticker ).toBe( 1 );
|
|
297
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no use for product price data
|
|
298
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product price data
|
|
299
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
300
|
-
// });
|
|
301
|
-
// cleanupPerfTest();
|
|
302
|
-
// } );
|
|
303
|
-
// } );
|
|
304
|
-
// describe( 'with pure-component children', () => {
|
|
305
|
-
// test( 'only re-renders Provider children affected by the Provider parent prop change', async () => {
|
|
306
|
-
// const { renderCount } = perf( React );
|
|
307
|
-
// render( <AppWithPureChildren /> );
|
|
308
|
-
// let baseRenderCount;
|
|
309
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ); });
|
|
310
|
-
// fireEvent.keyUp( screen.getByLabelText( 'Type:' ), { target: { value: 'A' } } );
|
|
311
|
-
// await wait(() => {
|
|
312
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
313
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product type data
|
|
314
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
315
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
316
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
317
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product type data
|
|
318
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
319
|
-
// });
|
|
320
|
-
// cleanupPerfTest();
|
|
321
|
-
// } );
|
|
322
|
-
// test( 'only re-renders parts of the Provider tree directly affected by the Provider parent state update', async () => {
|
|
323
|
-
// const { renderCount } = perf( React );
|
|
324
|
-
// render( <AppWithPureChildren /> );
|
|
325
|
-
// let baseRenderCount;
|
|
326
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ); });
|
|
327
|
-
// fireEvent.keyUp( screen.getByLabelText( '$', { key: '5', code: 'Key5' } as SelectorMatcherOptions ) );
|
|
328
|
-
// await wait(() => {
|
|
329
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
330
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product price data
|
|
331
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product price data
|
|
332
|
-
// expect( netCount.PriceSticker ).toBe( 1 );
|
|
333
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no use for product price data
|
|
334
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product price data
|
|
335
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
336
|
-
// });
|
|
337
|
-
// cleanupPerfTest();
|
|
338
|
-
// } );
|
|
339
|
-
// } );
|
|
340
|
-
// describe( 'with non pure-component children ', () => {
|
|
341
|
-
// test( 'only re-renders Provider children affected by the Provider parent prop change', async () => {
|
|
342
|
-
// const { renderCount } = perf( React );
|
|
343
|
-
// render( <AppNormal /> );
|
|
344
|
-
// let baseRenderCount;
|
|
345
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ); });
|
|
346
|
-
// fireEvent.keyUp( screen.getByLabelText( 'Type:' ), { target: { value: 'A' } } );
|
|
347
|
-
// await wait(() => {
|
|
348
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
349
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
350
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
351
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
352
|
-
// expect( netCount.ProductDescription ).toBe( 1 );
|
|
353
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
354
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
355
|
-
// });
|
|
356
|
-
// cleanupPerfTest();
|
|
357
|
-
// } );
|
|
358
|
-
// test( 'oonly re-renders parts of the Provider tree directly affected by the Provider parent state update', async () => {
|
|
359
|
-
// const { renderCount } = perf( React );
|
|
360
|
-
// render( <AppNormal /> );
|
|
361
|
-
// let baseRenderCount;
|
|
362
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ); });
|
|
363
|
-
// fireEvent.keyUp( screen.getByLabelText( '$', { key: '5', code: 'Key5' } as SelectorMatcherOptions ) );
|
|
364
|
-
// await wait(() => {
|
|
365
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;;
|
|
366
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
367
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product price data
|
|
368
|
-
// expect( netCount.PriceSticker ).toBe( 1 );
|
|
369
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // unaffected: no use for product price data
|
|
370
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
371
|
-
// expect( netCount.TallyDisplay ).toBe( 1 );
|
|
372
|
-
// });
|
|
373
|
-
// cleanupPerfTest();
|
|
374
|
-
// } );
|
|
375
|
-
// } );
|
|
376
|
-
// } );
|
|
377
|
-
// describe( 'prehooks', () => {
|
|
378
|
-
// describe( 'resetState prehook', () => {
|
|
379
|
-
// describe( 'when `resetState` prehook does not exist on the context', () => {
|
|
380
|
-
// test( 'completes `store.resetState` method call', async () => {
|
|
381
|
-
// const { renderCount } = perf( React );
|
|
382
|
-
// const prehooks = {};
|
|
383
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
384
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
385
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
386
|
-
// let baseRenderCount;
|
|
387
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
388
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'reset context' } ) );
|
|
389
|
-
// await wait(() => {
|
|
390
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
391
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
392
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
393
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
394
|
-
// expect( netCount.ProductDescription ).toBe( 1 ); // DULY UPDATED WITH NEW STATE RESET
|
|
395
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
396
|
-
// expect( netCount.TallyDisplay ).toBe( 1 ); // DULY UPDATED WITH NEW STATE RESET
|
|
397
|
-
// });
|
|
398
|
-
// cleanupPerfTest();
|
|
399
|
-
// } );
|
|
400
|
-
// } );
|
|
401
|
-
// describe( 'when `resetState` prehook exists on the context', () => {
|
|
402
|
-
// test( 'is called by the `store.resetState` method', async () => {
|
|
403
|
-
// const prehooks = Object.freeze({ resetState: jest.fn().mockReturnValue( false ) });
|
|
404
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
405
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
406
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
407
|
-
// fireEvent.change( screen.getByLabelText( 'New Color:' ), { target: { value: 'Teal' } } );
|
|
408
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update color' } ) );
|
|
409
|
-
// prehooks.resetState.mockClear();
|
|
410
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'reset context' } ) );
|
|
411
|
-
// expect( prehooks.resetState ).toHaveBeenCalledTimes( 1 );
|
|
412
|
-
// expect( prehooks.resetState ).toHaveBeenCalledWith({
|
|
413
|
-
// [ REPLACE_TAG ]: {
|
|
414
|
-
// // data slices from original state to reset current state slices
|
|
415
|
-
// color: 'Burgundy',
|
|
416
|
-
// customer: {
|
|
417
|
-
// name: { first: null, last: null },
|
|
418
|
-
// phone: null
|
|
419
|
-
// },
|
|
420
|
-
// price: 22.5,
|
|
421
|
-
// type: 'Computer'
|
|
422
|
-
// }
|
|
423
|
-
// }, {
|
|
424
|
-
// // current: context state value after the `update type` & `update color` button clicks
|
|
425
|
-
// current: {
|
|
426
|
-
// color: 'Teal',
|
|
427
|
-
// customer: {
|
|
428
|
-
// name: { first: null, last: null },
|
|
429
|
-
// phone: null
|
|
430
|
-
// },
|
|
431
|
-
// price: 22.5,
|
|
432
|
-
// type: 'Bag'
|
|
433
|
-
// },
|
|
434
|
-
// // original: obtained from the './normal' Product >> Provider value prop
|
|
435
|
-
// original: {
|
|
436
|
-
// color: 'Burgundy',
|
|
437
|
-
// customer: {
|
|
438
|
-
// name: { first: null, last: null },
|
|
439
|
-
// phone: null
|
|
440
|
-
// },
|
|
441
|
-
// price: 22.5,
|
|
442
|
-
// type: 'Computer'
|
|
443
|
-
// }
|
|
444
|
-
// });
|
|
445
|
-
// } );
|
|
446
|
-
// test( 'completes `store.setState` method call if `setState` prehook returns TRUTHY', async () => {
|
|
447
|
-
// const { renderCount } = perf( React );
|
|
448
|
-
// const prehooks = Object.freeze({ resetState: jest.fn().mockReturnValue( true ) });
|
|
449
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
450
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
451
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
452
|
-
// let baseRenderCount;
|
|
453
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
454
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'reset context' } ) );
|
|
455
|
-
// await wait(() => {
|
|
456
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;;
|
|
457
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
458
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
459
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
460
|
-
// expect( netCount.ProductDescription ).toBe( 1 ); // DULY UPDATED WITH NEW STATE RESET
|
|
461
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
462
|
-
// expect( netCount.TallyDisplay ).toBe( 1 ); // DULY UPDATED WITH NEW STATE RESET
|
|
463
|
-
// });
|
|
464
|
-
// cleanupPerfTest();
|
|
465
|
-
// } );
|
|
466
|
-
// test( 'aborts `store.setState` method call if `setState` prehook returns FALSY', async () => {
|
|
467
|
-
// const { renderCount } = perf( React );
|
|
468
|
-
// const prehooks = Object.freeze({ resetState: jest.fn().mockReturnValue( false ) });
|
|
469
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
470
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
471
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
472
|
-
// let baseRenderCount;
|
|
473
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
474
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'reset context' } ) );
|
|
475
|
-
// await wait(() => {
|
|
476
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
477
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product type data
|
|
478
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
479
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
480
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // NORMAL UPDATE DUE CANCELED: RESET STATE ABORTED
|
|
481
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product type data
|
|
482
|
-
// expect( netCount.TallyDisplay ).toBe( 0 ); // NORMAL UPDATE DUE CANCELED: RESET STATE ABORTED
|
|
483
|
-
// });
|
|
484
|
-
// cleanupPerfTest();
|
|
485
|
-
// } );
|
|
486
|
-
// } );
|
|
487
|
-
// } );
|
|
488
|
-
// describe( 'setState prehook', () => {
|
|
489
|
-
// describe( 'when `setState` prehook does not exist on the context', () => {
|
|
490
|
-
// test( 'completes `store.setState` method call', async () => {
|
|
491
|
-
// const { renderCount } = perf( React );
|
|
492
|
-
// const prehooks = Object.freeze( expect.any( Object ) );
|
|
493
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
494
|
-
// let baseRenderCount;
|
|
495
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
496
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
497
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
498
|
-
// await wait(() => {
|
|
499
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
500
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
501
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
502
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
503
|
-
// expect( netCount.ProductDescription ).toBe( 1 ); // DULY UPDATED WITH NEW STATE CHANGE
|
|
504
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
505
|
-
// expect( netCount.TallyDisplay ).toBe( 1 ); // DULY UPDATED WITH NEW STATE CHANGE
|
|
506
|
-
// });
|
|
507
|
-
// cleanupPerfTest();
|
|
508
|
-
// } );
|
|
509
|
-
// } );
|
|
510
|
-
// describe( 'when `setState` prehook exists on the context', () => {
|
|
511
|
-
// test( 'is called by the `store.setState` method', async () => {
|
|
512
|
-
// const prehooks = Object.freeze({ setState: jest.fn().mockReturnValue( false ) });
|
|
513
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
514
|
-
// prehooks.setState.mockClear();
|
|
515
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
516
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
517
|
-
// expect( prehooks.setState ).toHaveBeenCalledTimes( 1 );
|
|
518
|
-
// expect( prehooks.setState ).toHaveBeenCalledWith({ type: 'Bag' });
|
|
519
|
-
// } );
|
|
520
|
-
// test( 'completes `store.setState` method call if `setState` prehook returns TRUTHY', async () => {
|
|
521
|
-
// const { renderCount } = perf( React );
|
|
522
|
-
// const prehooks = Object.freeze({ setState: jest.fn().mockReturnValue( true ) });
|
|
523
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
524
|
-
// let baseRenderCount;
|
|
525
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
526
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
527
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
528
|
-
// await wait(() => {
|
|
529
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
530
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
531
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
532
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
533
|
-
// expect( netCount.ProductDescription ).toBe( 1 ); // DULY UPDATED WITH NEW STATE CHANGE
|
|
534
|
-
// expect( netCount.Reset ).toBe( 1 ); // UPDATED BY REACT PROPAGATION (b/c no memoization)
|
|
535
|
-
// expect( netCount.TallyDisplay ).toBe( 1 ); // DULY UPDATED WITH NEW STATE CHANGE
|
|
536
|
-
// });
|
|
537
|
-
// cleanupPerfTest();
|
|
538
|
-
// } );
|
|
539
|
-
// test( 'aborts `store.setState` method call if `setState` prehook returns FALSY', async () => {
|
|
540
|
-
// const { renderCount } = perf( React );
|
|
541
|
-
// const prehooks = Object.freeze({ setState: jest.fn().mockReturnValue( false ) });
|
|
542
|
-
// render( <Product prehooks={ prehooks } type="Computer" /> );
|
|
543
|
-
// let baseRenderCount;
|
|
544
|
-
// await wait(() => { baseRenderCount = tranformRenderCount( renderCount ) });
|
|
545
|
-
// fireEvent.change( screen.getByLabelText( 'New Type:' ), { target: { value: 'Bag' } } );
|
|
546
|
-
// fireEvent.click( screen.getByRole( 'button', { name: 'update type' } ) );
|
|
547
|
-
// await wait(() => {
|
|
548
|
-
// const netCount = tranformRenderCount( renderCount, baseRenderCount ) as any;
|
|
549
|
-
// expect( netCount.CustomerPhoneDisplay ).toBe( 0 ); // unaffected: no use for product type data
|
|
550
|
-
// expect( netCount.Editor ).toBe( 0 ); // unaffected: no use for product type data
|
|
551
|
-
// expect( netCount.PriceSticker ).toBe( 0 ); // unaffected: no use for product type data
|
|
552
|
-
// expect( netCount.ProductDescription ).toBe( 0 ); // NORMAL UPDATE DUE CANCELED: SET STATE ABORTED
|
|
553
|
-
// expect( netCount.Reset ).toBe( 0 ); // unaffected: no use for product type data
|
|
554
|
-
// expect( netCount.TallyDisplay ).toBe( 0 ); // NORMAL UPDATE DUE CANCELED: SET STATE ABORTED
|
|
555
|
-
// });
|
|
556
|
-
// cleanupPerfTest();
|
|
557
|
-
// } );
|
|
558
|
-
// } );
|
|
559
|
-
// } );
|
|
560
|
-
// } );
|
|
561
|
-
// describe( 'API', () => {
|
|
562
|
-
// describe( 'connect(...)', () => {
|
|
563
|
-
// let connector, selectorMap;
|
|
564
|
-
// let ConnectedComponent1, ConnectedComponent2, ConnectedRefForwardingComponent, ConnectedMemoizedComponent;
|
|
565
|
-
// let compOneProps, compTwoProps, refForwardingCompProps, memoCompProps;
|
|
566
|
-
// beforeAll(() => {
|
|
567
|
-
// selectorMap = { box: 'items.1.name', all: FULL_STATE_SELECTOR };
|
|
568
|
-
// connector = connect( ObservableContext, selectorMap );
|
|
569
|
-
// ConnectedComponent1 = connector( props => { compOneProps = props; return null } );
|
|
570
|
-
// ConnectedComponent2 = connector( props => { compTwoProps = props; return null } );
|
|
571
|
-
// const RefForwardingComponent = React.forwardRef(( props, ref ) => { refForwardingCompProps = props; return null });
|
|
572
|
-
// RefForwardingComponent.displayName = 'Connect.RefForwardingComponent';
|
|
573
|
-
// ConnectedRefForwardingComponent = connector( RefForwardingComponent );
|
|
574
|
-
// const MemoizedComponent = React.memo( props => { memoCompProps = props; return null });
|
|
575
|
-
// MemoizedComponent.displayName = 'Connect.MemoizedComponent';
|
|
576
|
-
// ConnectedMemoizedComponent = connector( MemoizedComponent );
|
|
577
|
-
// });
|
|
578
|
-
// test( 'returns a function', () => expect( connector ).toBeInstanceOf( Function ) );
|
|
579
|
-
// describe( 'returned function\'s return value', () => {
|
|
580
|
-
// let state : {items: Array<{name: string}>};
|
|
581
|
-
// beforeAll(() => {
|
|
582
|
-
// state = {
|
|
583
|
-
// items: [
|
|
584
|
-
// { name: 'box_0' },
|
|
585
|
-
// { name: 'box_1' },
|
|
586
|
-
// { name: 'box_2' },
|
|
587
|
-
// { name: 'box_3' }
|
|
588
|
-
// ]
|
|
589
|
-
// };
|
|
590
|
-
// const Ui = () => (
|
|
591
|
-
// <article>
|
|
592
|
-
// <header>Just a Nested Content Tester</header>
|
|
593
|
-
// <main>
|
|
594
|
-
// <ConnectedComponent1 />
|
|
595
|
-
// <ConnectedComponent2 />
|
|
596
|
-
// <ConnectedRefForwardingComponent />
|
|
597
|
-
// <ConnectedMemoizedComponent />
|
|
598
|
-
// </main>
|
|
599
|
-
// <footer>The End</footer>
|
|
600
|
-
// </article>
|
|
601
|
-
// );
|
|
602
|
-
// render(
|
|
603
|
-
// <ObservableContext.Provider value={ state }>
|
|
604
|
-
// <Ui />
|
|
605
|
-
// </ObservableContext.Provider>
|
|
606
|
-
// );
|
|
607
|
-
// });
|
|
608
|
-
// test( 'is always a memoized component', () => {
|
|
609
|
-
// expect( 'compare' in ConnectedComponent1 ).toBe( true );
|
|
610
|
-
// expect( 'compare' in ConnectedComponent2 ).toBe( true );
|
|
611
|
-
// expect( 'compare' in ConnectedRefForwardingComponent ).toBe( true );
|
|
612
|
-
// expect( 'compare' in ConnectedMemoizedComponent ).toBe( true );
|
|
613
|
-
// } );
|
|
614
|
-
// test( 'is always interested in the same context state data', () => {
|
|
615
|
-
// expect( compOneProps.data ).toStrictEqual( compTwoProps.data );
|
|
616
|
-
// expect( compOneProps.data ).toStrictEqual( refForwardingCompProps.data );
|
|
617
|
-
// expect( compOneProps.data ).toStrictEqual( memoCompProps.data );
|
|
618
|
-
// } );
|
|
619
|
-
// test( 'contains the store\'s public API', () => {
|
|
620
|
-
// const data = {};
|
|
621
|
-
// for( const k in selectorMap ) { data[ k ] = expect.anything() }
|
|
622
|
-
// expect( compOneProps ).toEqual({
|
|
623
|
-
// data,
|
|
624
|
-
// resetState: expect.any( Function ),
|
|
625
|
-
// setState: expect.any( Function )
|
|
626
|
-
// });
|
|
627
|
-
// } );
|
|
628
|
-
// test( 'accepts own props (i.e. additional props at runtime)', () => {
|
|
629
|
-
// let capturedProps;
|
|
630
|
-
// const selectorMap = {
|
|
631
|
-
// fullBox2: 'items[1]',
|
|
632
|
-
// nameFirstBox: 'items.0.name'
|
|
633
|
-
// };
|
|
634
|
-
// const ownProps = {
|
|
635
|
-
// anotherOwnProp: expect.anything(),
|
|
636
|
-
// ownProp: expect.anything()
|
|
637
|
-
// };
|
|
638
|
-
// const WrappedComponent : React.ComponentType<ConnectProps<
|
|
639
|
-
// typeof ownProps,
|
|
640
|
-
// typeof state,
|
|
641
|
-
// typeof selectorMap
|
|
642
|
-
// >> = props => {
|
|
643
|
-
// capturedProps = props;
|
|
644
|
-
// return ( <div /> );
|
|
645
|
-
// };
|
|
646
|
-
// const ConnectedComponent = connect( ObservableContext, selectorMap )( WrappedComponent )
|
|
647
|
-
// render(
|
|
648
|
-
// <ObservableContext.Provider value={ state }>
|
|
649
|
-
// <ConnectedComponent { ...ownProps } ref={ React.useRef() } />
|
|
650
|
-
// </ObservableContext.Provider>
|
|
651
|
-
// );
|
|
652
|
-
// const data = {};
|
|
653
|
-
// for( const k in selectorMap ) { data[ k ] = expect.anything() }
|
|
654
|
-
// expect( capturedProps ).toEqual({
|
|
655
|
-
// ...ownProps,
|
|
656
|
-
// data,
|
|
657
|
-
// resetState: expect.any( Function ),
|
|
658
|
-
// setState: expect.any( Function )
|
|
659
|
-
// });
|
|
660
|
-
// } );
|
|
661
|
-
// describe( 'prop name conflict resolution: ownProps vs store API props', () => {
|
|
662
|
-
// test( 'defaults to ownProps', () => {
|
|
663
|
-
// let capturedProps;
|
|
664
|
-
// const selectorMap = {
|
|
665
|
-
// fullBox2: 'items[1]',
|
|
666
|
-
// nameFirstBox: 'items.0.name'
|
|
667
|
-
// };
|
|
668
|
-
// const T = props => {
|
|
669
|
-
// capturedProps = props;
|
|
670
|
-
// return null
|
|
671
|
-
// };
|
|
672
|
-
// const ownProps = {
|
|
673
|
-
// data: {
|
|
674
|
-
// anotherOwnProp: expect.anything(),
|
|
675
|
-
// ownProp: expect.anything()
|
|
676
|
-
// }
|
|
677
|
-
// };
|
|
678
|
-
// const ConnectedComponent = connect( ObservableContext, selectorMap )( T );
|
|
679
|
-
// render(
|
|
680
|
-
// <ObservableContext.Provider value={ state }>
|
|
681
|
-
// <ConnectedComponent { ...ownProps } />
|
|
682
|
-
// </ObservableContext.Provider>
|
|
683
|
-
// );
|
|
684
|
-
// const data = {};
|
|
685
|
-
// for( const k in selectorMap ) { data[ k ] = expect.anything() }
|
|
686
|
-
// expect( capturedProps ).toEqual({
|
|
687
|
-
// ...ownProps, // using `data` from ownProps
|
|
688
|
-
// resetState: expect.any( Function ),
|
|
689
|
-
// setState: expect.any( Function )
|
|
690
|
-
// });
|
|
691
|
-
// } );
|
|
692
|
-
// } );
|
|
693
|
-
// } );
|
|
694
|
-
// } );
|
|
695
|
-
// describe( 'createContext(...)', () => {
|
|
696
|
-
// test( 'returns observable context', () => {
|
|
697
|
-
// expect( ObservableContext ).toEqual(
|
|
698
|
-
// expect.objectContaining({
|
|
699
|
-
// displayName: expect.any( String ),
|
|
700
|
-
// Consumer: expect.any( Object ),
|
|
701
|
-
// Provider: expect.any( Object )
|
|
702
|
-
// })
|
|
703
|
-
// );
|
|
704
|
-
// expect( ObservableContext.Consumer.$$typeof.toString() )
|
|
705
|
-
// .toEqual( 'Symbol(react.context)' );
|
|
706
|
-
// expect( ObservableContext.Provider.$$typeof.toString() )
|
|
707
|
-
// .toEqual( 'Symbol(react.forward_ref)' );
|
|
708
|
-
// } );
|
|
709
|
-
// describe( 'Context provider component property', () => {
|
|
710
|
-
// test( 'also allows for no children', () => {
|
|
711
|
-
// let renderResult;
|
|
712
|
-
// expect(() => { renderResult = render( <ObservableContext.Provider value={{}} /> ) }).not.toThrow();
|
|
713
|
-
// expect( renderResult.container ).toBeEmptyDOMElement();
|
|
714
|
-
// } );
|
|
715
|
-
// describe( 'with store object reference for external exposure', () => {
|
|
716
|
-
// let state, storeRef, TestProvider;
|
|
717
|
-
// beforeAll(() => {
|
|
718
|
-
// state = {
|
|
719
|
-
// color: 'Burgundy',
|
|
720
|
-
// customer: {
|
|
721
|
-
// name: { first: 'tFirst', last: 'tLast' },
|
|
722
|
-
// phone: null
|
|
723
|
-
// },
|
|
724
|
-
// price: 22.5,
|
|
725
|
-
// type: 'TEST TYPE'
|
|
726
|
-
// }
|
|
727
|
-
// TestProvider = () => { // eslint-disable-line react/display-name
|
|
728
|
-
// storeRef = React.useRef();
|
|
729
|
-
// return (
|
|
730
|
-
// <ObservableContext.Provider ref={ storeRef } value={ state }>
|
|
731
|
-
// <TallyDisplay />
|
|
732
|
-
// </ObservableContext.Provider>
|
|
733
|
-
// );
|
|
734
|
-
// };
|
|
735
|
-
// });
|
|
736
|
-
// test( 'is provided', () => {
|
|
737
|
-
// render( <TestProvider /> );
|
|
738
|
-
// expect( storeRef.current ).toStrictEqual( expect.objectContaining({
|
|
739
|
-
// getState: expect.any( Function ),
|
|
740
|
-
// resetState: expect.any( Function ),
|
|
741
|
-
// setState: expect.any( Function ),
|
|
742
|
-
// subscribe: expect.any( Function )
|
|
743
|
-
// }) );
|
|
744
|
-
// } );
|
|
745
|
-
// test( 'gets a copy of the current state', () => {
|
|
746
|
-
// render( <TestProvider /> );
|
|
747
|
-
// const currentState = storeRef.current.getState();
|
|
748
|
-
// expect( currentState ).not.toBe( state );
|
|
749
|
-
// expect( currentState ).toStrictEqual( state );
|
|
750
|
-
// } );
|
|
751
|
-
// test( 'updates internal state', async () => {
|
|
752
|
-
// const { renderCount } = perf( React );
|
|
753
|
-
// render( <TestProvider /> );
|
|
754
|
-
// await wait(() => {});
|
|
755
|
-
// expect( ( renderCount.current.TallyDisplay as RenderCountField ).value ).toBe( 1 );
|
|
756
|
-
// const currentState = storeRef.current.getState();
|
|
757
|
-
// storeRef.current.setState({ price: 45 });
|
|
758
|
-
// const newState = { ...state, price: 45 };
|
|
759
|
-
// await wait(() => {});
|
|
760
|
-
// await new Promise( resolve => setTimeout( resolve, 50 ) );
|
|
761
|
-
// expect( ( renderCount.current.TallyDisplay as RenderCountField ).value ).toBe( 2 );
|
|
762
|
-
// expect( currentState ).not.toEqual( newState );
|
|
763
|
-
// expect( storeRef.current.getState() ).toEqual( newState );
|
|
764
|
-
// storeRef.current.resetState([ FULL_STATE_SELECTOR ]); // resets store internal state
|
|
765
|
-
// await wait(() => {});
|
|
766
|
-
// await new Promise( resolve => setTimeout( resolve, 50 ) );
|
|
767
|
-
// expect( ( renderCount.current.TallyDisplay as RenderCountField ).value ).toBe( 3 );
|
|
768
|
-
// const currentState2 = storeRef.current.getState();
|
|
769
|
-
// expect( currentState2 ).not.toBe( state );
|
|
770
|
-
// expect( currentState2 ).toStrictEqual( state );
|
|
771
|
-
// expect( currentState2 ).not.toBe( currentState );
|
|
772
|
-
// expect( currentState2 ).toStrictEqual( currentState );
|
|
773
|
-
// cleanupPerfTest();
|
|
774
|
-
// } );
|
|
775
|
-
// test( 'subscribes to state changes', async () => {
|
|
776
|
-
// render( <TestProvider /> );
|
|
777
|
-
// const changes = { price: 45 };
|
|
778
|
-
// const onChangeMock = jest.fn();
|
|
779
|
-
// const unsub = storeRef.current.subscribe( onChangeMock );
|
|
780
|
-
// expect( onChangeMock ).not.toHaveBeenCalled();
|
|
781
|
-
// storeRef.current.setState( changes );
|
|
782
|
-
// expect( onChangeMock ).toHaveBeenCalled();
|
|
783
|
-
// expect( onChangeMock ).toHaveBeenCalledWith( changes );
|
|
784
|
-
// onChangeMock.mockClear();
|
|
785
|
-
// storeRef.current.resetState([ FULL_STATE_SELECTOR ]);
|
|
786
|
-
// expect( onChangeMock ).toHaveBeenCalled();
|
|
787
|
-
// expect( onChangeMock ).toHaveBeenCalledWith({ [ REPLACE_TAG ]: state });
|
|
788
|
-
// onChangeMock.mockClear();
|
|
789
|
-
// unsub();
|
|
790
|
-
// storeRef.current.setState( changes );
|
|
791
|
-
// expect( onChangeMock ).not.toHaveBeenCalled();
|
|
792
|
-
// storeRef.current.resetState([ FULL_STATE_SELECTOR ]);
|
|
793
|
-
// expect( onChangeMock ).not.toHaveBeenCalled();
|
|
794
|
-
// } );
|
|
795
|
-
// } );
|
|
796
|
-
// } );
|
|
797
|
-
// } );
|
|
798
|
-
// describe( 'useContext(...)', () => {
|
|
799
|
-
// let Client, renderUseContextHook, Wrapper;
|
|
800
|
-
// beforeAll(() => {
|
|
801
|
-
// /**
|
|
802
|
-
// * @type {(initialProps: RenderUseContextProps) => RenderHookResult<
|
|
803
|
-
// * RenderUseContextProps, ContextConsumerMock, Renderer<RenderUseContextProps>
|
|
804
|
-
// * >}
|
|
805
|
-
// */
|
|
806
|
-
// renderUseContextHook = initialProps => renderHook(
|
|
807
|
-
// p => useContext( p.context, p.selectorMap ),
|
|
808
|
-
// { initialProps }
|
|
809
|
-
// );
|
|
810
|
-
// /* eslint-disable react/display-name */
|
|
811
|
-
// Client = ({ selectorMap, onChange }) => {
|
|
812
|
-
// const store = useContext( ObservableContext, selectorMap );
|
|
813
|
-
// React.useMemo(() => onChange( store ), [ store ]);
|
|
814
|
-
// return null;
|
|
815
|
-
// };
|
|
816
|
-
// Client.displayName = 'Client';
|
|
817
|
-
// Wrapper = ({ children }) => (
|
|
818
|
-
// <ObservableContext.Provider value={ createSourceData() }>
|
|
819
|
-
// { children }
|
|
820
|
-
// </ObservableContext.Provider>
|
|
821
|
-
// );
|
|
822
|
-
// Wrapper.displayName = 'Wrapper';
|
|
823
|
-
// /* eslint-disable react/display-name */
|
|
824
|
-
// });
|
|
825
|
-
// test( 'returns a observable context store', () => {
|
|
826
|
-
// /** @type {Store<SourceData>} */
|
|
827
|
-
// let store;
|
|
828
|
-
// const onChange = s => { store = s };
|
|
829
|
-
// render(
|
|
830
|
-
// <Wrapper>
|
|
831
|
-
// <Client onChange={ onChange } selectorMap={{ tags: 'tags', all: FULL_STATE_SELECTOR }} />
|
|
832
|
-
// </Wrapper>
|
|
833
|
-
// );
|
|
834
|
-
// expect( store ).toEqual({
|
|
835
|
-
// data: expect.any( Object ),
|
|
836
|
-
// resetState: expect.any( Function ),
|
|
837
|
-
// setState: expect.any( Function )
|
|
838
|
-
// });
|
|
839
|
-
// } );
|
|
840
|
-
// describe( 'selectorMap update', () => {
|
|
841
|
-
// describe( 'normal flow', () => {
|
|
842
|
-
// let mockConsumer, mockSetData, mockUnsubscribe;
|
|
843
|
-
// let reactUseEffectSpy, reactUseContextSpy, reactUseStateSpy;
|
|
844
|
-
// let selectorMapOnRender : Record<string, string>;
|
|
845
|
-
// let selectorMapOnRerender : Record<string, string>;
|
|
846
|
-
// beforeAll(() => {
|
|
847
|
-
// selectorMapOnRender = {
|
|
848
|
-
// year3: 'history.places[2].year',
|
|
849
|
-
// isActive: 'isActive',
|
|
850
|
-
// tag6: 'tags[5]'
|
|
851
|
-
// };
|
|
852
|
-
// selectorMapOnRerender = clonedeep( selectorMapOnRender );
|
|
853
|
-
// selectorMapOnRerender.country3 = 'history.places[2].country';
|
|
854
|
-
// mockSetData = jest.fn();
|
|
855
|
-
// mockUnsubscribe = jest.fn();
|
|
856
|
-
// reactUseEffectSpy = jest.spyOn( React, 'useEffect' );
|
|
857
|
-
// reactUseStateSpy = jest.spyOn( React, 'useState' ).mockReturnValue([
|
|
858
|
-
// Object.values( selectorMapOnRender ).reduce(
|
|
859
|
-
// ( o, k ) => { o[ k ] = expect.anything(); return o }, {}
|
|
860
|
-
// ),
|
|
861
|
-
// mockSetData
|
|
862
|
-
// ]);
|
|
863
|
-
// mockConsumer = {
|
|
864
|
-
// getState: () => Object.values( selectorMapOnRerender ).reduce(
|
|
865
|
-
// ( o, k ) => { o[ k ] = expect.anything(); return o }, {}
|
|
866
|
-
// ),
|
|
867
|
-
// resetState: () => {},
|
|
868
|
-
// subscribe: jest.fn().mockReturnValue( mockUnsubscribe ),
|
|
869
|
-
// unlinkCache: jest.fn()
|
|
870
|
-
// };
|
|
871
|
-
// reactUseContextSpy = jest.spyOn( React, 'useContext' ).mockReturnValue( mockConsumer );
|
|
872
|
-
// const { rerender } = renderUseContextHook({
|
|
873
|
-
// context: ObservableContext,
|
|
874
|
-
// selectorMap: selectorMapOnRender
|
|
875
|
-
// });
|
|
876
|
-
// mockConsumer.subscribe.mockClear();
|
|
877
|
-
// mockConsumer.unlinkCache.mockClear();
|
|
878
|
-
// reactUseEffectSpy.mockClear();
|
|
879
|
-
// rerender({
|
|
880
|
-
// context: ObservableContext,
|
|
881
|
-
// selectorMap: selectorMapOnRerender
|
|
882
|
-
// });
|
|
883
|
-
// });
|
|
884
|
-
// afterAll(() => {
|
|
885
|
-
// reactUseContextSpy.mockRestore();
|
|
886
|
-
// reactUseEffectSpy.mockRestore();
|
|
887
|
-
// reactUseStateSpy.mockRestore();
|
|
888
|
-
// } );
|
|
889
|
-
// test( 'adjusts the store on selctorMap change', () => {
|
|
890
|
-
// expect( reactUseEffectSpy.mock.calls[ 0 ][ 1 ] ).toEqual([
|
|
891
|
-
// Object.values( selectorMapOnRerender )
|
|
892
|
-
// ]);
|
|
893
|
-
// } );
|
|
894
|
-
// test( 'cleans up all previous associations to the consumer', () => {
|
|
895
|
-
// expect( mockUnsubscribe ).toHaveBeenCalled();
|
|
896
|
-
// expect( mockConsumer.unlinkCache ).toHaveBeenCalled();
|
|
897
|
-
// } );
|
|
898
|
-
// describe( 'when the new selectorMap is not empty', () => {
|
|
899
|
-
// test( 'refreshes state data', () => {
|
|
900
|
-
// expect( mockSetData ).toHaveBeenCalled();
|
|
901
|
-
// } );
|
|
902
|
-
// test( 'sets up new subscription with the consumer', () => {
|
|
903
|
-
// expect( mockConsumer.subscribe ).toHaveBeenCalled();
|
|
904
|
-
// } );
|
|
905
|
-
// } );
|
|
906
|
-
// } );
|
|
907
|
-
// describe( 'accepting an array of propertyPaths in place of a selector map', () => {
|
|
908
|
-
// /** @type {Store<SourceData>} */ let store;
|
|
909
|
-
// beforeAll(() => {
|
|
910
|
-
// const onChange = s => { store = s };
|
|
911
|
-
// render(
|
|
912
|
-
// <Wrapper>
|
|
913
|
-
// <Client onChange={ onChange } selectorMap={[
|
|
914
|
-
// 'history.places[2].year',
|
|
915
|
-
// 'isActive',
|
|
916
|
-
// 'tags[5]',
|
|
917
|
-
// FULL_STATE_SELECTOR
|
|
918
|
-
// ]} />
|
|
919
|
-
// </Wrapper>
|
|
920
|
-
// );
|
|
921
|
-
// });
|
|
922
|
-
// test( 'produces an indexed-based context state data object', () => {
|
|
923
|
-
// const stateSource = createSourceData();
|
|
924
|
-
// expect( store.data ).toStrictEqual({
|
|
925
|
-
// 0: stateSource.history.places[ 2 ].year,
|
|
926
|
-
// 1: stateSource.isActive,
|
|
927
|
-
// 2: stateSource.tags[ 5 ],
|
|
928
|
-
// 3: stateSource
|
|
929
|
-
// });
|
|
930
|
-
// } );
|
|
931
|
-
// } );
|
|
932
|
-
// describe( 'when the new selectorMap is empty', () => {
|
|
933
|
-
// describe( 'and existing data is not empty', () => {
|
|
934
|
-
// let mockConsumer, mockSetData, mockUnsubscribe;
|
|
935
|
-
// let reactUseEffectSpy, reactUseContextSpy, reactUseStateSpy;
|
|
936
|
-
// let selectorMapOnRender : Record<string, string>;
|
|
937
|
-
// let selectorMapOnRerender : Record<string, string>;
|
|
938
|
-
// beforeAll(() => {
|
|
939
|
-
// selectorMapOnRender = {
|
|
940
|
-
// year3: 'history.places[2].year',
|
|
941
|
-
// isActive: 'isActive',
|
|
942
|
-
// tag6: 'tags[5]'
|
|
943
|
-
// };
|
|
944
|
-
// selectorMapOnRerender = {};
|
|
945
|
-
// mockSetData = jest.fn();
|
|
946
|
-
// mockUnsubscribe = jest.fn();
|
|
947
|
-
// reactUseEffectSpy = jest.spyOn( React, 'useEffect' );
|
|
948
|
-
// reactUseStateSpy = jest.spyOn( React, 'useState' ).mockReturnValue([
|
|
949
|
-
// Object.values( selectorMapOnRender ).reduce(
|
|
950
|
-
// ( o, k ) => { o[ k ] = expect.anything(); return o }, {}
|
|
951
|
-
// ),
|
|
952
|
-
// mockSetData
|
|
953
|
-
// ]);
|
|
954
|
-
// mockConsumer = {
|
|
955
|
-
// getState: () => Object.values( selectorMapOnRerender ).reduce(
|
|
956
|
-
// ( o, k ) => { o[ k ] = expect.anything(); return o }, {}
|
|
957
|
-
// ),
|
|
958
|
-
// resetState: () => {},
|
|
959
|
-
// subscribe: jest.fn().mockReturnValue( mockUnsubscribe ),
|
|
960
|
-
// unlinkCache: jest.fn()
|
|
961
|
-
// };
|
|
962
|
-
// reactUseContextSpy = jest.spyOn( React, 'useContext' ).mockReturnValue( mockConsumer );
|
|
963
|
-
// const { rerender } = renderUseContextHook({
|
|
964
|
-
// context: ObservableContext,
|
|
965
|
-
// selectorMap: selectorMapOnRender
|
|
966
|
-
// });
|
|
967
|
-
// mockConsumer.subscribe.mockClear();
|
|
968
|
-
// mockConsumer.unlinkCache.mockClear();
|
|
969
|
-
// reactUseEffectSpy.mockClear();
|
|
970
|
-
// mockUnsubscribe.mockClear();
|
|
971
|
-
// rerender({
|
|
972
|
-
// context: ObservableContext,
|
|
973
|
-
// selectorMap: selectorMapOnRerender
|
|
974
|
-
// });
|
|
975
|
-
// });
|
|
976
|
-
// afterAll(() => {
|
|
977
|
-
// reactUseContextSpy.mockRestore();
|
|
978
|
-
// reactUseEffectSpy.mockRestore();
|
|
979
|
-
// reactUseStateSpy.mockRestore();
|
|
980
|
-
// } );
|
|
981
|
-
// test( 'adjusts the store on selctorMap change', () => {
|
|
982
|
-
// expect( reactUseEffectSpy.mock.calls[ 0 ][ 1 ] ).toEqual([[]]);
|
|
983
|
-
// } );
|
|
984
|
-
// test( 'cleans up all previous associations to the consumer', () => {
|
|
985
|
-
// expect( mockUnsubscribe ).toHaveBeenCalled();
|
|
986
|
-
// expect( mockConsumer.unlinkCache ).toHaveBeenCalled();
|
|
987
|
-
// } );
|
|
988
|
-
// test( 'refreshes state data with empty object', () => {
|
|
989
|
-
// expect( mockSetData ).toHaveBeenCalledWith({});
|
|
990
|
-
// } );
|
|
991
|
-
// test( 'does not set up new subscription with the consumer', () => {
|
|
992
|
-
// expect( mockConsumer.subscribe ).not.toHaveBeenCalled();
|
|
993
|
-
// } );
|
|
994
|
-
// } );
|
|
995
|
-
// describe( 'and existing data is empty', () => {
|
|
996
|
-
// let mockConsumer, mockSetData, mockUnsubscribe;
|
|
997
|
-
// let reactUseEffectSpy, reactUseContextSpy, reactUseStateSpy;
|
|
998
|
-
// let selectorMapOnRender;
|
|
999
|
-
// beforeAll(() => {
|
|
1000
|
-
// selectorMapOnRender = {
|
|
1001
|
-
// year3: 'history.places[2].year',
|
|
1002
|
-
// isActive: 'isActive',
|
|
1003
|
-
// tag6: 'tags[5]'
|
|
1004
|
-
// };
|
|
1005
|
-
// mockSetData = jest.fn();
|
|
1006
|
-
// mockUnsubscribe = jest.fn();
|
|
1007
|
-
// reactUseEffectSpy = jest.spyOn( React, 'useEffect' );
|
|
1008
|
-
// reactUseStateSpy = jest.spyOn( React, 'useState' ).mockReturnValue([ {}, mockSetData ]);
|
|
1009
|
-
// mockConsumer = {
|
|
1010
|
-
// getState: () => ({}),
|
|
1011
|
-
// resetState: () => {},
|
|
1012
|
-
// subscribe: jest.fn().mockReturnValue( mockUnsubscribe ),
|
|
1013
|
-
// unlinkCache: jest.fn()
|
|
1014
|
-
// };
|
|
1015
|
-
// reactUseContextSpy = jest.spyOn( React, 'useContext' ).mockReturnValue( mockConsumer );
|
|
1016
|
-
// const { rerender } = renderUseContextHook({
|
|
1017
|
-
// context: ObservableContext,
|
|
1018
|
-
// selectorMap: selectorMapOnRender
|
|
1019
|
-
// });
|
|
1020
|
-
// mockConsumer.subscribe.mockClear();
|
|
1021
|
-
// mockConsumer.unlinkCache.mockClear();
|
|
1022
|
-
// reactUseEffectSpy.mockClear();
|
|
1023
|
-
// mockUnsubscribe.mockClear();
|
|
1024
|
-
// rerender({ context: ObservableContext });
|
|
1025
|
-
// });
|
|
1026
|
-
// afterAll(() => {
|
|
1027
|
-
// reactUseContextSpy.mockRestore();
|
|
1028
|
-
// reactUseEffectSpy.mockRestore();
|
|
1029
|
-
// reactUseStateSpy.mockRestore();
|
|
1030
|
-
// } );
|
|
1031
|
-
// test( 'adjusts the store on selctorMap change', () => {
|
|
1032
|
-
// expect( reactUseEffectSpy.mock.calls[ 0 ][ 1 ] ).toEqual([[]]);
|
|
1033
|
-
// } );
|
|
1034
|
-
// test( 'performs no state data update', () => {
|
|
1035
|
-
// expect( mockSetData ).not.toHaveBeenCalled();
|
|
1036
|
-
// } );
|
|
1037
|
-
// test( 'does not set up new subscription with the consumer', () => {
|
|
1038
|
-
// expect( mockConsumer.subscribe ).not.toHaveBeenCalled();
|
|
1039
|
-
// } );
|
|
1040
|
-
// describe( 'and previous property path is empty', () => {
|
|
1041
|
-
// test( 'skips cleanup: no previous associations to the consumer', () => {
|
|
1042
|
-
// const mockSetData = jest.fn();
|
|
1043
|
-
// const mockUnsubscribe = jest.fn();
|
|
1044
|
-
// const reactUseEffectSpy = jest.spyOn( React, 'useEffect' );
|
|
1045
|
-
// const reactUseStateSpy = jest.spyOn( React, 'useState' ).mockReturnValue([ {}, mockSetData ]);
|
|
1046
|
-
// const mockConsumer = {
|
|
1047
|
-
// getState: () => ({}),
|
|
1048
|
-
// resetState: () => {},
|
|
1049
|
-
// subscribe: jest.fn().mockReturnValue( mockUnsubscribe ),
|
|
1050
|
-
// unlinkCache: jest.fn()
|
|
1051
|
-
// };
|
|
1052
|
-
// const reactUseContextSpy = jest.spyOn( React, 'useContext' ).mockReturnValue( mockConsumer );
|
|
1053
|
-
// const { rerender } = renderUseContextHook({ context: ObservableContext });
|
|
1054
|
-
// mockConsumer.subscribe.mockClear();
|
|
1055
|
-
// mockConsumer.unlinkCache.mockClear();
|
|
1056
|
-
// reactUseEffectSpy.mockClear();
|
|
1057
|
-
// mockUnsubscribe.mockClear();
|
|
1058
|
-
// rerender({ context: ObservableContext, selectorMap: {} });
|
|
1059
|
-
// expect( mockConsumer.unlinkCache ).not.toHaveBeenCalled();
|
|
1060
|
-
// expect( mockUnsubscribe ).not.toHaveBeenCalled();
|
|
1061
|
-
// reactUseContextSpy.mockRestore();
|
|
1062
|
-
// reactUseEffectSpy.mockRestore();
|
|
1063
|
-
// reactUseStateSpy.mockRestore();
|
|
1064
|
-
// } );
|
|
1065
|
-
// } );
|
|
1066
|
-
// } );
|
|
1067
|
-
// } );
|
|
1068
|
-
// } );
|
|
1069
|
-
// describe( 'store.data', () => {
|
|
1070
|
-
// let Client;
|
|
1071
|
-
// beforeAll(() => {
|
|
1072
|
-
// Client = ({ selectorMap, onChange }) => {
|
|
1073
|
-
// const store = useContext( ObservableContext, selectorMap );
|
|
1074
|
-
// React.useMemo(() => onChange( store ), [ store ]);
|
|
1075
|
-
// return null;
|
|
1076
|
-
// };
|
|
1077
|
-
// Client.displayName = 'Client';
|
|
1078
|
-
// });
|
|
1079
|
-
// test( 'carries the latest state data as referenced by the selectorMap', async () => {
|
|
1080
|
-
// /** @type {Store<SourceData>} */
|
|
1081
|
-
// let store;
|
|
1082
|
-
// const onChange = s => { store = s };
|
|
1083
|
-
// render(
|
|
1084
|
-
// <Wrapper>
|
|
1085
|
-
// <Client onChange={ onChange } selectorMap={{
|
|
1086
|
-
// city3: 'history.places[2].city',
|
|
1087
|
-
// country3: 'history.places[2].country',
|
|
1088
|
-
// friends: 'friends',
|
|
1089
|
-
// year3: 'history.places[2].year',
|
|
1090
|
-
// isActive: 'isActive',
|
|
1091
|
-
// tag6: 'tags[5]',
|
|
1092
|
-
// tag7: 'tags[6]',
|
|
1093
|
-
// tags: 'tags'
|
|
1094
|
-
// }} />
|
|
1095
|
-
// </Wrapper>
|
|
1096
|
-
// );
|
|
1097
|
-
// const defaultState = createSourceData();
|
|
1098
|
-
// const expectedValue = {
|
|
1099
|
-
// city3: defaultState.history.places[ 2 ].city,
|
|
1100
|
-
// country3: defaultState.history.places[ 2 ].country,
|
|
1101
|
-
// friends: defaultState.friends,
|
|
1102
|
-
// year3: defaultState.history.places[ 2 ].year,
|
|
1103
|
-
// isActive: defaultState.isActive,
|
|
1104
|
-
// tag6: defaultState.tags[ 5 ],
|
|
1105
|
-
// tag7: defaultState.tags[ 6 ],
|
|
1106
|
-
// tags: defaultState.tags
|
|
1107
|
-
// };
|
|
1108
|
-
// expect( store.data ).toEqual( expectedValue );
|
|
1109
|
-
// store.setState({
|
|
1110
|
-
// friends: { [ MOVE_TAG ]: [ -1, 1 ] },
|
|
1111
|
-
// isActive: true,
|
|
1112
|
-
// history: {
|
|
1113
|
-
// places: {
|
|
1114
|
-
// 2: {
|
|
1115
|
-
// city: 'Marakesh',
|
|
1116
|
-
// country: 'Morocco'
|
|
1117
|
-
// }
|
|
1118
|
-
// }
|
|
1119
|
-
// },
|
|
1120
|
-
// tags: { [ DELETE_TAG ]: [ 3, 5 ] }
|
|
1121
|
-
// });
|
|
1122
|
-
// await new Promise( resolve => setTimeout( resolve, 10 ) );
|
|
1123
|
-
// expect( store.data ).toEqual({
|
|
1124
|
-
// ...expectedValue,
|
|
1125
|
-
// city3: 'Marakesh',
|
|
1126
|
-
// country3: 'Morocco',
|
|
1127
|
-
// friends: [ 0, 2, 1 ].map( i => defaultState.friends[ i ] ),
|
|
1128
|
-
// isActive: true,
|
|
1129
|
-
// tag6: undefined,
|
|
1130
|
-
// tag7: undefined,
|
|
1131
|
-
// tags: [ 0, 1, 2, 4, 6 ].map( i => defaultState.tags[ i ] )
|
|
1132
|
-
// });
|
|
1133
|
-
// } );
|
|
1134
|
-
// test( 'holds the complete current state object whenever `@@STATE` entry appears in the selectorMap', async () => {
|
|
1135
|
-
// /** @type {Store<SourceData>} */
|
|
1136
|
-
// let store;
|
|
1137
|
-
// const onChange = s => { store = s };
|
|
1138
|
-
// render(
|
|
1139
|
-
// <Wrapper>
|
|
1140
|
-
// <Client onChange={ onChange } selectorMap={{
|
|
1141
|
-
// city3: 'history.places[2].city',
|
|
1142
|
-
// country3: 'history.places[2].country',
|
|
1143
|
-
// year3: 'history.places[2].year',
|
|
1144
|
-
// isActive: 'isActive',
|
|
1145
|
-
// tag6: 'tags[5]',
|
|
1146
|
-
// tag7: 'tags[6]',
|
|
1147
|
-
// state: '@@STATE'
|
|
1148
|
-
// }} />
|
|
1149
|
-
// </Wrapper>
|
|
1150
|
-
// );
|
|
1151
|
-
// const defaultState = createSourceData();
|
|
1152
|
-
// const expectedValue = {
|
|
1153
|
-
// city3: defaultState.history.places[ 2 ].city,
|
|
1154
|
-
// country3: defaultState.history.places[ 2 ].country,
|
|
1155
|
-
// year3: defaultState.history.places[ 2 ].year,
|
|
1156
|
-
// isActive: defaultState.isActive,
|
|
1157
|
-
// tag6: defaultState.tags[ 5 ],
|
|
1158
|
-
// tag7: defaultState.tags[ 6 ],
|
|
1159
|
-
// state: defaultState
|
|
1160
|
-
// };
|
|
1161
|
-
// expect( store.data ).toEqual( expectedValue );
|
|
1162
|
-
// store.setState({
|
|
1163
|
-
// isActive: true,
|
|
1164
|
-
// history: {
|
|
1165
|
-
// places: {
|
|
1166
|
-
// 2: {
|
|
1167
|
-
// city: 'Marakesh',
|
|
1168
|
-
// country: 'Morocco'
|
|
1169
|
-
// }
|
|
1170
|
-
// }
|
|
1171
|
-
// }
|
|
1172
|
-
// });
|
|
1173
|
-
// await new Promise( resolve => setTimeout( resolve, 10 ) );
|
|
1174
|
-
// const updatedDataEquiv = createSourceData();
|
|
1175
|
-
// updatedDataEquiv.history.places[ 2 ].city = 'Marakesh';
|
|
1176
|
-
// updatedDataEquiv.history.places[ 2 ].country = 'Morocco';
|
|
1177
|
-
// updatedDataEquiv.isActive = true;
|
|
1178
|
-
// expect( store.data ).toEqual({
|
|
1179
|
-
// ...expectedValue,
|
|
1180
|
-
// city3: 'Marakesh',
|
|
1181
|
-
// country3: 'Morocco',
|
|
1182
|
-
// isActive: true,
|
|
1183
|
-
// state: updatedDataEquiv
|
|
1184
|
-
// });
|
|
1185
|
-
// } );
|
|
1186
|
-
// test( 'holds an empty object when no renderKeys provided ', async () => {
|
|
1187
|
-
// /** @type {Store<SourceData>} */
|
|
1188
|
-
// let store;
|
|
1189
|
-
// const onChange = s => { store = s };
|
|
1190
|
-
// render( <Wrapper><Client onChange={ onChange } /></Wrapper> );
|
|
1191
|
-
// expect( store.data ).toEqual({});
|
|
1192
|
-
// store.setState({ // can still update state
|
|
1193
|
-
// isActive: true,
|
|
1194
|
-
// history: {
|
|
1195
|
-
// places: {
|
|
1196
|
-
// 2: {
|
|
1197
|
-
// city: 'Marakesh',
|
|
1198
|
-
// country: 'Morocco'
|
|
1199
|
-
// }
|
|
1200
|
-
// }
|
|
1201
|
-
// }
|
|
1202
|
-
// });
|
|
1203
|
-
// await new Promise( resolve => setTimeout( resolve, 10 ) );
|
|
1204
|
-
// expect( store.data ).toEqual({});
|
|
1205
|
-
// } );
|
|
1206
|
-
// } );
|
|
1207
|
-
// describe( 'store.resetState', () => {
|
|
1208
|
-
// describe( 'when selectorMap is present in the consumer', () => {
|
|
1209
|
-
// let reactUseEffectSpy, reactUseContextSpy, reactUseStateSpy;
|
|
1210
|
-
// let mockConsumer, resetState;
|
|
1211
|
-
// let selectorMap : Record<string, string>;
|
|
1212
|
-
// beforeAll(() => {
|
|
1213
|
-
// selectorMap = { a: 'aggregation', b: 'blatant', c: 'charitable' };
|
|
1214
|
-
// mockConsumer = {
|
|
1215
|
-
// getState: () => ({}),
|
|
1216
|
-
// resetState: jest.fn(),
|
|
1217
|
-
// subscribe: () => () => {},
|
|
1218
|
-
// unlinkCache: () => {}
|
|
1219
|
-
// };
|
|
1220
|
-
// reactUseEffectSpy = jest.spyOn( React, 'useEffect' );
|
|
1221
|
-
// reactUseStateSpy = jest.spyOn( React, 'useState' ).mockReturnValue([
|
|
1222
|
-
// Object.values( selectorMap ).reduce(
|
|
1223
|
-
// ( o, k ) => { o[ k ] = expect.anything(); return o }, {}
|
|
1224
|
-
// ),
|
|
1225
|
-
// () => {}
|
|
1226
|
-
// ]);
|
|
1227
|
-
// reactUseContextSpy = jest.spyOn( React, 'useContext' ).mockReturnValue( mockConsumer );
|
|
1228
|
-
// const { result } = renderUseContextHook({ context: expect.anything(), selectorMap });
|
|
1229
|
-
// resetState = result.current.resetState;
|
|
1230
|
-
// });
|
|
1231
|
-
// afterAll(() => {
|
|
1232
|
-
// reactUseContextSpy.mockRestore();
|
|
1233
|
-
// reactUseEffectSpy.mockRestore();
|
|
1234
|
-
// reactUseStateSpy.mockRestore();
|
|
1235
|
-
// } );
|
|
1236
|
-
// describe( 'and called with own state property paths to reset', () => {
|
|
1237
|
-
// test( 'calculates setstate changes using state slice matching the supplied property paths', () => {
|
|
1238
|
-
// const args = [ 'blatant', 'xylophone', 'yodellers', 'zenith' ];
|
|
1239
|
-
// resetState( args );
|
|
1240
|
-
// expect( mockConsumer.resetState ).toHaveBeenCalledWith( args );
|
|
1241
|
-
// } );
|
|
1242
|
-
// } );
|
|
1243
|
-
// describe( 'and called with NO state property paths to reset', () => {
|
|
1244
|
-
// test( 'calculates setstate changes using state slice matching property paths derived from the selectorMap', () => {
|
|
1245
|
-
// resetState();
|
|
1246
|
-
// expect( mockConsumer.resetState ).toHaveBeenCalledWith( Object.values( selectorMap ) )
|
|
1247
|
-
// } );
|
|
1248
|
-
// } );
|
|
1249
|
-
// } );
|
|
1250
|
-
// describe( 'when selectorMap is NOT present in the consumer', () => {
|
|
1251
|
-
// let reactUseEffectSpy, reactUseContextSpy, reactUseStateSpy;
|
|
1252
|
-
// let mockConsumer, resetState;
|
|
1253
|
-
// beforeAll(() => {
|
|
1254
|
-
// mockConsumer = {
|
|
1255
|
-
// getState: () => ({}),
|
|
1256
|
-
// resetState: jest.fn(),
|
|
1257
|
-
// subscribe: () => () => {},
|
|
1258
|
-
// unlinkCache: () => {}
|
|
1259
|
-
// };
|
|
1260
|
-
// reactUseEffectSpy = jest.spyOn( React, 'useEffect' );
|
|
1261
|
-
// reactUseStateSpy = jest.spyOn( React, 'useState' ).mockReturnValue([ {}, () => {} ]);
|
|
1262
|
-
// reactUseContextSpy = jest.spyOn( React, 'useContext' ).mockReturnValue( mockConsumer );
|
|
1263
|
-
// const { result } = renderUseContextHook({ context: expect.anything() });
|
|
1264
|
-
// resetState = result.current.resetState;
|
|
1265
|
-
// });
|
|
1266
|
-
// afterAll(() => {
|
|
1267
|
-
// reactUseContextSpy.mockRestore();
|
|
1268
|
-
// reactUseEffectSpy.mockRestore();
|
|
1269
|
-
// reactUseStateSpy.mockRestore();
|
|
1270
|
-
// } );
|
|
1271
|
-
// describe( 'and called with own state property paths to reset', () => {
|
|
1272
|
-
// test( 'calculates setstate changes using state slice matching the supplied property paths', () => {
|
|
1273
|
-
// const args = [ 'blatant', 'xylophone', 'yodellers', 'zenith' ];
|
|
1274
|
-
// resetState( args );
|
|
1275
|
-
// expect( mockConsumer.resetState ).toHaveBeenCalledWith( args );
|
|
1276
|
-
// } );
|
|
1277
|
-
// } );
|
|
1278
|
-
// describe( 'and called with NO state property paths to reset', () => {
|
|
1279
|
-
// test( 'calculates setstate changes using no property paths -- the consumer applies no store reset [see usestore(...)]', () => {
|
|
1280
|
-
// resetState();
|
|
1281
|
-
// expect( mockConsumer.resetState ).toHaveBeenCalledWith([]);
|
|
1282
|
-
// } );
|
|
1283
|
-
// } );
|
|
1284
|
-
// } );
|
|
1285
|
-
// } );
|
|
1286
|
-
// } );
|
|
1287
|
-
// } );
|
|
1288
|
-
});
|
|
1289
|
-
/**
|
|
1290
|
-
* @typedef {import('@testing-library/react-hooks').Renderer<TProps>} Renderer
|
|
1291
|
-
* @template TProps
|
|
1292
|
-
*/
|
|
1293
|
-
/**
|
|
1294
|
-
* @typedef {import('@testing-library/react-hooks').RenderHookResult<TProps, TValue, TRenderer>} RenderHookResult
|
|
1295
|
-
* @template TProps
|
|
1296
|
-
* @template TValue
|
|
1297
|
-
* @template {Renderer<TProps>} TRenderer
|
|
1298
|
-
*/
|