@wordpress/data 6.3.0 → 6.3.1-next.a55ed9455a.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/build/components/use-select/index.js +1 -1
  3. package/build/components/use-select/index.js.map +1 -1
  4. package/build/factory.js +1 -1
  5. package/build/factory.js.map +1 -1
  6. package/build/plugins/persistence/index.js +114 -12
  7. package/build/plugins/persistence/index.js.map +1 -1
  8. package/build/redux-store/index.js +24 -8
  9. package/build/redux-store/index.js.map +1 -1
  10. package/build/redux-store/metadata/actions.js +47 -4
  11. package/build/redux-store/metadata/actions.js.map +1 -1
  12. package/build/redux-store/metadata/reducer.js +57 -4
  13. package/build/redux-store/metadata/reducer.js.map +1 -1
  14. package/build/redux-store/metadata/selectors.js +71 -7
  15. package/build/redux-store/metadata/selectors.js.map +1 -1
  16. package/build/registry.js +0 -2
  17. package/build/registry.js.map +1 -1
  18. package/build/resolvers-cache-middleware.js +3 -3
  19. package/build/resolvers-cache-middleware.js.map +1 -1
  20. package/build-module/components/use-select/index.js +1 -1
  21. package/build-module/components/use-select/index.js.map +1 -1
  22. package/build-module/factory.js +1 -1
  23. package/build-module/factory.js.map +1 -1
  24. package/build-module/plugins/persistence/index.js +111 -12
  25. package/build-module/plugins/persistence/index.js.map +1 -1
  26. package/build-module/redux-store/index.js +24 -8
  27. package/build-module/redux-store/index.js.map +1 -1
  28. package/build-module/redux-store/metadata/actions.js +43 -4
  29. package/build-module/redux-store/metadata/actions.js.map +1 -1
  30. package/build-module/redux-store/metadata/reducer.js +57 -4
  31. package/build-module/redux-store/metadata/reducer.js.map +1 -1
  32. package/build-module/redux-store/metadata/selectors.js +65 -7
  33. package/build-module/redux-store/metadata/selectors.js.map +1 -1
  34. package/build-module/registry.js +0 -2
  35. package/build-module/registry.js.map +1 -1
  36. package/build-module/resolvers-cache-middleware.js +3 -3
  37. package/build-module/resolvers-cache-middleware.js.map +1 -1
  38. package/build-types/redux-store/metadata/actions.d.ts +37 -4
  39. package/build-types/redux-store/metadata/actions.d.ts.map +1 -1
  40. package/build-types/redux-store/metadata/reducer.d.ts +10 -2
  41. package/build-types/redux-store/metadata/reducer.d.ts.map +1 -1
  42. package/build-types/redux-store/metadata/selectors.d.ts +40 -0
  43. package/build-types/redux-store/metadata/selectors.d.ts.map +1 -1
  44. package/build-types/types.d.ts.map +1 -1
  45. package/package.json +8 -8
  46. package/src/components/use-select/index.js +1 -1
  47. package/src/components/use-select/test/index.js +7 -7
  48. package/src/components/with-select/test/index.js +3 -3
  49. package/src/factory.js +1 -1
  50. package/src/plugins/persistence/index.js +142 -8
  51. package/src/plugins/persistence/test/index.js +405 -0
  52. package/src/redux-store/index.js +43 -15
  53. package/src/redux-store/metadata/actions.js +43 -4
  54. package/src/redux-store/metadata/reducer.ts +60 -9
  55. package/src/redux-store/metadata/selectors.js +60 -7
  56. package/src/redux-store/metadata/test/reducer.js +64 -24
  57. package/src/redux-store/metadata/test/selectors.js +263 -53
  58. package/src/redux-store/test/index.js +42 -0
  59. package/src/registry.js +0 -2
  60. package/src/resolvers-cache-middleware.js +4 -3
  61. package/src/test/controls.js +7 -7
  62. package/src/test/registry.js +6 -6
  63. package/src/types.ts +1 -3
  64. package/tsconfig.tsbuildinfo +1 -1
@@ -9,9 +9,11 @@ import { get } from 'lodash';
9
9
  import { selectorArgsToStateKey } from './utils';
10
10
 
11
11
  /** @typedef {Record<string, import('./reducer').State>} State */
12
+ /** @typedef {import('./reducer').StateValue} StateValue */
13
+ /** @typedef {import('./reducer').Status} Status */
12
14
 
13
15
  /**
14
- * Returns the raw `isResolving` value for a given selector name,
16
+ * Returns the raw resolution state value for a given selector name,
15
17
  * and arguments set. May be undefined if the selector has never been resolved
16
18
  * or not resolved for the given set of arguments, otherwise true or false for
17
19
  * resolution started and completed respectively.
@@ -20,17 +22,35 @@ import { selectorArgsToStateKey } from './utils';
20
22
  * @param {string} selectorName Selector name.
21
23
  * @param {unknown[]?} args Arguments passed to selector.
22
24
  *
23
- * @return {boolean | undefined} isResolving value.
25
+ * @return {StateValue|undefined} isResolving value.
24
26
  */
25
- export function getIsResolving( state, selectorName, args ) {
27
+ export function getResolutionState( state, selectorName, args ) {
26
28
  const map = get( state, [ selectorName ] );
27
29
  if ( ! map ) {
28
- return undefined;
30
+ return;
29
31
  }
30
32
 
31
33
  return map.get( selectorArgsToStateKey( args ) );
32
34
  }
33
35
 
36
+ /**
37
+ * Returns the raw `isResolving` value for a given selector name,
38
+ * and arguments set. May be undefined if the selector has never been resolved
39
+ * or not resolved for the given set of arguments, otherwise true or false for
40
+ * resolution started and completed respectively.
41
+ *
42
+ * @param {State} state Data state.
43
+ * @param {string} selectorName Selector name.
44
+ * @param {unknown[]?} args Arguments passed to selector.
45
+ *
46
+ * @return {boolean | undefined} isResolving value.
47
+ */
48
+ export function getIsResolving( state, selectorName, args ) {
49
+ const resolutionState = getResolutionState( state, selectorName, args );
50
+
51
+ return resolutionState && resolutionState.status === 'resolving';
52
+ }
53
+
34
54
  /**
35
55
  * Returns true if resolution has already been triggered for a given
36
56
  * selector name, and arguments set.
@@ -42,7 +62,7 @@ export function getIsResolving( state, selectorName, args ) {
42
62
  * @return {boolean} Whether resolution has been triggered.
43
63
  */
44
64
  export function hasStartedResolution( state, selectorName, args ) {
45
- return getIsResolving( state, selectorName, args ) !== undefined;
65
+ return getResolutionState( state, selectorName, args ) !== undefined;
46
66
  }
47
67
 
48
68
  /**
@@ -56,7 +76,38 @@ export function hasStartedResolution( state, selectorName, args ) {
56
76
  * @return {boolean} Whether resolution has completed.
57
77
  */
58
78
  export function hasFinishedResolution( state, selectorName, args ) {
59
- return getIsResolving( state, selectorName, args ) === false;
79
+ const status = getResolutionState( state, selectorName, args )?.status;
80
+ return status === 'finished' || status === 'error';
81
+ }
82
+
83
+ /**
84
+ * Returns true if resolution has failed for a given selector
85
+ * name, and arguments set.
86
+ *
87
+ * @param {State} state Data state.
88
+ * @param {string} selectorName Selector name.
89
+ * @param {unknown[]?} args Arguments passed to selector.
90
+ *
91
+ * @return {boolean} Has resolution failed
92
+ */
93
+ export function hasResolutionFailed( state, selectorName, args ) {
94
+ return getResolutionState( state, selectorName, args )?.status === 'error';
95
+ }
96
+
97
+ /**
98
+ * Returns the resolution error for a given selector name, and arguments set.
99
+ * Note it may be of an Error type, but may also be null, undefined, or anything else
100
+ * that can be `throw`-n.
101
+ *
102
+ * @param {State} state Data state.
103
+ * @param {string} selectorName Selector name.
104
+ * @param {unknown[]?} args Arguments passed to selector.
105
+ *
106
+ * @return {Error|unknown} Last resolution error
107
+ */
108
+ export function getResolutionError( state, selectorName, args ) {
109
+ const resolutionState = getResolutionState( state, selectorName, args );
110
+ return resolutionState?.status === 'error' ? resolutionState.error : null;
60
111
  }
61
112
 
62
113
  /**
@@ -70,7 +121,9 @@ export function hasFinishedResolution( state, selectorName, args ) {
70
121
  * @return {boolean} Whether resolution is in progress.
71
122
  */
72
123
  export function isResolving( state, selectorName, args ) {
73
- return getIsResolving( state, selectorName, args ) === true;
124
+ return (
125
+ getResolutionState( state, selectorName, args )?.status === 'resolving'
126
+ );
74
127
  }
75
128
 
76
129
  /**
@@ -23,8 +23,10 @@ describe( 'reducer', () => {
23
23
  args: [],
24
24
  } );
25
25
 
26
- // { test: { getFoo: EquivalentKeyMap( [] => true ) } }
27
- expect( state.getFoo.get( [] ) ).toBe( true );
26
+ // { test: { getFoo: EquivalentKeyMap( [] => status: 'resolving } ) } }
27
+ expect( state.getFoo.get( [] ) ).toEqual( {
28
+ status: 'resolving',
29
+ } );
28
30
  } );
29
31
 
30
32
  it( 'should return with finished resolution', () => {
@@ -39,8 +41,10 @@ describe( 'reducer', () => {
39
41
  args: [],
40
42
  } );
41
43
 
42
- // { test: { getFoo: EquivalentKeyMap( [] => false ) } }
43
- expect( state.getFoo.get( [] ) ).toBe( false );
44
+ // { test: { getFoo: EquivalentKeyMap( [] => { status: 'finished' } ) } }
45
+ expect( state.getFoo.get( [] ) ).toEqual( {
46
+ status: 'finished',
47
+ } );
44
48
  } );
45
49
 
46
50
  it( 'should remove invalidations', () => {
@@ -81,9 +85,13 @@ describe( 'reducer', () => {
81
85
  args: [ 'block' ],
82
86
  } );
83
87
 
84
- // { getFoo: EquivalentKeyMap( [] => false ) }
85
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
86
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( true );
88
+ // { getFoo: EquivalentKeyMap( [] => { status: 'finished' } ) }
89
+ expect( state.getFoo.get( [ 'post' ] ) ).toEqual( {
90
+ status: 'finished',
91
+ } );
92
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
93
+ status: 'resolving',
94
+ } );
87
95
  } );
88
96
 
89
97
  it(
@@ -123,8 +131,10 @@ describe( 'reducer', () => {
123
131
  } );
124
132
 
125
133
  expect( state.getBar ).toBeUndefined();
126
- // { getFoo: EquivalentKeyMap( [] => false ) }
127
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
134
+ // { getFoo: EquivalentKeyMap( [] => { status: 'finished' } ) }
135
+ expect( state.getFoo.get( [ 'post' ] ) ).toEqual( {
136
+ status: 'finished',
137
+ } );
128
138
  }
129
139
  );
130
140
 
@@ -134,14 +144,18 @@ describe( 'reducer', () => {
134
144
  selectorName: 'getFoo',
135
145
  args: [ 1, undefined ],
136
146
  } );
137
- expect( started.getFoo.get( [ 1 ] ) ).toBe( true );
147
+ expect( started.getFoo.get( [ 1 ] ) ).toEqual( {
148
+ status: 'resolving',
149
+ } );
138
150
 
139
151
  const finished = reducer( started, {
140
152
  type: 'FINISH_RESOLUTION',
141
153
  selectorName: 'getFoo',
142
154
  args: [ 1, undefined, undefined ],
143
155
  } );
144
- expect( finished.getFoo.get( [ 1 ] ) ).toBe( false );
156
+ expect( finished.getFoo.get( [ 1 ] ) ).toEqual( {
157
+ status: 'finished',
158
+ } );
145
159
  } );
146
160
  } );
147
161
 
@@ -153,8 +167,12 @@ describe( 'reducer', () => {
153
167
  args: [ [ 'post' ], [ 'block' ] ],
154
168
  } );
155
169
 
156
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( true );
157
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( true );
170
+ expect( state.getFoo.get( [ 'post' ] ) ).toEqual( {
171
+ status: 'resolving',
172
+ } );
173
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
174
+ status: 'resolving',
175
+ } );
158
176
  } );
159
177
 
160
178
  it( 'should return with finished resolutions', () => {
@@ -169,8 +187,12 @@ describe( 'reducer', () => {
169
187
  args: [ [ 'post' ], [ 'block' ] ],
170
188
  } );
171
189
 
172
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
173
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( false );
190
+ expect( state.getFoo.get( [ 'post' ] ) ).toEqual( {
191
+ status: 'finished',
192
+ } );
193
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
194
+ status: 'finished',
195
+ } );
174
196
  } );
175
197
 
176
198
  it( 'should remove invalidations', () => {
@@ -191,7 +213,9 @@ describe( 'reducer', () => {
191
213
  } );
192
214
 
193
215
  expect( state.getFoo.get( [ 'post' ] ) ).toBe( undefined );
194
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( false );
216
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
217
+ status: 'finished',
218
+ } );
195
219
  } );
196
220
 
197
221
  it( 'different arguments should not conflict', () => {
@@ -211,8 +235,12 @@ describe( 'reducer', () => {
211
235
  args: [ [ 'block' ] ],
212
236
  } );
213
237
 
214
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
215
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( true );
238
+ expect( state.getFoo.get( [ 'post' ] ) ).toEqual( {
239
+ status: 'finished',
240
+ } );
241
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
242
+ status: 'resolving',
243
+ } );
216
244
  } );
217
245
 
218
246
  it(
@@ -252,8 +280,12 @@ describe( 'reducer', () => {
252
280
  } );
253
281
 
254
282
  expect( state.getBar ).toBeUndefined();
255
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
256
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( false );
283
+ expect( state.getFoo.get( [ 'post' ] ) ).toEqual( {
284
+ status: 'finished',
285
+ } );
286
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
287
+ status: 'finished',
288
+ } );
257
289
  }
258
290
  );
259
291
 
@@ -266,8 +298,12 @@ describe( 'reducer', () => {
266
298
  [ 2, undefined, undefined ],
267
299
  ],
268
300
  } );
269
- expect( started.getFoo.get( [ 1 ] ) ).toBe( true );
270
- expect( started.getFoo.get( [ 2 ] ) ).toBe( true );
301
+ expect( started.getFoo.get( [ 1 ] ) ).toEqual( {
302
+ status: 'resolving',
303
+ } );
304
+ expect( started.getFoo.get( [ 2 ] ) ).toEqual( {
305
+ status: 'resolving',
306
+ } );
271
307
 
272
308
  const finished = reducer( started, {
273
309
  type: 'FINISH_RESOLUTIONS',
@@ -277,8 +313,12 @@ describe( 'reducer', () => {
277
313
  [ 2, undefined ],
278
314
  ],
279
315
  } );
280
- expect( finished.getFoo.get( [ 1 ] ) ).toBe( false );
281
- expect( finished.getFoo.get( [ 2 ] ) ).toBe( false );
316
+ expect( finished.getFoo.get( [ 1 ] ) ).toEqual( {
317
+ status: 'finished',
318
+ } );
319
+ expect( finished.getFoo.get( [ 2 ] ) ).toEqual( {
320
+ status: 'finished',
321
+ } );
282
322
  } );
283
323
  } );
284
324
  } );
@@ -1,117 +1,327 @@
1
1
  /**
2
- * External dependencies
2
+ * WordPress dependencies
3
3
  */
4
- import EquivalentKeyMap from 'equivalent-key-map';
4
+ import { createRegistry } from '@wordpress/data';
5
5
 
6
- /**
7
- * Internal dependencies
8
- */
9
- import {
10
- getIsResolving,
11
- hasStartedResolution,
12
- hasFinishedResolution,
13
- isResolving,
14
- } from '../selectors';
6
+ jest.useRealTimers();
7
+ const testStore = {
8
+ reducer: ( state = null, action ) => {
9
+ if ( action.type === 'RECEIVE' ) {
10
+ return action.items;
11
+ }
12
+
13
+ return state;
14
+ },
15
+ selectors: {
16
+ getFoo: ( state ) => state,
17
+ },
18
+ };
19
+
20
+ async function resolve( registry, selector ) {
21
+ try {
22
+ await registry.resolveSelect( 'store' )[ selector ]();
23
+ } catch ( e ) {}
24
+ }
15
25
 
16
26
  describe( 'getIsResolving', () => {
27
+ let registry;
28
+ beforeEach( () => {
29
+ registry = createRegistry();
30
+ registry.registerStore( 'testStore', testStore );
31
+ } );
32
+
17
33
  it( 'should return undefined if no state by reducerKey, selectorName', () => {
18
- const state = {};
19
- const result = getIsResolving( state, 'getFoo', [] );
34
+ const result = registry
35
+ .select( 'testStore' )
36
+ .getIsResolving( 'getFoo', [] );
20
37
 
21
38
  expect( result ).toBe( undefined );
22
39
  } );
23
40
 
24
41
  it( 'should return undefined if state by reducerKey, selectorName, but not args', () => {
25
- const state = {
26
- getFoo: new EquivalentKeyMap( [ [ [], true ] ] ),
27
- };
28
- const result = getIsResolving( state, 'getFoo', [ 'bar' ] );
42
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
43
+ const result = registry
44
+ .select( 'testStore' )
45
+ .getIsResolving( 'getFoo', [ 'bar' ] );
29
46
 
30
47
  expect( result ).toBe( undefined );
31
48
  } );
32
49
 
33
50
  it( 'should return value by reducerKey, selectorName', () => {
34
- const state = {
35
- getFoo: new EquivalentKeyMap( [ [ [], true ] ] ),
36
- };
37
- const result = getIsResolving( state, 'getFoo', [] );
51
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
52
+ const result = registry
53
+ .select( 'testStore' )
54
+ .getIsResolving( 'getFoo', [] );
38
55
 
39
56
  expect( result ).toBe( true );
40
57
  } );
41
58
 
42
59
  it( 'should normalize args ard return the right value', () => {
43
- const state = {
44
- getFoo: new EquivalentKeyMap( [ [ [], true ] ] ),
45
- };
46
- expect( getIsResolving( state, 'getFoo' ) ).toBe( true );
47
- expect( getIsResolving( state, 'getFoo', [ undefined ] ) ).toBe( true );
48
- expect(
49
- getIsResolving( state, 'getFoo', [ undefined, undefined ] )
50
- ).toBe( true );
60
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
61
+ const { getIsResolving } = registry.select( 'testStore' );
62
+
63
+ expect( getIsResolving( 'getFoo' ) ).toBe( true );
64
+ expect( getIsResolving( 'getFoo', [ undefined ] ) ).toBe( true );
65
+ expect( getIsResolving( 'getFoo', [ undefined, undefined ] ) ).toBe(
66
+ true
67
+ );
51
68
  } );
52
69
  } );
53
70
 
54
71
  describe( 'hasStartedResolution', () => {
72
+ let registry;
73
+ beforeEach( () => {
74
+ registry = createRegistry();
75
+ registry.registerStore( 'testStore', testStore );
76
+ } );
77
+
55
78
  it( 'returns false if not has started', () => {
56
- const state = {};
57
- const result = hasStartedResolution( state, 'getFoo', [] );
79
+ const result = registry
80
+ .select( 'testStore' )
81
+ .hasStartedResolution( 'getFoo', [] );
58
82
 
59
83
  expect( result ).toBe( false );
60
84
  } );
61
85
 
62
86
  it( 'returns true if has started', () => {
63
- const state = {
64
- getFoo: new EquivalentKeyMap( [ [ [], true ] ] ),
65
- };
66
- const result = hasStartedResolution( state, 'getFoo', [] );
87
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
88
+ const { hasStartedResolution } = registry.select( 'testStore' );
89
+ const result = hasStartedResolution( 'getFoo', [] );
67
90
 
68
91
  expect( result ).toBe( true );
69
92
  } );
70
93
  } );
71
94
 
72
95
  describe( 'hasFinishedResolution', () => {
96
+ let registry;
97
+ beforeEach( () => {
98
+ registry = createRegistry();
99
+ registry.registerStore( 'testStore', testStore );
100
+ } );
101
+
73
102
  it( 'returns false if not has finished', () => {
74
- const state = {
75
- getFoo: new EquivalentKeyMap( [ [ [], true ] ] ),
76
- };
77
- const result = hasFinishedResolution( state, 'getFoo', [] );
103
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
104
+ const { hasFinishedResolution } = registry.select( 'testStore' );
105
+ const result = hasFinishedResolution( 'getFoo', [] );
78
106
 
79
107
  expect( result ).toBe( false );
80
108
  } );
81
109
 
82
110
  it( 'returns true if has finished', () => {
83
- const state = {
84
- getFoo: new EquivalentKeyMap( [ [ [], false ] ] ),
85
- };
86
- const result = hasFinishedResolution( state, 'getFoo', [] );
111
+ registry.dispatch( 'testStore' ).finishResolution( 'getFoo', [] );
112
+ const { hasFinishedResolution } = registry.select( 'testStore' );
113
+ const result = hasFinishedResolution( 'getFoo', [] );
87
114
 
88
115
  expect( result ).toBe( true );
89
116
  } );
90
117
  } );
91
118
 
92
119
  describe( 'isResolving', () => {
120
+ let registry;
121
+ beforeEach( () => {
122
+ registry = createRegistry();
123
+ registry.registerStore( 'testStore', testStore );
124
+ } );
125
+
93
126
  it( 'returns false if not has started', () => {
94
- const state = {};
95
- const result = isResolving( state, 'getFoo', [] );
127
+ const { isResolving } = registry.select( 'testStore' );
128
+ const result = isResolving( 'getFoo', [] );
96
129
 
97
130
  expect( result ).toBe( false );
98
131
  } );
99
132
 
100
133
  it( 'returns false if has finished', () => {
101
- const state = {
102
- getFoo: new EquivalentKeyMap( [ [ [], false ] ] ),
103
- };
104
- const result = isResolving( state, 'getFoo', [] );
134
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
135
+ registry.dispatch( 'testStore' ).finishResolution( 'getFoo', [] );
136
+ const { isResolving } = registry.select( 'testStore' );
137
+ const result = isResolving( 'getFoo', [] );
105
138
 
106
139
  expect( result ).toBe( false );
107
140
  } );
108
141
 
109
142
  it( 'returns true if has started but not finished', () => {
110
- const state = {
111
- getFoo: new EquivalentKeyMap( [ [ [], true ] ] ),
112
- };
113
- const result = isResolving( state, 'getFoo', [] );
143
+ registry.dispatch( 'testStore' ).startResolution( 'getFoo', [] );
144
+ const { isResolving } = registry.select( 'testStore' );
145
+ const result = isResolving( 'getFoo', [] );
114
146
 
115
147
  expect( result ).toBe( true );
116
148
  } );
117
149
  } );
150
+
151
+ describe( 'hasResolutionFailed', () => {
152
+ let registry;
153
+
154
+ beforeEach( () => {
155
+ registry = createRegistry();
156
+ } );
157
+
158
+ it( 'returns false if the resolution has succeeded', async () => {
159
+ registry.registerStore( 'store', {
160
+ reducer: ( state = null, action ) => {
161
+ if ( action.type === 'RECEIVE' ) {
162
+ return action.items;
163
+ }
164
+
165
+ return state;
166
+ },
167
+ selectors: {
168
+ getFoo: ( state ) => state,
169
+ },
170
+ resolvers: {
171
+ getFoo: () => {},
172
+ },
173
+ } );
174
+
175
+ expect(
176
+ registry.select( 'store' ).hasResolutionFailed( 'getFoo' )
177
+ ).toBeFalsy();
178
+
179
+ registry.select( 'store' ).getFoo();
180
+
181
+ expect(
182
+ registry.select( 'store' ).hasResolutionFailed( 'getFoo' )
183
+ ).toBeFalsy();
184
+ } );
185
+
186
+ it( 'returns true if the resolution has failed', async () => {
187
+ registry.registerStore( 'store', {
188
+ reducer: ( state = null, action ) => {
189
+ if ( action.type === 'RECEIVE' ) {
190
+ return action.items;
191
+ }
192
+
193
+ return state;
194
+ },
195
+ selectors: {
196
+ getFoo: ( state ) => state,
197
+ },
198
+ resolvers: {
199
+ getFoo: () => {
200
+ throw new Error( 'cannot fetch items' );
201
+ },
202
+ },
203
+ } );
204
+
205
+ expect(
206
+ registry.select( 'store' ).hasResolutionFailed( 'getFoo' )
207
+ ).toBeFalsy();
208
+
209
+ await resolve( registry, 'getFoo' );
210
+
211
+ expect(
212
+ registry.select( 'store' ).hasResolutionFailed( 'getFoo' )
213
+ ).toBeTruthy();
214
+ } );
215
+
216
+ it( 'returns true if the resolution has failed even if the error is falsy', async () => {
217
+ registry.registerStore( 'store', {
218
+ reducer: ( state = null, action ) => {
219
+ if ( action.type === 'RECEIVE' ) {
220
+ return action.items;
221
+ }
222
+
223
+ return state;
224
+ },
225
+ selectors: {
226
+ getFoo: ( state ) => state,
227
+ },
228
+ resolvers: {
229
+ getFoo: () => {
230
+ throw null;
231
+ },
232
+ },
233
+ } );
234
+
235
+ expect(
236
+ registry.select( 'store' ).hasResolutionFailed( 'getFoo' )
237
+ ).toBeFalsy();
238
+
239
+ await resolve( registry, 'getFoo' );
240
+
241
+ expect(
242
+ registry.select( 'store' ).hasResolutionFailed( 'getFoo' )
243
+ ).toBeTruthy();
244
+ } );
245
+ } );
246
+
247
+ describe( 'getResolutionError', () => {
248
+ let registry;
249
+ let shouldFail;
250
+
251
+ beforeEach( () => {
252
+ shouldFail = false;
253
+ registry = createRegistry();
254
+
255
+ registry.registerStore( 'store', {
256
+ reducer: ( state = null, action ) => {
257
+ if ( action.type === 'RECEIVE' ) {
258
+ return action.items;
259
+ }
260
+
261
+ return state;
262
+ },
263
+ selectors: {
264
+ getFoo: ( state ) => state,
265
+ },
266
+ resolvers: {
267
+ getFoo: () => {
268
+ if ( shouldFail ) {
269
+ throw new Error( 'cannot fetch items' );
270
+ }
271
+ },
272
+ },
273
+ } );
274
+ } );
275
+
276
+ it( 'returns undefined if the resolution has succeeded', async () => {
277
+ expect(
278
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
279
+ ).toBeFalsy();
280
+
281
+ registry.select( 'store' ).getFoo();
282
+
283
+ expect(
284
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
285
+ ).toBeFalsy();
286
+ } );
287
+
288
+ it( 'returns error if the resolution has failed', async () => {
289
+ shouldFail = true;
290
+
291
+ expect(
292
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
293
+ ).toBeFalsy();
294
+
295
+ await resolve( registry, 'getFoo' );
296
+
297
+ expect(
298
+ registry.select( 'store' ).getResolutionError( 'getFoo' ).toString()
299
+ ).toBe( 'Error: cannot fetch items' );
300
+ } );
301
+
302
+ it( 'returns undefined if the failed resolution succeeded after retry', async () => {
303
+ shouldFail = true;
304
+ expect(
305
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
306
+ ).toBeFalsy();
307
+
308
+ await resolve( registry, 'getFoo' );
309
+
310
+ expect(
311
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
312
+ ).toBeTruthy();
313
+
314
+ registry.dispatch( 'store' ).invalidateResolution( 'getFoo', [] );
315
+
316
+ expect(
317
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
318
+ ).toBeFalsy();
319
+
320
+ shouldFail = false;
321
+ registry.select( 'store' ).getFoo();
322
+
323
+ expect(
324
+ registry.select( 'store' ).getResolutionError( 'getFoo' )
325
+ ).toBeFalsy();
326
+ } );
327
+ } );