@wordpress/data 6.2.1 → 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 (73) hide show
  1. package/CHANGELOG.md +8 -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 +25 -14
  9. package/build/redux-store/index.js.map +1 -1
  10. package/build/redux-store/metadata/actions.js +51 -8
  11. package/build/redux-store/metadata/actions.js.map +1 -1
  12. package/build/redux-store/metadata/reducer.js +58 -5
  13. package/build/redux-store/metadata/reducer.js.map +1 -1
  14. package/build/redux-store/metadata/selectors.js +93 -26
  15. package/build/redux-store/metadata/selectors.js.map +1 -1
  16. package/build/redux-store/metadata/utils.js +31 -13
  17. package/build/redux-store/metadata/utils.js.map +1 -1
  18. package/build/registry.js +0 -2
  19. package/build/registry.js.map +1 -1
  20. package/build/resolvers-cache-middleware.js +3 -3
  21. package/build/resolvers-cache-middleware.js.map +1 -1
  22. package/build-module/components/use-select/index.js +1 -1
  23. package/build-module/components/use-select/index.js.map +1 -1
  24. package/build-module/factory.js +1 -1
  25. package/build-module/factory.js.map +1 -1
  26. package/build-module/plugins/persistence/index.js +111 -12
  27. package/build-module/plugins/persistence/index.js.map +1 -1
  28. package/build-module/redux-store/index.js +25 -14
  29. package/build-module/redux-store/index.js.map +1 -1
  30. package/build-module/redux-store/metadata/actions.js +47 -8
  31. package/build-module/redux-store/metadata/actions.js.map +1 -1
  32. package/build-module/redux-store/metadata/reducer.js +59 -6
  33. package/build-module/redux-store/metadata/reducer.js.map +1 -1
  34. package/build-module/redux-store/metadata/selectors.js +86 -26
  35. package/build-module/redux-store/metadata/selectors.js.map +1 -1
  36. package/build-module/redux-store/metadata/utils.js +29 -13
  37. package/build-module/redux-store/metadata/utils.js.map +1 -1
  38. package/build-module/registry.js +0 -2
  39. package/build-module/registry.js.map +1 -1
  40. package/build-module/resolvers-cache-middleware.js +3 -3
  41. package/build-module/resolvers-cache-middleware.js.map +1 -1
  42. package/build-types/redux-store/metadata/actions.d.ts +44 -11
  43. package/build-types/redux-store/metadata/actions.d.ts.map +1 -1
  44. package/build-types/redux-store/metadata/reducer.d.ts +10 -2
  45. package/build-types/redux-store/metadata/reducer.d.ts.map +1 -1
  46. package/build-types/redux-store/metadata/selectors.d.ts +56 -16
  47. package/build-types/redux-store/metadata/selectors.d.ts.map +1 -1
  48. package/build-types/redux-store/metadata/utils.d.ts +20 -1
  49. package/build-types/redux-store/metadata/utils.d.ts.map +1 -1
  50. package/build-types/types.d.ts.map +1 -1
  51. package/package.json +8 -8
  52. package/src/components/use-select/index.js +1 -1
  53. package/src/components/use-select/test/index.js +7 -7
  54. package/src/components/with-select/test/index.js +3 -3
  55. package/src/factory.js +1 -1
  56. package/src/plugins/persistence/index.js +142 -8
  57. package/src/plugins/persistence/test/index.js +405 -0
  58. package/src/redux-store/index.js +44 -19
  59. package/src/redux-store/metadata/actions.js +47 -8
  60. package/src/redux-store/metadata/reducer.ts +63 -9
  61. package/src/redux-store/metadata/selectors.js +81 -23
  62. package/src/redux-store/metadata/test/reducer.js +98 -18
  63. package/src/redux-store/metadata/test/selectors.js +266 -45
  64. package/src/redux-store/metadata/test/utils.js +28 -1
  65. package/src/redux-store/metadata/utils.ts +59 -0
  66. package/src/redux-store/test/index.js +42 -0
  67. package/src/registry.js +0 -2
  68. package/src/resolvers-cache-middleware.js +4 -3
  69. package/src/test/controls.js +7 -7
  70. package/src/test/registry.js +6 -6
  71. package/src/types.ts +1 -3
  72. package/tsconfig.tsbuildinfo +1 -1
  73. package/src/redux-store/metadata/utils.js +0 -37
@@ -32,15 +32,34 @@ export function finishResolution( selectorName, args ) {
32
32
  };
33
33
  }
34
34
 
35
+ /**
36
+ * Returns an action object used in signalling that selector resolution has
37
+ * failed.
38
+ *
39
+ * @param {string} selectorName Name of selector for which resolver triggered.
40
+ * @param {unknown[]} args Arguments to associate for uniqueness.
41
+ * @param {Error|unknown} error The error that caused the failure.
42
+ *
43
+ * @return {{ type: 'FAIL_RESOLUTION', selectorName: string, args: unknown[], error: Error|unknown }} Action object.
44
+ */
45
+ export function failResolution( selectorName, args, error ) {
46
+ return {
47
+ type: 'FAIL_RESOLUTION',
48
+ selectorName,
49
+ args,
50
+ error,
51
+ };
52
+ }
53
+
35
54
  /**
36
55
  * Returns an action object used in signalling that a batch of selector resolutions has
37
56
  * started.
38
57
  *
39
- * @param {string} selectorName Name of selector for which resolver triggered.
40
- * @param {unknown[]} args Array of arguments to associate for uniqueness, each item
41
- * is associated to a resolution.
58
+ * @param {string} selectorName Name of selector for which resolver triggered.
59
+ * @param {unknown[][]} args Array of arguments to associate for uniqueness, each item
60
+ * is associated to a resolution.
42
61
  *
43
- * @return {{ type: 'START_RESOLUTIONS', selectorName: string, args: unknown[] }} Action object.
62
+ * @return {{ type: 'START_RESOLUTIONS', selectorName: string, args: unknown[][] }} Action object.
44
63
  */
45
64
  export function startResolutions( selectorName, args ) {
46
65
  return {
@@ -54,11 +73,11 @@ export function startResolutions( selectorName, args ) {
54
73
  * Returns an action object used in signalling that a batch of selector resolutions has
55
74
  * completed.
56
75
  *
57
- * @param {string} selectorName Name of selector for which resolver triggered.
58
- * @param {unknown[]} args Array of arguments to associate for uniqueness, each item
59
- * is associated to a resolution.
76
+ * @param {string} selectorName Name of selector for which resolver triggered.
77
+ * @param {unknown[][]} args Array of arguments to associate for uniqueness, each item
78
+ * is associated to a resolution.
60
79
  *
61
- * @return {{ type: 'FINISH_RESOLUTIONS', selectorName: string, args: unknown[] }} Action object.
80
+ * @return {{ type: 'FINISH_RESOLUTIONS', selectorName: string, args: unknown[][] }} Action object.
62
81
  */
63
82
  export function finishResolutions( selectorName, args ) {
64
83
  return {
@@ -68,6 +87,26 @@ export function finishResolutions( selectorName, args ) {
68
87
  };
69
88
  }
70
89
 
90
+ /**
91
+ * Returns an action object used in signalling that a batch of selector resolutions has
92
+ * completed and at least one of them has failed.
93
+ *
94
+ * @param {string} selectorName Name of selector for which resolver triggered.
95
+ * @param {unknown[]} args Array of arguments to associate for uniqueness, each item
96
+ * is associated to a resolution.
97
+ * @param {(Error|unknown)[]} errors Array of errors to associate for uniqueness, each item
98
+ * is associated to a resolution.
99
+ * @return {{ type: 'FAIL_RESOLUTIONS', selectorName: string, args: unknown[], errors: Array<Error|unknown> }} Action object.
100
+ */
101
+ export function failResolutions( selectorName, args, errors ) {
102
+ return {
103
+ type: 'FAIL_RESOLUTIONS',
104
+ selectorName,
105
+ args,
106
+ errors,
107
+ };
108
+ }
109
+
71
110
  /**
72
111
  * Returns an action object used in signalling that we should invalidate the resolution cache.
73
112
  *
@@ -8,20 +8,28 @@ import type { Reducer } from 'redux';
8
8
  /**
9
9
  * Internal dependencies
10
10
  */
11
- import { onSubKey } from './utils';
11
+ import { selectorArgsToStateKey, onSubKey } from './utils';
12
12
 
13
13
  type Action =
14
14
  | ReturnType< typeof import('./actions').startResolution >
15
15
  | ReturnType< typeof import('./actions').finishResolution >
16
+ | ReturnType< typeof import('./actions').failResolution >
16
17
  | ReturnType< typeof import('./actions').startResolutions >
17
18
  | ReturnType< typeof import('./actions').finishResolutions >
19
+ | ReturnType< typeof import('./actions').failResolutions >
18
20
  | ReturnType< typeof import('./actions').invalidateResolution >
19
21
  | ReturnType< typeof import('./actions').invalidateResolutionForStore >
20
22
  | ReturnType<
21
23
  typeof import('./actions').invalidateResolutionForStoreSelector
22
24
  >;
23
25
 
24
- export type State = EquivalentKeyMap< unknown[] | unknown, boolean >;
26
+ type StateKey = unknown[] | unknown;
27
+ export type StateValue =
28
+ | { status: 'resolving' | 'finished' }
29
+ | { status: 'error'; error: Error | unknown };
30
+
31
+ export type Status = StateValue[ 'status' ];
32
+ export type State = EquivalentKeyMap< StateKey, StateValue >;
25
33
 
26
34
  /**
27
35
  * Reducer function returning next state for selector resolution of
@@ -34,25 +42,69 @@ const subKeysIsResolved: Reducer< Record< string, State >, Action > = onSubKey<
34
42
  Action
35
43
  >( 'selectorName' )( ( state = new EquivalentKeyMap(), action: Action ) => {
36
44
  switch ( action.type ) {
37
- case 'START_RESOLUTION':
45
+ case 'START_RESOLUTION': {
46
+ const nextState = new EquivalentKeyMap( state );
47
+ nextState.set( selectorArgsToStateKey( action.args ), {
48
+ status: 'resolving',
49
+ } );
50
+ return nextState;
51
+ }
38
52
  case 'FINISH_RESOLUTION': {
39
- const isStarting = action.type === 'START_RESOLUTION';
40
53
  const nextState = new EquivalentKeyMap( state );
41
- nextState.set( action.args, isStarting );
54
+ nextState.set( selectorArgsToStateKey( action.args ), {
55
+ status: 'finished',
56
+ } );
57
+ return nextState;
58
+ }
59
+ case 'FAIL_RESOLUTION': {
60
+ const nextState = new EquivalentKeyMap( state );
61
+ nextState.set( selectorArgsToStateKey( action.args ), {
62
+ status: 'error',
63
+ error: action.error,
64
+ } );
65
+ return nextState;
66
+ }
67
+ case 'START_RESOLUTIONS': {
68
+ const nextState = new EquivalentKeyMap( state );
69
+ for ( const resolutionArgs of action.args ) {
70
+ nextState.set( selectorArgsToStateKey( resolutionArgs ), {
71
+ status: 'resolving',
72
+ } );
73
+ }
42
74
  return nextState;
43
75
  }
44
- case 'START_RESOLUTIONS':
45
76
  case 'FINISH_RESOLUTIONS': {
46
- const isStarting = action.type === 'START_RESOLUTIONS';
47
77
  const nextState = new EquivalentKeyMap( state );
48
78
  for ( const resolutionArgs of action.args ) {
49
- nextState.set( resolutionArgs, isStarting );
79
+ nextState.set( selectorArgsToStateKey( resolutionArgs ), {
80
+ status: 'finished',
81
+ } );
50
82
  }
51
83
  return nextState;
52
84
  }
85
+ case 'FAIL_RESOLUTIONS': {
86
+ const nextState = new EquivalentKeyMap( state );
87
+ action.args.forEach( ( resolutionArgs, idx ) => {
88
+ const resolutionState: StateValue = {
89
+ status: 'error',
90
+ error: undefined,
91
+ };
92
+
93
+ const error = action.errors[ idx ];
94
+ if ( error ) {
95
+ resolutionState.error = error;
96
+ }
97
+
98
+ nextState.set(
99
+ selectorArgsToStateKey( resolutionArgs as unknown[] ),
100
+ resolutionState
101
+ );
102
+ } );
103
+ return nextState;
104
+ }
53
105
  case 'INVALIDATE_RESOLUTION': {
54
106
  const nextState = new EquivalentKeyMap( state );
55
- nextState.delete( action.args );
107
+ nextState.delete( selectorArgsToStateKey( action.args ) );
56
108
  return nextState;
57
109
  }
58
110
  }
@@ -79,8 +131,10 @@ const isResolved = ( state: Record< string, State > = {}, action: Action ) => {
79
131
  : state;
80
132
  case 'START_RESOLUTION':
81
133
  case 'FINISH_RESOLUTION':
134
+ case 'FAIL_RESOLUTION':
82
135
  case 'START_RESOLUTIONS':
83
136
  case 'FINISH_RESOLUTIONS':
137
+ case 'FAIL_RESOLUTIONS':
84
138
  case 'INVALIDATE_RESOLUTION':
85
139
  return subKeysIsResolved( state, action );
86
140
  }
@@ -3,69 +3,127 @@
3
3
  */
4
4
  import { get } from 'lodash';
5
5
 
6
+ /**
7
+ * Internal dependencies
8
+ */
9
+ import { selectorArgsToStateKey } from './utils';
10
+
6
11
  /** @typedef {Record<string, import('./reducer').State>} State */
12
+ /** @typedef {import('./reducer').StateValue} StateValue */
13
+ /** @typedef {import('./reducer').Status} Status */
7
14
 
8
15
  /**
9
- * Returns the raw `isResolving` value for a given selector name,
16
+ * Returns the raw resolution state value for a given selector name,
10
17
  * and arguments set. May be undefined if the selector has never been resolved
11
18
  * or not resolved for the given set of arguments, otherwise true or false for
12
19
  * resolution started and completed respectively.
13
20
  *
14
- * @param {State} state Data state.
15
- * @param {string} selectorName Selector name.
16
- * @param {unknown[]} args Arguments passed to selector.
21
+ * @param {State} state Data state.
22
+ * @param {string} selectorName Selector name.
23
+ * @param {unknown[]?} args Arguments passed to selector.
17
24
  *
18
- * @return {boolean | undefined} isResolving value.
25
+ * @return {StateValue|undefined} isResolving value.
19
26
  */
20
- export function getIsResolving( state, selectorName, args ) {
27
+ export function getResolutionState( state, selectorName, args ) {
21
28
  const map = get( state, [ selectorName ] );
22
29
  if ( ! map ) {
23
- return undefined;
30
+ return;
24
31
  }
25
32
 
26
- return map.get( args );
33
+ return map.get( selectorArgsToStateKey( args ) );
34
+ }
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';
27
52
  }
28
53
 
29
54
  /**
30
55
  * Returns true if resolution has already been triggered for a given
31
56
  * selector name, and arguments set.
32
57
  *
33
- * @param {State} state Data state.
34
- * @param {string} selectorName Selector name.
35
- * @param {unknown[]} [args] Arguments passed to selector (default `[]`).
58
+ * @param {State} state Data state.
59
+ * @param {string} selectorName Selector name.
60
+ * @param {unknown[]?} args Arguments passed to selector.
36
61
  *
37
62
  * @return {boolean} Whether resolution has been triggered.
38
63
  */
39
- export function hasStartedResolution( state, selectorName, args = [] ) {
40
- return getIsResolving( state, selectorName, args ) !== undefined;
64
+ export function hasStartedResolution( state, selectorName, args ) {
65
+ return getResolutionState( state, selectorName, args ) !== undefined;
41
66
  }
42
67
 
43
68
  /**
44
69
  * Returns true if resolution has completed for a given selector
45
70
  * name, and arguments set.
46
71
  *
47
- * @param {State} state Data state.
48
- * @param {string} selectorName Selector name.
49
- * @param {unknown[]} [args] Arguments passed to selector.
72
+ * @param {State} state Data state.
73
+ * @param {string} selectorName Selector name.
74
+ * @param {unknown[]?} args Arguments passed to selector.
50
75
  *
51
76
  * @return {boolean} Whether resolution has completed.
52
77
  */
53
- export function hasFinishedResolution( state, selectorName, args = [] ) {
54
- return getIsResolving( state, selectorName, args ) === false;
78
+ export function hasFinishedResolution( state, selectorName, args ) {
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;
55
111
  }
56
112
 
57
113
  /**
58
114
  * Returns true if resolution has been triggered but has not yet completed for
59
115
  * a given selector name, and arguments set.
60
116
  *
61
- * @param {State} state Data state.
62
- * @param {string} selectorName Selector name.
63
- * @param {unknown[]} [args] Arguments passed to selector.
117
+ * @param {State} state Data state.
118
+ * @param {string} selectorName Selector name.
119
+ * @param {unknown[]?} args Arguments passed to selector.
64
120
  *
65
121
  * @return {boolean} Whether resolution is in progress.
66
122
  */
67
- export function isResolving( state, selectorName, args = [] ) {
68
- return getIsResolving( state, selectorName, args ) === true;
123
+ export function isResolving( state, selectorName, args ) {
124
+ return (
125
+ getResolutionState( state, selectorName, args )?.status === 'resolving'
126
+ );
69
127
  }
70
128
 
71
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,10 +131,32 @@ 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
  );
140
+
141
+ it( 'should normalize args array when dispatching actions', () => {
142
+ const started = reducer( undefined, {
143
+ type: 'START_RESOLUTION',
144
+ selectorName: 'getFoo',
145
+ args: [ 1, undefined ],
146
+ } );
147
+ expect( started.getFoo.get( [ 1 ] ) ).toEqual( {
148
+ status: 'resolving',
149
+ } );
150
+
151
+ const finished = reducer( started, {
152
+ type: 'FINISH_RESOLUTION',
153
+ selectorName: 'getFoo',
154
+ args: [ 1, undefined, undefined ],
155
+ } );
156
+ expect( finished.getFoo.get( [ 1 ] ) ).toEqual( {
157
+ status: 'finished',
158
+ } );
159
+ } );
130
160
  } );
131
161
 
132
162
  describe( 'resolution batch', () => {
@@ -137,8 +167,12 @@ describe( 'reducer', () => {
137
167
  args: [ [ 'post' ], [ 'block' ] ],
138
168
  } );
139
169
 
140
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( true );
141
- 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
+ } );
142
176
  } );
143
177
 
144
178
  it( 'should return with finished resolutions', () => {
@@ -153,8 +187,12 @@ describe( 'reducer', () => {
153
187
  args: [ [ 'post' ], [ 'block' ] ],
154
188
  } );
155
189
 
156
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
157
- 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
+ } );
158
196
  } );
159
197
 
160
198
  it( 'should remove invalidations', () => {
@@ -175,7 +213,9 @@ describe( 'reducer', () => {
175
213
  } );
176
214
 
177
215
  expect( state.getFoo.get( [ 'post' ] ) ).toBe( undefined );
178
- expect( state.getFoo.get( [ 'block' ] ) ).toBe( false );
216
+ expect( state.getFoo.get( [ 'block' ] ) ).toEqual( {
217
+ status: 'finished',
218
+ } );
179
219
  } );
180
220
 
181
221
  it( 'different arguments should not conflict', () => {
@@ -195,8 +235,12 @@ describe( 'reducer', () => {
195
235
  args: [ [ 'block' ] ],
196
236
  } );
197
237
 
198
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
199
- 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
+ } );
200
244
  } );
201
245
 
202
246
  it(
@@ -236,9 +280,45 @@ describe( 'reducer', () => {
236
280
  } );
237
281
 
238
282
  expect( state.getBar ).toBeUndefined();
239
- expect( state.getFoo.get( [ 'post' ] ) ).toBe( false );
240
- 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
+ } );
241
289
  }
242
290
  );
291
+
292
+ it( 'should normalize args array when dispatching actions', () => {
293
+ const started = reducer( undefined, {
294
+ type: 'START_RESOLUTIONS',
295
+ selectorName: 'getFoo',
296
+ args: [
297
+ [ 1, undefined ],
298
+ [ 2, undefined, undefined ],
299
+ ],
300
+ } );
301
+ expect( started.getFoo.get( [ 1 ] ) ).toEqual( {
302
+ status: 'resolving',
303
+ } );
304
+ expect( started.getFoo.get( [ 2 ] ) ).toEqual( {
305
+ status: 'resolving',
306
+ } );
307
+
308
+ const finished = reducer( started, {
309
+ type: 'FINISH_RESOLUTIONS',
310
+ selectorName: 'getFoo',
311
+ args: [
312
+ [ 1, undefined, undefined ],
313
+ [ 2, undefined ],
314
+ ],
315
+ } );
316
+ expect( finished.getFoo.get( [ 1 ] ) ).toEqual( {
317
+ status: 'finished',
318
+ } );
319
+ expect( finished.getFoo.get( [ 2 ] ) ).toEqual( {
320
+ status: 'finished',
321
+ } );
322
+ } );
243
323
  } );
244
324
  } );