@wordpress/data 6.6.0 → 6.8.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 +4 -0
- package/build/components/use-select/index.js +35 -36
- package/build/components/use-select/index.js.map +1 -1
- package/build/plugins/persistence/index.js +2 -375
- package/build/plugins/persistence/index.js.map +1 -1
- package/build-module/components/use-select/index.js +35 -36
- package/build-module/components/use-select/index.js.map +1 -1
- package/build-module/plugins/persistence/index.js +2 -365
- package/build-module/plugins/persistence/index.js.map +1 -1
- package/package.json +8 -8
- package/src/components/use-select/index.js +36 -33
- package/src/components/use-select/test/index.js +503 -264
- package/src/plugins/persistence/index.js +2 -420
- package/src/plugins/persistence/test/index.js +1 -777
- package/src/test/registry.js +8 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External dependencies
|
|
3
3
|
*/
|
|
4
|
-
import
|
|
4
|
+
import { act, render } from '@testing-library/react';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* WordPress dependencies
|
|
@@ -11,10 +11,15 @@ import { useState, useReducer } from '@wordpress/element';
|
|
|
11
11
|
/**
|
|
12
12
|
* Internal dependencies
|
|
13
13
|
*/
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
import {
|
|
15
|
+
createRegistry,
|
|
16
|
+
createRegistrySelector,
|
|
17
|
+
RegistryProvider,
|
|
18
|
+
AsyncModeProvider,
|
|
19
|
+
} from '../../..';
|
|
20
|
+
import useSelect from '..';
|
|
21
|
+
|
|
22
|
+
jest.useRealTimers();
|
|
18
23
|
|
|
19
24
|
describe( 'useSelect', () => {
|
|
20
25
|
let registry;
|
|
@@ -22,15 +27,6 @@ describe( 'useSelect', () => {
|
|
|
22
27
|
registry = createRegistry();
|
|
23
28
|
} );
|
|
24
29
|
|
|
25
|
-
const getTestComponent = ( mapSelectSpy, dependencyKey ) => ( props ) => {
|
|
26
|
-
const dependencies = props[ dependencyKey ];
|
|
27
|
-
mapSelectSpy.mockImplementation( ( select ) => ( {
|
|
28
|
-
results: select( 'testStore' ).testSelector( props.keyName ),
|
|
29
|
-
} ) );
|
|
30
|
-
const data = useSelect( mapSelectSpy, [ dependencies ] );
|
|
31
|
-
return <div>{ data.results }</div>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
30
|
it( 'passes the relevant data to the component', () => {
|
|
35
31
|
registry.registerStore( 'testStore', {
|
|
36
32
|
reducer: () => ( { foo: 'bar' } ),
|
|
@@ -38,19 +34,22 @@ describe( 'useSelect', () => {
|
|
|
38
34
|
testSelector: ( state, key ) => state[ key ],
|
|
39
35
|
},
|
|
40
36
|
} );
|
|
37
|
+
|
|
41
38
|
const selectSpy = jest.fn();
|
|
42
|
-
const TestComponent = jest
|
|
43
|
-
.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<RegistryProvider value={ registry }>
|
|
49
|
-
<TestComponent keyName="foo" />
|
|
50
|
-
</RegistryProvider>
|
|
51
|
-
);
|
|
39
|
+
const TestComponent = jest.fn( ( props ) => {
|
|
40
|
+
selectSpy.mockImplementation( ( select ) => ( {
|
|
41
|
+
results: select( 'testStore' ).testSelector( props.keyName ),
|
|
42
|
+
} ) );
|
|
43
|
+
const data = useSelect( selectSpy, [ props.keyName ] );
|
|
44
|
+
return <div role="status">{ data.results }</div>;
|
|
52
45
|
} );
|
|
53
|
-
|
|
46
|
+
|
|
47
|
+
const rendered = render(
|
|
48
|
+
<RegistryProvider value={ registry }>
|
|
49
|
+
<TestComponent keyName="foo" />
|
|
50
|
+
</RegistryProvider>
|
|
51
|
+
);
|
|
52
|
+
|
|
54
53
|
// 2 times expected
|
|
55
54
|
// - 1 for initial mount
|
|
56
55
|
// - 1 for after mount before subscription set.
|
|
@@ -58,9 +57,7 @@ describe( 'useSelect', () => {
|
|
|
58
57
|
expect( TestComponent ).toHaveBeenCalledTimes( 1 );
|
|
59
58
|
|
|
60
59
|
// Ensure expected state was rendered.
|
|
61
|
-
expect(
|
|
62
|
-
children: 'bar',
|
|
63
|
-
} );
|
|
60
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'bar' );
|
|
64
61
|
} );
|
|
65
62
|
|
|
66
63
|
it( 'uses memoized selector if dependencies do not change', () => {
|
|
@@ -71,73 +68,127 @@ describe( 'useSelect', () => {
|
|
|
71
68
|
},
|
|
72
69
|
} );
|
|
73
70
|
|
|
74
|
-
const selectSpyFoo = jest.fn(
|
|
75
|
-
const selectSpyBar = jest.fn(
|
|
76
|
-
const TestComponent = jest.fn(
|
|
71
|
+
const selectSpyFoo = jest.fn( () => 'foo' );
|
|
72
|
+
const selectSpyBar = jest.fn( () => 'bar' );
|
|
73
|
+
const TestComponent = jest.fn( ( props ) => {
|
|
77
74
|
const mapSelect = props.change ? selectSpyFoo : selectSpyBar;
|
|
78
75
|
const data = useSelect( mapSelect, [ props.keyName ] );
|
|
79
|
-
return <div>{ data }</div>;
|
|
80
|
-
} );
|
|
81
|
-
let renderer;
|
|
82
|
-
act( () => {
|
|
83
|
-
renderer = TestRenderer.create(
|
|
84
|
-
<RegistryProvider value={ registry }>
|
|
85
|
-
<TestComponent keyName="foo" change={ true } />
|
|
86
|
-
</RegistryProvider>
|
|
87
|
-
);
|
|
76
|
+
return <div role="status">{ data }</div>;
|
|
88
77
|
} );
|
|
89
|
-
|
|
78
|
+
|
|
79
|
+
const rendered = render(
|
|
80
|
+
<RegistryProvider value={ registry }>
|
|
81
|
+
<TestComponent keyName="foo" change={ true } />
|
|
82
|
+
</RegistryProvider>
|
|
83
|
+
);
|
|
90
84
|
|
|
91
85
|
expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 );
|
|
92
86
|
expect( selectSpyBar ).toHaveBeenCalledTimes( 0 );
|
|
93
87
|
expect( TestComponent ).toHaveBeenCalledTimes( 1 );
|
|
94
88
|
|
|
95
89
|
// Ensure expected state was rendered.
|
|
96
|
-
expect(
|
|
97
|
-
children: 'foo',
|
|
98
|
-
} );
|
|
90
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'foo' );
|
|
99
91
|
|
|
100
92
|
// Rerender with non dependency changed.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
<
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
);
|
|
107
|
-
} );
|
|
93
|
+
rendered.rerender(
|
|
94
|
+
<RegistryProvider value={ registry }>
|
|
95
|
+
<TestComponent keyName="foo" change={ false } />
|
|
96
|
+
</RegistryProvider>
|
|
97
|
+
);
|
|
108
98
|
|
|
109
99
|
expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 );
|
|
110
100
|
expect( selectSpyBar ).toHaveBeenCalledTimes( 0 );
|
|
111
101
|
expect( TestComponent ).toHaveBeenCalledTimes( 2 );
|
|
112
102
|
|
|
113
103
|
// Ensure expected state was rendered.
|
|
114
|
-
expect(
|
|
115
|
-
children: 'foo',
|
|
116
|
-
} );
|
|
104
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'foo' );
|
|
117
105
|
|
|
118
106
|
// Rerender with dependency changed.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
);
|
|
125
|
-
} );
|
|
107
|
+
rendered.rerender(
|
|
108
|
+
<RegistryProvider value={ registry }>
|
|
109
|
+
<TestComponent keyName="bar" change={ false } />
|
|
110
|
+
</RegistryProvider>
|
|
111
|
+
);
|
|
126
112
|
|
|
127
113
|
expect( selectSpyFoo ).toHaveBeenCalledTimes( 2 );
|
|
128
114
|
expect( selectSpyBar ).toHaveBeenCalledTimes( 2 );
|
|
129
115
|
expect( TestComponent ).toHaveBeenCalledTimes( 3 );
|
|
130
116
|
|
|
131
117
|
// Ensure expected state was rendered.
|
|
132
|
-
expect(
|
|
133
|
-
|
|
118
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'bar' );
|
|
119
|
+
} );
|
|
120
|
+
|
|
121
|
+
it( 'avoid calling nested listener after unmounted', async () => {
|
|
122
|
+
registry.registerStore( 'toggler', {
|
|
123
|
+
reducer: ( state = false, action ) =>
|
|
124
|
+
action.type === 'TOGGLE' ? ! state : state,
|
|
125
|
+
actions: {
|
|
126
|
+
toggle: () => ( { type: 'TOGGLE' } ),
|
|
127
|
+
},
|
|
128
|
+
selectors: {
|
|
129
|
+
get: ( state ) => state,
|
|
130
|
+
},
|
|
131
|
+
} );
|
|
132
|
+
|
|
133
|
+
const mapSelect = ( select ) => select( 'toggler' ).get();
|
|
134
|
+
|
|
135
|
+
const mapSelectChild = jest.fn( mapSelect );
|
|
136
|
+
function Child() {
|
|
137
|
+
const show = useSelect( mapSelectChild, [] );
|
|
138
|
+
return show ? 'yes' : 'no';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const mapSelectParent = jest.fn( mapSelect );
|
|
142
|
+
function Parent() {
|
|
143
|
+
const show = useSelect( mapSelectParent, [] );
|
|
144
|
+
return show ? <Child /> : 'none';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const rendered = render(
|
|
148
|
+
<RegistryProvider value={ registry }>
|
|
149
|
+
<Parent />
|
|
150
|
+
</RegistryProvider>
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
// Initial render renders only parent and subscribes the parent to store.
|
|
154
|
+
expect( rendered.getByText( 'none' ) ).toBeInTheDocument();
|
|
155
|
+
expect( mapSelectParent ).toHaveBeenCalledTimes( 2 );
|
|
156
|
+
expect( mapSelectChild ).toHaveBeenCalledTimes( 0 );
|
|
157
|
+
|
|
158
|
+
// act() does batched updates internally, i.e., any scheduled setStates or effects
|
|
159
|
+
// will be executed only after the dispatch finishes. But we want to opt out of
|
|
160
|
+
// batched updates here. We want all the setStates to be done synchronously, as the
|
|
161
|
+
// store listeners are called. The async/await code is a trick to do it: do the
|
|
162
|
+
// dispatch in a different event loop tick, where the batched updates are no longer active.
|
|
163
|
+
await act( async () => {
|
|
164
|
+
await Promise.resolve();
|
|
165
|
+
registry.dispatch( 'toggler' ).toggle();
|
|
134
166
|
} );
|
|
167
|
+
|
|
168
|
+
// Child was rendered and subscribed to the store, as the _second_ subscription.
|
|
169
|
+
expect( rendered.getByText( 'yes' ) ).toBeInTheDocument();
|
|
170
|
+
expect( mapSelectParent ).toHaveBeenCalledTimes( 3 );
|
|
171
|
+
expect( mapSelectChild ).toHaveBeenCalledTimes( 2 );
|
|
172
|
+
|
|
173
|
+
await act( async () => {
|
|
174
|
+
await Promise.resolve();
|
|
175
|
+
registry.dispatch( 'toggler' ).toggle();
|
|
176
|
+
} );
|
|
177
|
+
|
|
178
|
+
// Check that child was unmounted without any extra state update being performed on it.
|
|
179
|
+
// I.e., `mapSelectChild` was never called again, and no "state update on an unmounted
|
|
180
|
+
// component" warning was triggered.
|
|
181
|
+
expect( rendered.getByText( 'none' ) ).toBeInTheDocument();
|
|
182
|
+
expect( mapSelectParent ).toHaveBeenCalledTimes( 4 );
|
|
183
|
+
expect( mapSelectChild ).toHaveBeenCalledTimes( 2 );
|
|
135
184
|
} );
|
|
185
|
+
|
|
136
186
|
describe( 'rerenders as expected with various mapSelect return types', () => {
|
|
137
187
|
const getComponent = ( mapSelectSpy ) => () => {
|
|
138
188
|
const data = useSelect( mapSelectSpy, [] );
|
|
139
|
-
return <div data={ data } />;
|
|
189
|
+
return <div role="status" data-d={ JSON.stringify( data ) } />;
|
|
140
190
|
};
|
|
191
|
+
|
|
141
192
|
let TestComponent;
|
|
142
193
|
const mapSelectSpy = jest.fn( ( select ) =>
|
|
143
194
|
select( 'testStore' ).testSelector()
|
|
@@ -156,6 +207,7 @@ describe( 'useSelect', () => {
|
|
|
156
207
|
} );
|
|
157
208
|
TestComponent = getComponent( mapSelectSpy );
|
|
158
209
|
} );
|
|
210
|
+
|
|
159
211
|
afterEach( () => {
|
|
160
212
|
selectorSpy.mockClear();
|
|
161
213
|
mapSelectSpy.mockClear();
|
|
@@ -180,18 +232,14 @@ describe( 'useSelect', () => {
|
|
|
180
232
|
( type, testValues ) => {
|
|
181
233
|
const [ valueA, valueB ] = testValues;
|
|
182
234
|
selectorSpy.mockReturnValue( valueA );
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
</RegistryProvider>
|
|
189
|
-
);
|
|
190
|
-
} );
|
|
191
|
-
const testInstance = renderer.root;
|
|
235
|
+
const rendered = render(
|
|
236
|
+
<RegistryProvider value={ registry }>
|
|
237
|
+
<TestComponent />
|
|
238
|
+
</RegistryProvider>
|
|
239
|
+
);
|
|
192
240
|
// Ensure expected state was rendered.
|
|
193
|
-
expect(
|
|
194
|
-
valueA
|
|
241
|
+
expect( rendered.getByRole( 'status' ).dataset.d ).toBe(
|
|
242
|
+
JSON.stringify( valueA )
|
|
195
243
|
);
|
|
196
244
|
|
|
197
245
|
// Update the returned value from the selector and trigger the
|
|
@@ -200,15 +248,15 @@ describe( 'useSelect', () => {
|
|
|
200
248
|
selectorSpy.mockReturnValue( valueB );
|
|
201
249
|
registry.dispatch( 'testStore' ).forceUpdate();
|
|
202
250
|
} );
|
|
203
|
-
expect(
|
|
204
|
-
valueB
|
|
251
|
+
expect( rendered.getByRole( 'status' ).dataset.d ).toBe(
|
|
252
|
+
JSON.stringify( valueB )
|
|
205
253
|
);
|
|
206
254
|
expect( mapSelectSpy ).toHaveBeenCalledTimes( 3 );
|
|
207
255
|
}
|
|
208
256
|
);
|
|
209
257
|
} );
|
|
210
258
|
|
|
211
|
-
describe( 're-calls the selector as
|
|
259
|
+
describe( 're-calls the selector as few times as possible', () => {
|
|
212
260
|
const counterStore = {
|
|
213
261
|
actions: {
|
|
214
262
|
increment: () => ( { type: 'INCREMENT' } ),
|
|
@@ -231,8 +279,6 @@ describe( 'useSelect', () => {
|
|
|
231
279
|
registry.registerStore( 'store-1', counterStore );
|
|
232
280
|
registry.registerStore( 'store-2', counterStore );
|
|
233
281
|
|
|
234
|
-
let renderer;
|
|
235
|
-
|
|
236
282
|
const selectCount1 = jest.fn();
|
|
237
283
|
const selectCount2 = jest.fn();
|
|
238
284
|
|
|
@@ -248,23 +294,19 @@ describe( 'useSelect', () => {
|
|
|
248
294
|
[]
|
|
249
295
|
);
|
|
250
296
|
|
|
251
|
-
return <div
|
|
297
|
+
return <div role="status">{ count1 }</div>;
|
|
252
298
|
} );
|
|
253
299
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
<
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
);
|
|
260
|
-
} );
|
|
261
|
-
|
|
262
|
-
const testInstance = renderer.root;
|
|
300
|
+
const rendered = render(
|
|
301
|
+
<RegistryProvider value={ registry }>
|
|
302
|
+
<TestComponent />
|
|
303
|
+
</RegistryProvider>
|
|
304
|
+
);
|
|
263
305
|
|
|
264
306
|
expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
|
|
265
307
|
expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
|
|
266
308
|
expect( TestComponent ).toHaveBeenCalledTimes( 1 );
|
|
267
|
-
expect(
|
|
309
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
268
310
|
|
|
269
311
|
act( () => {
|
|
270
312
|
registry.dispatch( 'store-2' ).increment();
|
|
@@ -273,7 +315,7 @@ describe( 'useSelect', () => {
|
|
|
273
315
|
expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
|
|
274
316
|
expect( selectCount2 ).toHaveBeenCalledTimes( 3 );
|
|
275
317
|
expect( TestComponent ).toHaveBeenCalledTimes( 2 );
|
|
276
|
-
expect(
|
|
318
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
277
319
|
|
|
278
320
|
act( () => {
|
|
279
321
|
registry.dispatch( 'store-1' ).increment();
|
|
@@ -282,10 +324,10 @@ describe( 'useSelect', () => {
|
|
|
282
324
|
expect( selectCount1 ).toHaveBeenCalledTimes( 3 );
|
|
283
325
|
expect( selectCount2 ).toHaveBeenCalledTimes( 3 );
|
|
284
326
|
expect( TestComponent ).toHaveBeenCalledTimes( 3 );
|
|
285
|
-
expect(
|
|
327
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 1 );
|
|
286
328
|
|
|
287
329
|
// Test if the unsubscribers get called correctly.
|
|
288
|
-
|
|
330
|
+
rendered.unmount();
|
|
289
331
|
} );
|
|
290
332
|
|
|
291
333
|
it( 'can subscribe to multiple stores at once', () => {
|
|
@@ -293,8 +335,6 @@ describe( 'useSelect', () => {
|
|
|
293
335
|
registry.registerStore( 'store-2', counterStore );
|
|
294
336
|
registry.registerStore( 'store-3', counterStore );
|
|
295
337
|
|
|
296
|
-
let renderer;
|
|
297
|
-
|
|
298
338
|
const selectCount1And2 = jest.fn();
|
|
299
339
|
|
|
300
340
|
const TestComponent = jest.fn( () => {
|
|
@@ -307,44 +347,35 @@ describe( 'useSelect', () => {
|
|
|
307
347
|
[]
|
|
308
348
|
);
|
|
309
349
|
|
|
310
|
-
return
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
renderer = TestRenderer.create(
|
|
315
|
-
<RegistryProvider value={ registry }>
|
|
316
|
-
<TestComponent />
|
|
317
|
-
</RegistryProvider>
|
|
350
|
+
return (
|
|
351
|
+
<div role="status">
|
|
352
|
+
{ count1 },{ count2 }
|
|
353
|
+
</div>
|
|
318
354
|
);
|
|
319
355
|
} );
|
|
320
356
|
|
|
321
|
-
const
|
|
357
|
+
const rendered = render(
|
|
358
|
+
<RegistryProvider value={ registry }>
|
|
359
|
+
<TestComponent />
|
|
360
|
+
</RegistryProvider>
|
|
361
|
+
);
|
|
322
362
|
|
|
323
363
|
expect( selectCount1And2 ).toHaveBeenCalledTimes( 2 );
|
|
324
|
-
expect(
|
|
325
|
-
count1: 0,
|
|
326
|
-
count2: 0,
|
|
327
|
-
} );
|
|
364
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( '0,0' );
|
|
328
365
|
|
|
329
366
|
act( () => {
|
|
330
367
|
registry.dispatch( 'store-2' ).increment();
|
|
331
368
|
} );
|
|
332
369
|
|
|
333
370
|
expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
|
|
334
|
-
expect(
|
|
335
|
-
count1: 0,
|
|
336
|
-
count2: 1,
|
|
337
|
-
} );
|
|
371
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( '0,1' );
|
|
338
372
|
|
|
339
373
|
act( () => {
|
|
340
374
|
registry.dispatch( 'store-3' ).increment();
|
|
341
375
|
} );
|
|
342
376
|
|
|
343
377
|
expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
|
|
344
|
-
expect(
|
|
345
|
-
count1: 0,
|
|
346
|
-
count2: 1,
|
|
347
|
-
} );
|
|
378
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( '0,1' );
|
|
348
379
|
} );
|
|
349
380
|
|
|
350
381
|
it( 're-calls the selector when deps changed', () => {
|
|
@@ -352,7 +383,7 @@ describe( 'useSelect', () => {
|
|
|
352
383
|
registry.registerStore( 'store-2', counterStore );
|
|
353
384
|
registry.registerStore( 'store-3', counterStore );
|
|
354
385
|
|
|
355
|
-
let
|
|
386
|
+
let dep, setDep;
|
|
356
387
|
const selectCount1AndDep = jest.fn();
|
|
357
388
|
|
|
358
389
|
const TestComponent = jest.fn( () => {
|
|
@@ -366,44 +397,41 @@ describe( 'useSelect', () => {
|
|
|
366
397
|
[ dep ]
|
|
367
398
|
);
|
|
368
399
|
|
|
369
|
-
return
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
renderer = TestRenderer.create(
|
|
374
|
-
<RegistryProvider value={ registry }>
|
|
375
|
-
<TestComponent />
|
|
376
|
-
</RegistryProvider>
|
|
400
|
+
return (
|
|
401
|
+
<div role="status">
|
|
402
|
+
count:{ state.count1 },dep:{ state.dep }
|
|
403
|
+
</div>
|
|
377
404
|
);
|
|
378
405
|
} );
|
|
379
406
|
|
|
380
|
-
const
|
|
407
|
+
const rendered = render(
|
|
408
|
+
<RegistryProvider value={ registry }>
|
|
409
|
+
<TestComponent />
|
|
410
|
+
</RegistryProvider>
|
|
411
|
+
);
|
|
381
412
|
|
|
382
413
|
expect( selectCount1AndDep ).toHaveBeenCalledTimes( 2 );
|
|
383
|
-
expect(
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
} );
|
|
414
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
415
|
+
'count:0,dep:0'
|
|
416
|
+
);
|
|
387
417
|
|
|
388
418
|
act( () => {
|
|
389
419
|
setDep( 1 );
|
|
390
420
|
} );
|
|
391
421
|
|
|
392
422
|
expect( selectCount1AndDep ).toHaveBeenCalledTimes( 4 );
|
|
393
|
-
expect(
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
} );
|
|
423
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
424
|
+
'count:0,dep:1'
|
|
425
|
+
);
|
|
397
426
|
|
|
398
427
|
act( () => {
|
|
399
428
|
registry.dispatch( 'store-1' ).increment();
|
|
400
429
|
} );
|
|
401
430
|
|
|
402
431
|
expect( selectCount1AndDep ).toHaveBeenCalledTimes( 5 );
|
|
403
|
-
expect(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
} );
|
|
432
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
433
|
+
'count:1,dep:1'
|
|
434
|
+
);
|
|
407
435
|
} );
|
|
408
436
|
|
|
409
437
|
it( 'handles registry selectors', () => {
|
|
@@ -423,7 +451,6 @@ describe( 'useSelect', () => {
|
|
|
423
451
|
} );
|
|
424
452
|
registry.registerStore( 'store-2', counterStore );
|
|
425
453
|
|
|
426
|
-
let renderer;
|
|
427
454
|
const selectCount1And2 = jest.fn();
|
|
428
455
|
|
|
429
456
|
const TestComponent = jest.fn( () => {
|
|
@@ -434,46 +461,43 @@ describe( 'useSelect', () => {
|
|
|
434
461
|
[]
|
|
435
462
|
);
|
|
436
463
|
|
|
437
|
-
return
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
renderer = TestRenderer.create(
|
|
442
|
-
<RegistryProvider value={ registry }>
|
|
443
|
-
<TestComponent />
|
|
444
|
-
</RegistryProvider>
|
|
464
|
+
return (
|
|
465
|
+
<div role="status">
|
|
466
|
+
count1:{ state.count1 },count2:{ state.count2 }
|
|
467
|
+
</div>
|
|
445
468
|
);
|
|
446
469
|
} );
|
|
447
470
|
|
|
448
|
-
const
|
|
471
|
+
const rendered = render(
|
|
472
|
+
<RegistryProvider value={ registry }>
|
|
473
|
+
<TestComponent />
|
|
474
|
+
</RegistryProvider>
|
|
475
|
+
);
|
|
449
476
|
|
|
450
477
|
expect( selectCount1And2 ).toHaveBeenCalledTimes( 2 );
|
|
451
|
-
expect(
|
|
452
|
-
count1:
|
|
453
|
-
|
|
454
|
-
} );
|
|
478
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
479
|
+
'count1:0,count2:0'
|
|
480
|
+
);
|
|
455
481
|
|
|
456
482
|
act( () => {
|
|
457
483
|
registry.dispatch( 'store-2' ).increment();
|
|
458
484
|
} );
|
|
459
485
|
|
|
460
486
|
expect( selectCount1And2 ).toHaveBeenCalledTimes( 3 );
|
|
461
|
-
expect(
|
|
462
|
-
count1:
|
|
463
|
-
|
|
464
|
-
} );
|
|
487
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
488
|
+
'count1:0,count2:1'
|
|
489
|
+
);
|
|
465
490
|
} );
|
|
466
491
|
|
|
467
492
|
it( 'handles conditional statements in selectors', () => {
|
|
468
493
|
registry.registerStore( 'store-1', counterStore );
|
|
469
494
|
registry.registerStore( 'store-2', counterStore );
|
|
470
495
|
|
|
471
|
-
let renderer, shouldSelectCount1, toggle;
|
|
472
496
|
const selectCount1 = jest.fn();
|
|
473
497
|
const selectCount2 = jest.fn();
|
|
474
498
|
|
|
475
499
|
const TestComponent = jest.fn( () => {
|
|
476
|
-
[ shouldSelectCount1, toggle ] = useReducer(
|
|
500
|
+
const [ shouldSelectCount1, toggle ] = useReducer(
|
|
477
501
|
( should ) => ! should,
|
|
478
502
|
false
|
|
479
503
|
);
|
|
@@ -492,32 +516,31 @@ describe( 'useSelect', () => {
|
|
|
492
516
|
[ shouldSelectCount1 ]
|
|
493
517
|
);
|
|
494
518
|
|
|
495
|
-
return
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
<RegistryProvider value={ registry }>
|
|
501
|
-
<TestComponent />
|
|
502
|
-
</RegistryProvider>
|
|
519
|
+
return (
|
|
520
|
+
<>
|
|
521
|
+
<div role="status">{ state }</div>
|
|
522
|
+
<button onClick={ toggle }>Toggle</button>
|
|
523
|
+
</>
|
|
503
524
|
);
|
|
504
525
|
} );
|
|
505
526
|
|
|
506
|
-
const
|
|
527
|
+
const rendered = render(
|
|
528
|
+
<RegistryProvider value={ registry }>
|
|
529
|
+
<TestComponent />
|
|
530
|
+
</RegistryProvider>
|
|
531
|
+
);
|
|
507
532
|
|
|
508
533
|
expect( selectCount1 ).toHaveBeenCalledTimes( 0 );
|
|
509
534
|
expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
|
|
510
|
-
expect(
|
|
535
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
511
536
|
'count2'
|
|
512
537
|
);
|
|
513
538
|
|
|
514
|
-
|
|
515
|
-
toggle();
|
|
516
|
-
} );
|
|
539
|
+
rendered.getByText( 'Toggle' ).click();
|
|
517
540
|
|
|
518
541
|
expect( selectCount1 ).toHaveBeenCalledTimes( 2 );
|
|
519
542
|
expect( selectCount2 ).toHaveBeenCalledTimes( 2 );
|
|
520
|
-
expect(
|
|
543
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
521
544
|
'count1'
|
|
522
545
|
);
|
|
523
546
|
} );
|
|
@@ -528,8 +551,6 @@ describe( 'useSelect', () => {
|
|
|
528
551
|
const subRegistry = createRegistry( {}, registry );
|
|
529
552
|
subRegistry.registerStore( 'child-store', counterStore );
|
|
530
553
|
|
|
531
|
-
let renderer;
|
|
532
|
-
|
|
533
554
|
const TestComponent = jest.fn( () => {
|
|
534
555
|
const state = useSelect(
|
|
535
556
|
( select ) => ( {
|
|
@@ -539,106 +560,95 @@ describe( 'useSelect', () => {
|
|
|
539
560
|
[]
|
|
540
561
|
);
|
|
541
562
|
|
|
542
|
-
return
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
renderer = TestRenderer.create(
|
|
547
|
-
<RegistryProvider value={ registry }>
|
|
548
|
-
<RegistryProvider value={ subRegistry }>
|
|
549
|
-
<TestComponent />
|
|
550
|
-
</RegistryProvider>
|
|
551
|
-
</RegistryProvider>
|
|
563
|
+
return (
|
|
564
|
+
<div role="status">
|
|
565
|
+
parent:{ state.parentCount },child:{ state.childCount }
|
|
566
|
+
</div>
|
|
552
567
|
);
|
|
553
568
|
} );
|
|
554
569
|
|
|
555
|
-
const
|
|
570
|
+
const rendered = render(
|
|
571
|
+
<RegistryProvider value={ registry }>
|
|
572
|
+
<RegistryProvider value={ subRegistry }>
|
|
573
|
+
<TestComponent />
|
|
574
|
+
</RegistryProvider>
|
|
575
|
+
</RegistryProvider>
|
|
576
|
+
);
|
|
556
577
|
|
|
557
|
-
expect(
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
} );
|
|
578
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
579
|
+
'parent:0,child:0'
|
|
580
|
+
);
|
|
561
581
|
|
|
562
582
|
act( () => {
|
|
563
583
|
registry.dispatch( 'parent-store' ).increment();
|
|
564
584
|
} );
|
|
565
585
|
|
|
566
|
-
expect(
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
} );
|
|
586
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
587
|
+
'parent:1,child:0'
|
|
588
|
+
);
|
|
570
589
|
} );
|
|
571
590
|
|
|
572
591
|
it( 'handles non-existing stores', () => {
|
|
573
592
|
registry.registerStore( 'store-1', counterStore );
|
|
574
593
|
|
|
575
|
-
let renderer;
|
|
576
|
-
|
|
577
594
|
const TestComponent = jest.fn( () => {
|
|
578
595
|
const state = useSelect(
|
|
579
596
|
( select ) => ( {
|
|
580
597
|
count1: select( 'store-1' ).getCounter(),
|
|
581
|
-
|
|
598
|
+
count2: select( 'store-2' )?.getCounter() ?? 'blank',
|
|
582
599
|
} ),
|
|
583
600
|
[]
|
|
584
601
|
);
|
|
585
602
|
|
|
586
|
-
return
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
renderer = TestRenderer.create(
|
|
591
|
-
<RegistryProvider value={ registry }>
|
|
592
|
-
<TestComponent />
|
|
593
|
-
</RegistryProvider>
|
|
603
|
+
return (
|
|
604
|
+
<div role="status">
|
|
605
|
+
count1:{ state.count1 },count2:{ state.count2 }
|
|
606
|
+
</div>
|
|
594
607
|
);
|
|
595
608
|
} );
|
|
596
609
|
|
|
597
|
-
const
|
|
610
|
+
const rendered = render(
|
|
611
|
+
<RegistryProvider value={ registry }>
|
|
612
|
+
<TestComponent />
|
|
613
|
+
</RegistryProvider>
|
|
614
|
+
);
|
|
598
615
|
|
|
599
|
-
expect(
|
|
600
|
-
count1:
|
|
601
|
-
|
|
602
|
-
} );
|
|
616
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
617
|
+
'count1:0,count2:blank'
|
|
618
|
+
);
|
|
603
619
|
|
|
604
620
|
act( () => {
|
|
605
621
|
registry.dispatch( 'store-1' ).increment();
|
|
606
622
|
} );
|
|
607
623
|
|
|
608
|
-
expect(
|
|
609
|
-
count1:
|
|
610
|
-
|
|
611
|
-
} );
|
|
624
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
625
|
+
'count1:1,count2:blank'
|
|
626
|
+
);
|
|
612
627
|
|
|
613
628
|
// Test if the unsubscribers get called correctly.
|
|
614
|
-
|
|
629
|
+
rendered.unmount();
|
|
615
630
|
} );
|
|
616
631
|
|
|
617
632
|
it( 'handles registration of a non-existing store during rendering', () => {
|
|
618
|
-
let renderer;
|
|
619
|
-
|
|
620
633
|
const TestComponent = jest.fn( () => {
|
|
621
634
|
const state = useSelect(
|
|
622
635
|
( select ) =>
|
|
623
|
-
select( 'not-yet-registered-store' )?.getCounter()
|
|
636
|
+
select( 'not-yet-registered-store' )?.getCounter() ??
|
|
637
|
+
'blank',
|
|
624
638
|
[]
|
|
625
639
|
);
|
|
626
640
|
|
|
627
|
-
return <div
|
|
628
|
-
} );
|
|
629
|
-
|
|
630
|
-
act( () => {
|
|
631
|
-
renderer = TestRenderer.create(
|
|
632
|
-
<RegistryProvider value={ registry }>
|
|
633
|
-
<TestComponent />
|
|
634
|
-
</RegistryProvider>
|
|
635
|
-
);
|
|
641
|
+
return <div role="status">{ state }</div>;
|
|
636
642
|
} );
|
|
637
643
|
|
|
638
|
-
const
|
|
644
|
+
const rendered = render(
|
|
645
|
+
<RegistryProvider value={ registry }>
|
|
646
|
+
<TestComponent />
|
|
647
|
+
</RegistryProvider>
|
|
648
|
+
);
|
|
639
649
|
|
|
640
|
-
expect(
|
|
641
|
-
|
|
650
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
651
|
+
'blank'
|
|
642
652
|
);
|
|
643
653
|
|
|
644
654
|
act( () => {
|
|
@@ -649,23 +659,21 @@ describe( 'useSelect', () => {
|
|
|
649
659
|
} );
|
|
650
660
|
|
|
651
661
|
// This is not ideal, but is the way it's working before and we want to prevent breaking changes.
|
|
652
|
-
expect(
|
|
653
|
-
|
|
662
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
663
|
+
'blank'
|
|
654
664
|
);
|
|
655
665
|
|
|
656
666
|
act( () => {
|
|
657
667
|
registry.dispatch( 'not-yet-registered-store' ).increment();
|
|
658
668
|
} );
|
|
659
669
|
|
|
660
|
-
expect(
|
|
670
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 1 );
|
|
661
671
|
|
|
662
672
|
// Test if the unsubscribers get called correctly.
|
|
663
|
-
|
|
673
|
+
rendered.unmount();
|
|
664
674
|
} );
|
|
665
675
|
|
|
666
676
|
it( 'handles registration of a non-existing store of sub-registry during rendering', () => {
|
|
667
|
-
let renderer;
|
|
668
|
-
|
|
669
677
|
const subRegistry = createRegistry( {}, registry );
|
|
670
678
|
|
|
671
679
|
const TestComponent = jest.fn( () => {
|
|
@@ -673,27 +681,23 @@ describe( 'useSelect', () => {
|
|
|
673
681
|
( select ) =>
|
|
674
682
|
select(
|
|
675
683
|
'not-yet-registered-child-store'
|
|
676
|
-
)?.getCounter(),
|
|
684
|
+
)?.getCounter() ?? 'blank',
|
|
677
685
|
[]
|
|
678
686
|
);
|
|
679
687
|
|
|
680
|
-
return <div
|
|
688
|
+
return <div role="status">{ state }</div>;
|
|
681
689
|
} );
|
|
682
690
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
<RegistryProvider value={
|
|
686
|
-
<
|
|
687
|
-
<TestComponent />
|
|
688
|
-
</RegistryProvider>
|
|
691
|
+
const rendered = render(
|
|
692
|
+
<RegistryProvider value={ registry }>
|
|
693
|
+
<RegistryProvider value={ subRegistry }>
|
|
694
|
+
<TestComponent />
|
|
689
695
|
</RegistryProvider>
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
const testInstance = renderer.root;
|
|
696
|
+
</RegistryProvider>
|
|
697
|
+
);
|
|
694
698
|
|
|
695
|
-
expect(
|
|
696
|
-
|
|
699
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
700
|
+
'blank'
|
|
697
701
|
);
|
|
698
702
|
|
|
699
703
|
act( () => {
|
|
@@ -704,8 +708,8 @@ describe( 'useSelect', () => {
|
|
|
704
708
|
} );
|
|
705
709
|
|
|
706
710
|
// This is not ideal, but is the way it's working before and we want to prevent breaking changes.
|
|
707
|
-
expect(
|
|
708
|
-
|
|
711
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent(
|
|
712
|
+
'blank'
|
|
709
713
|
);
|
|
710
714
|
|
|
711
715
|
act( () => {
|
|
@@ -714,15 +718,13 @@ describe( 'useSelect', () => {
|
|
|
714
718
|
.increment();
|
|
715
719
|
} );
|
|
716
720
|
|
|
717
|
-
expect(
|
|
721
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( '1' );
|
|
718
722
|
|
|
719
723
|
// Test if the unsubscribers get called correctly.
|
|
720
|
-
|
|
724
|
+
rendered.unmount();
|
|
721
725
|
} );
|
|
722
726
|
|
|
723
727
|
it( 'handles custom generic stores without a unsubscribe function', () => {
|
|
724
|
-
let renderer;
|
|
725
|
-
|
|
726
728
|
const customStore = {
|
|
727
729
|
name: 'generic-store',
|
|
728
730
|
instantiate() {
|
|
@@ -762,28 +764,265 @@ describe( 'useSelect', () => {
|
|
|
762
764
|
[]
|
|
763
765
|
);
|
|
764
766
|
|
|
765
|
-
return <div
|
|
767
|
+
return <div role="status">{ state }</div>;
|
|
766
768
|
} );
|
|
767
769
|
|
|
770
|
+
const rendered = render(
|
|
771
|
+
<RegistryProvider value={ registry }>
|
|
772
|
+
<TestComponent />
|
|
773
|
+
</RegistryProvider>
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
777
|
+
|
|
768
778
|
act( () => {
|
|
769
|
-
|
|
779
|
+
registry.dispatch( customStore ).increment();
|
|
780
|
+
} );
|
|
781
|
+
|
|
782
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 1 );
|
|
783
|
+
|
|
784
|
+
expect( () => rendered.unmount() ).not.toThrow();
|
|
785
|
+
} );
|
|
786
|
+
} );
|
|
787
|
+
|
|
788
|
+
describe( 'async mode', () => {
|
|
789
|
+
function registerCounterStore( reg, initialCount = 0 ) {
|
|
790
|
+
reg.registerStore( 'counter', {
|
|
791
|
+
reducer: ( state = initialCount, action ) => {
|
|
792
|
+
if ( action.type === 'INCREMENT' ) {
|
|
793
|
+
return state + 1;
|
|
794
|
+
}
|
|
795
|
+
return state;
|
|
796
|
+
},
|
|
797
|
+
actions: {
|
|
798
|
+
inc: () => ( { type: 'INCREMENT' } ),
|
|
799
|
+
},
|
|
800
|
+
selectors: {
|
|
801
|
+
get: ( state ) => state,
|
|
802
|
+
},
|
|
803
|
+
} );
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
beforeEach( () => {
|
|
807
|
+
registerCounterStore( registry );
|
|
808
|
+
} );
|
|
809
|
+
|
|
810
|
+
it( 'renders with async mode', async () => {
|
|
811
|
+
const selectSpy = jest.fn( ( select ) =>
|
|
812
|
+
select( 'counter' ).get()
|
|
813
|
+
);
|
|
814
|
+
|
|
815
|
+
const TestComponent = jest.fn( () => {
|
|
816
|
+
const count = useSelect( selectSpy, [] );
|
|
817
|
+
return <div role="status">{ count }</div>;
|
|
818
|
+
} );
|
|
819
|
+
|
|
820
|
+
const rendered = render(
|
|
821
|
+
<AsyncModeProvider value={ true }>
|
|
770
822
|
<RegistryProvider value={ registry }>
|
|
771
823
|
<TestComponent />
|
|
772
824
|
</RegistryProvider>
|
|
773
|
-
|
|
825
|
+
</AsyncModeProvider>
|
|
826
|
+
);
|
|
827
|
+
|
|
828
|
+
// initial render + missed update catcher in subscribing effect
|
|
829
|
+
expect( selectSpy ).toHaveBeenCalledTimes( 2 );
|
|
830
|
+
expect( TestComponent ).toHaveBeenCalledTimes( 1 );
|
|
831
|
+
|
|
832
|
+
// Ensure expected state was rendered.
|
|
833
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
834
|
+
|
|
835
|
+
act( () => {
|
|
836
|
+
registry.dispatch( 'counter' ).inc();
|
|
837
|
+
} );
|
|
838
|
+
|
|
839
|
+
// still not called right after increment
|
|
840
|
+
expect( selectSpy ).toHaveBeenCalledTimes( 2 );
|
|
841
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
842
|
+
|
|
843
|
+
expect( await rendered.findByText( 1 ) ).toBeInTheDocument();
|
|
844
|
+
|
|
845
|
+
expect( selectSpy ).toHaveBeenCalledTimes( 3 );
|
|
846
|
+
expect( TestComponent ).toHaveBeenCalledTimes( 2 );
|
|
847
|
+
} );
|
|
848
|
+
|
|
849
|
+
// Tests render queue fixes done in https://github.com/WordPress/gutenberg/pull/19286
|
|
850
|
+
it( 'catches updates while switching from async to sync', () => {
|
|
851
|
+
const selectSpy = jest.fn( ( select ) =>
|
|
852
|
+
select( 'counter' ).get()
|
|
853
|
+
);
|
|
854
|
+
|
|
855
|
+
const TestComponent = jest.fn( () => {
|
|
856
|
+
const count = useSelect( selectSpy, [] );
|
|
857
|
+
return <div role="status">{ count }</div>;
|
|
858
|
+
} );
|
|
859
|
+
|
|
860
|
+
const App = ( { async } ) => (
|
|
861
|
+
<AsyncModeProvider value={ async }>
|
|
862
|
+
<RegistryProvider value={ registry }>
|
|
863
|
+
<TestComponent />
|
|
864
|
+
</RegistryProvider>
|
|
865
|
+
</AsyncModeProvider>
|
|
866
|
+
);
|
|
867
|
+
|
|
868
|
+
const rendered = render( <App async={ true } /> );
|
|
869
|
+
|
|
870
|
+
// Ensure expected state was rendered.
|
|
871
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
872
|
+
|
|
873
|
+
// Schedules an async update of the component.
|
|
874
|
+
act( () => {
|
|
875
|
+
registry.dispatch( 'counter' ).inc();
|
|
876
|
+
} );
|
|
877
|
+
|
|
878
|
+
// Ensure the async update wasn't processed yet.
|
|
879
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
880
|
+
|
|
881
|
+
// Switch from async mode to sync.
|
|
882
|
+
rendered.rerender( <App async={ false } /> );
|
|
883
|
+
|
|
884
|
+
// Ensure the async update was flushed during the rerender.
|
|
885
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 1 );
|
|
886
|
+
|
|
887
|
+
// initial render + subscription check + rerender with isAsync=false
|
|
888
|
+
expect( selectSpy ).toHaveBeenCalledTimes( 3 );
|
|
889
|
+
// initial render + rerender with isAsync=false
|
|
890
|
+
expect( TestComponent ).toHaveBeenCalledTimes( 2 );
|
|
891
|
+
} );
|
|
892
|
+
|
|
893
|
+
it( 'cancels scheduled updates when mapSelect function changes', async () => {
|
|
894
|
+
const selectA = jest.fn(
|
|
895
|
+
( select ) => 'a:' + select( 'counter' ).get()
|
|
896
|
+
);
|
|
897
|
+
const selectB = jest.fn(
|
|
898
|
+
( select ) => 'b:' + select( 'counter' ).get()
|
|
899
|
+
);
|
|
900
|
+
|
|
901
|
+
const TestComponent = jest.fn( ( { variant } ) => {
|
|
902
|
+
const count = useSelect( variant === 'a' ? selectA : selectB, [
|
|
903
|
+
variant,
|
|
904
|
+
] );
|
|
905
|
+
return <div role="status">{ count }</div>;
|
|
774
906
|
} );
|
|
775
907
|
|
|
776
|
-
const
|
|
908
|
+
const App = ( { variant } ) => (
|
|
909
|
+
<AsyncModeProvider value={ true }>
|
|
910
|
+
<RegistryProvider value={ registry }>
|
|
911
|
+
<TestComponent variant={ variant } />
|
|
912
|
+
</RegistryProvider>
|
|
913
|
+
</AsyncModeProvider>
|
|
914
|
+
);
|
|
915
|
+
|
|
916
|
+
const rendered = render( <App variant="a" /> );
|
|
777
917
|
|
|
778
|
-
|
|
918
|
+
// Ensure expected state was rendered.
|
|
919
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'a:0' );
|
|
779
920
|
|
|
921
|
+
// Schedules an async update of the component.
|
|
780
922
|
act( () => {
|
|
781
|
-
registry.dispatch(
|
|
923
|
+
registry.dispatch( 'counter' ).inc();
|
|
782
924
|
} );
|
|
783
925
|
|
|
784
|
-
|
|
926
|
+
// Ensure the async update wasn't processed yet.
|
|
927
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'a:0' );
|
|
928
|
+
|
|
929
|
+
// Rerender with a prop change that causes dependency change.
|
|
930
|
+
rendered.rerender( <App variant="b" /> );
|
|
931
|
+
|
|
932
|
+
// Ensure the async update was flushed (cancelled) during the rerender.
|
|
933
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 'b:1' );
|
|
934
|
+
|
|
935
|
+
// Give the async update time to run in case it wasn't cancelled
|
|
936
|
+
await new Promise( setImmediate );
|
|
785
937
|
|
|
786
|
-
expect(
|
|
938
|
+
expect( selectA ).toHaveBeenCalledTimes( 2 );
|
|
939
|
+
expect( selectB ).toHaveBeenCalledTimes( 2 );
|
|
940
|
+
expect( TestComponent ).toHaveBeenCalledTimes( 2 );
|
|
941
|
+
} );
|
|
942
|
+
|
|
943
|
+
it( 'cancels scheduled updates when unmounting', async () => {
|
|
944
|
+
const selectSpy = jest.fn( ( select ) =>
|
|
945
|
+
select( 'counter' ).get()
|
|
946
|
+
);
|
|
947
|
+
|
|
948
|
+
const TestComponent = jest.fn( () => {
|
|
949
|
+
const count = useSelect( selectSpy, [] );
|
|
950
|
+
return <div role="status">{ count }</div>;
|
|
951
|
+
} );
|
|
952
|
+
|
|
953
|
+
const App = () => (
|
|
954
|
+
<AsyncModeProvider value={ true }>
|
|
955
|
+
<RegistryProvider value={ registry }>
|
|
956
|
+
<TestComponent />
|
|
957
|
+
</RegistryProvider>
|
|
958
|
+
</AsyncModeProvider>
|
|
959
|
+
);
|
|
960
|
+
|
|
961
|
+
const rendered = render( <App /> );
|
|
962
|
+
|
|
963
|
+
// Ensure expected state was rendered.
|
|
964
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
965
|
+
|
|
966
|
+
// Schedules an async update of the component.
|
|
967
|
+
act( () => {
|
|
968
|
+
registry.dispatch( 'counter' ).inc();
|
|
969
|
+
} );
|
|
970
|
+
|
|
971
|
+
// Ensure the async update wasn't processed yet.
|
|
972
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
973
|
+
|
|
974
|
+
// Unmount
|
|
975
|
+
rendered.unmount();
|
|
976
|
+
|
|
977
|
+
// Give the async update time to run in case it wasn't cancelled
|
|
978
|
+
await new Promise( setImmediate );
|
|
979
|
+
|
|
980
|
+
// only the initial render, no state updates
|
|
981
|
+
expect( selectSpy ).toHaveBeenCalledTimes( 2 );
|
|
982
|
+
expect( TestComponent ).toHaveBeenCalledTimes( 1 );
|
|
983
|
+
} );
|
|
984
|
+
|
|
985
|
+
it( 'cancels scheduled updates when registry changes', async () => {
|
|
986
|
+
const registry2 = createRegistry();
|
|
987
|
+
registerCounterStore( registry2, 100 );
|
|
988
|
+
|
|
989
|
+
const selectSpy = jest.fn( ( select ) =>
|
|
990
|
+
select( 'counter' ).get()
|
|
991
|
+
);
|
|
992
|
+
|
|
993
|
+
const TestComponent = jest.fn( () => {
|
|
994
|
+
const count = useSelect( selectSpy, [] );
|
|
995
|
+
return <div role="status">{ count }</div>;
|
|
996
|
+
} );
|
|
997
|
+
|
|
998
|
+
const App = ( { reg } ) => (
|
|
999
|
+
<AsyncModeProvider value={ true }>
|
|
1000
|
+
<RegistryProvider value={ reg }>
|
|
1001
|
+
<TestComponent />
|
|
1002
|
+
</RegistryProvider>
|
|
1003
|
+
</AsyncModeProvider>
|
|
1004
|
+
);
|
|
1005
|
+
|
|
1006
|
+
const rendered = render( <App reg={ registry } /> );
|
|
1007
|
+
|
|
1008
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
1009
|
+
|
|
1010
|
+
act( () => {
|
|
1011
|
+
registry.dispatch( 'counter' ).inc();
|
|
1012
|
+
} );
|
|
1013
|
+
|
|
1014
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 0 );
|
|
1015
|
+
|
|
1016
|
+
rendered.rerender( <App reg={ registry2 } /> );
|
|
1017
|
+
|
|
1018
|
+
expect( rendered.getByRole( 'status' ) ).toHaveTextContent( 100 );
|
|
1019
|
+
|
|
1020
|
+
// Give the async update time to run in case it wasn't cancelled
|
|
1021
|
+
await new Promise( setImmediate );
|
|
1022
|
+
|
|
1023
|
+
// initial render + registry change rerender, no state updates
|
|
1024
|
+
expect( selectSpy ).toHaveBeenCalledTimes( 4 );
|
|
1025
|
+
expect( TestComponent ).toHaveBeenCalledTimes( 2 );
|
|
787
1026
|
} );
|
|
788
1027
|
} );
|
|
789
1028
|
} );
|