@wordpress/core-data 4.0.1 → 4.0.5

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 (116) hide show
  1. package/README.md +13 -7
  2. package/build/actions.js +178 -122
  3. package/build/actions.js.map +1 -1
  4. package/build/batch/default-processor.js +58 -27
  5. package/build/batch/default-processor.js.map +1 -1
  6. package/build/entities.js +70 -23
  7. package/build/entities.js.map +1 -1
  8. package/build/fetch/__experimental-fetch-url-data.js +1 -1
  9. package/build/fetch/__experimental-fetch-url-data.js.map +1 -1
  10. package/build/index.js +9 -17
  11. package/build/index.js.map +1 -1
  12. package/build/locks/actions.js +17 -77
  13. package/build/locks/actions.js.map +1 -1
  14. package/build/locks/engine.js +77 -0
  15. package/build/locks/engine.js.map +1 -0
  16. package/build/locks/reducer.js +1 -5
  17. package/build/locks/reducer.js.map +1 -1
  18. package/build/locks/selectors.js +6 -6
  19. package/build/locks/selectors.js.map +1 -1
  20. package/build/queried-data/get-query-parts.js +9 -4
  21. package/build/queried-data/get-query-parts.js.map +1 -1
  22. package/build/queried-data/selectors.js +3 -9
  23. package/build/queried-data/selectors.js.map +1 -1
  24. package/build/reducer.js +17 -22
  25. package/build/reducer.js.map +1 -1
  26. package/build/resolvers.js +151 -97
  27. package/build/resolvers.js.map +1 -1
  28. package/build/selectors.js +79 -14
  29. package/build/selectors.js.map +1 -1
  30. package/build/utils/forward-resolver.js +23 -0
  31. package/build/utils/forward-resolver.js.map +1 -0
  32. package/build/utils/index.js +11 -3
  33. package/build/utils/index.js.map +1 -1
  34. package/build/utils/is-raw-attribute.js +19 -0
  35. package/build/utils/is-raw-attribute.js.map +1 -0
  36. package/build-module/actions.js +155 -112
  37. package/build-module/actions.js.map +1 -1
  38. package/build-module/batch/default-processor.js +57 -27
  39. package/build-module/batch/default-processor.js.map +1 -1
  40. package/build-module/entities.js +65 -19
  41. package/build-module/entities.js.map +1 -1
  42. package/build-module/fetch/__experimental-fetch-url-data.js +1 -1
  43. package/build-module/fetch/__experimental-fetch-url-data.js.map +1 -1
  44. package/build-module/index.js +10 -14
  45. package/build-module/index.js.map +1 -1
  46. package/build-module/locks/actions.js +14 -68
  47. package/build-module/locks/actions.js.map +1 -1
  48. package/build-module/locks/engine.js +66 -0
  49. package/build-module/locks/engine.js.map +1 -0
  50. package/build-module/locks/reducer.js +1 -2
  51. package/build-module/locks/reducer.js.map +1 -1
  52. package/build-module/locks/selectors.js +4 -4
  53. package/build-module/locks/selectors.js.map +1 -1
  54. package/build-module/queried-data/get-query-parts.js +9 -4
  55. package/build-module/queried-data/get-query-parts.js.map +1 -1
  56. package/build-module/queried-data/selectors.js +3 -9
  57. package/build-module/queried-data/selectors.js.map +1 -1
  58. package/build-module/reducer.js +15 -19
  59. package/build-module/reducer.js.map +1 -1
  60. package/build-module/resolvers.js +123 -81
  61. package/build-module/resolvers.js.map +1 -1
  62. package/build-module/selectors.js +74 -13
  63. package/build-module/selectors.js.map +1 -1
  64. package/build-module/utils/forward-resolver.js +15 -0
  65. package/build-module/utils/forward-resolver.js.map +1 -0
  66. package/build-module/utils/index.js +2 -1
  67. package/build-module/utils/index.js.map +1 -1
  68. package/build-module/utils/is-raw-attribute.js +12 -0
  69. package/build-module/utils/is-raw-attribute.js.map +1 -0
  70. package/package.json +10 -11
  71. package/src/actions.js +163 -194
  72. package/src/batch/default-processor.js +57 -26
  73. package/src/batch/test/default-processor.js +53 -26
  74. package/src/entities.js +47 -19
  75. package/src/fetch/__experimental-fetch-url-data.js +1 -1
  76. package/src/index.js +7 -10
  77. package/src/locks/actions.js +10 -61
  78. package/src/locks/engine.js +43 -0
  79. package/src/locks/reducer.js +1 -3
  80. package/src/locks/selectors.js +4 -4
  81. package/src/locks/test/engine.js +135 -0
  82. package/src/locks/test/reducer.js +1 -1
  83. package/src/locks/test/selectors.js +105 -124
  84. package/src/queried-data/get-query-parts.js +11 -6
  85. package/src/queried-data/selectors.js +2 -9
  86. package/src/queried-data/test/get-query-parts.js +1 -1
  87. package/src/queried-data/test/selectors.js +1 -0
  88. package/src/reducer.js +14 -19
  89. package/src/resolvers.js +132 -120
  90. package/src/selectors.js +156 -44
  91. package/src/test/actions.js +330 -170
  92. package/src/test/entities.js +40 -26
  93. package/src/test/resolvers.js +270 -223
  94. package/src/test/selectors.js +127 -1
  95. package/src/utils/forward-resolver.js +14 -0
  96. package/src/utils/index.js +2 -1
  97. package/src/utils/is-raw-attribute.js +11 -0
  98. package/src/utils/test/is-raw-attribute.js +22 -0
  99. package/build/controls.js +0 -44
  100. package/build/controls.js.map +0 -1
  101. package/build/locks/index.js +0 -47
  102. package/build/locks/index.js.map +0 -1
  103. package/build/utils/if-not-resolved.js +0 -46
  104. package/build/utils/if-not-resolved.js.map +0 -1
  105. package/build-module/controls.js +0 -31
  106. package/build-module/controls.js.map +0 -1
  107. package/build-module/locks/index.js +0 -4
  108. package/build-module/locks/index.js.map +0 -1
  109. package/build-module/utils/if-not-resolved.js +0 -36
  110. package/build-module/utils/if-not-resolved.js.map +0 -1
  111. package/src/controls.js +0 -31
  112. package/src/locks/index.js +0 -3
  113. package/src/locks/test/actions.js +0 -307
  114. package/src/test/integration.js +0 -264
  115. package/src/utils/if-not-resolved.js +0 -40
  116. package/src/utils/test/if-not-resolved.js +0 -75
@@ -1,7 +1,9 @@
1
1
  /**
2
2
  * WordPress dependencies
3
3
  */
4
- import { apiFetch } from '@wordpress/data-controls';
4
+ import triggerFetch from '@wordpress/api-fetch';
5
+
6
+ jest.mock( '@wordpress/api-fetch' );
5
7
 
6
8
  /**
7
9
  * Internal dependencies
@@ -14,26 +16,6 @@ import {
14
16
  getAutosaves,
15
17
  getCurrentUser,
16
18
  } from '../resolvers';
17
- import {
18
- receiveEntityRecords,
19
- receiveEmbedPreview,
20
- receiveUserPermission,
21
- receiveAutosaves,
22
- receiveCurrentUser,
23
- } from '../actions';
24
-
25
- jest.mock( '../locks/actions', () => ( {
26
- __unstableAcquireStoreLock: jest.fn( () => [
27
- {
28
- type: 'MOCKED_ACQUIRE_LOCK',
29
- },
30
- ] ),
31
- __unstableReleaseStoreLock: jest.fn( () => [
32
- {
33
- type: 'MOCKED_RELEASE_LOCK',
34
- },
35
- ] ),
36
- } ) );
37
19
 
38
20
  describe( 'getEntityRecord', () => {
39
21
  const POST_TYPE = { slug: 'post' };
@@ -45,28 +27,44 @@ describe( 'getEntityRecord', () => {
45
27
  baseURLParams: { context: 'edit' },
46
28
  },
47
29
  ];
30
+ beforeEach( async () => {
31
+ triggerFetch.mockReset();
32
+ jest.useFakeTimers();
33
+ } );
48
34
 
49
35
  it( 'yields with requested post type', async () => {
50
- const fulfillment = getEntityRecord( 'root', 'postType', 'post' );
51
- // Trigger generator
52
- fulfillment.next();
53
- // Provide entities and acquire lock
54
- expect( fulfillment.next( ENTITIES ).value.type ).toEqual(
55
- 'MOCKED_ACQUIRE_LOCK'
56
- );
57
- // trigger apiFetch
58
- const { value: apiFetchAction } = fulfillment.next();
59
- expect( apiFetchAction.request ).toEqual( {
36
+ const dispatch = Object.assign( jest.fn(), {
37
+ receiveEntityRecords: jest.fn(),
38
+ __unstableAcquireStoreLock: jest.fn(),
39
+ __unstableReleaseStoreLock: jest.fn(),
40
+ } );
41
+ // Provide entities
42
+ dispatch.mockReturnValueOnce( ENTITIES );
43
+
44
+ // Provide response
45
+ triggerFetch.mockImplementation( () => POST_TYPE );
46
+
47
+ await getEntityRecord( 'root', 'postType', 'post' )( { dispatch } );
48
+
49
+ // Fetch request should have been issued
50
+ expect( triggerFetch ).toHaveBeenCalledWith( {
60
51
  path: '/wp/v2/types/post?context=edit',
61
52
  } );
62
- // Provide response and trigger action
63
- const { value: received } = fulfillment.next( POST_TYPE );
64
- expect( received ).toEqual(
65
- receiveEntityRecords( 'root', 'postType', POST_TYPE )
53
+
54
+ // The record should have been received
55
+ expect( dispatch.receiveEntityRecords ).toHaveBeenCalledWith(
56
+ 'root',
57
+ 'postType',
58
+ POST_TYPE,
59
+ undefined
66
60
  );
67
- // Release lock
68
- expect( fulfillment.next().value.type ).toEqual(
69
- 'MOCKED_RELEASE_LOCK'
61
+
62
+ // Locks should have been acquired and released
63
+ expect( dispatch.__unstableAcquireStoreLock ).toHaveBeenCalledTimes(
64
+ 1
65
+ );
66
+ expect( dispatch.__unstableReleaseStoreLock ).toHaveBeenCalledTimes(
67
+ 1
70
68
  );
71
69
  } );
72
70
 
@@ -74,42 +72,54 @@ describe( 'getEntityRecord', () => {
74
72
  const query = { context: 'view', _envelope: '1' };
75
73
  const queryObj = { include: [ 'post' ], ...query };
76
74
 
77
- const fulfillment = getEntityRecord(
75
+ const select = {
76
+ hasEntityRecords: jest.fn( () => {} ),
77
+ };
78
+
79
+ const dispatch = Object.assign( jest.fn(), {
80
+ receiveEntityRecords: jest.fn(),
81
+ __unstableAcquireStoreLock: jest.fn(),
82
+ __unstableReleaseStoreLock: jest.fn(),
83
+ } );
84
+ // Provide entities
85
+ dispatch.mockReturnValueOnce( ENTITIES );
86
+
87
+ // Provide response
88
+ triggerFetch.mockImplementation( () => POST_TYPE );
89
+
90
+ await getEntityRecord(
78
91
  'root',
79
92
  'postType',
80
93
  'post',
81
94
  query
82
- );
83
-
84
- // Trigger generator
85
- fulfillment.next();
86
-
87
- // Provide entities and acquire lock
88
- expect( fulfillment.next( ENTITIES ).value.type ).toEqual(
89
- 'MOCKED_ACQUIRE_LOCK'
90
- );
95
+ )( { dispatch, select } );
91
96
 
92
97
  // Check resolution cache for an existing entity that fulfills the request with query
93
- const {
94
- value: { args: selectArgs },
95
- } = fulfillment.next();
96
- expect( selectArgs ).toEqual( [ 'root', 'postType', queryObj ] );
98
+ expect( select.hasEntityRecords ).toHaveBeenCalledWith(
99
+ 'root',
100
+ 'postType',
101
+ queryObj
102
+ );
97
103
 
98
104
  // Trigger apiFetch, test that the query is present in the url
99
- const { value: apiFetchAction } = fulfillment.next();
100
- expect( apiFetchAction.request ).toEqual( {
105
+ expect( triggerFetch ).toHaveBeenCalledWith( {
101
106
  path: '/wp/v2/types/post?context=view&_envelope=1',
102
107
  } );
103
108
 
104
- // Receive response
105
- const { value: received } = fulfillment.next( POST_TYPE );
106
- expect( received ).toEqual(
107
- receiveEntityRecords( 'root', 'postType', POST_TYPE, queryObj )
109
+ // The record should have been received
110
+ expect( dispatch.receiveEntityRecords ).toHaveBeenCalledWith(
111
+ 'root',
112
+ 'postType',
113
+ POST_TYPE,
114
+ queryObj
108
115
  );
109
116
 
110
- // Release lock
111
- expect( fulfillment.next().value.type ).toEqual(
112
- 'MOCKED_RELEASE_LOCK'
117
+ // Locks should have been acquired and released
118
+ expect( dispatch.__unstableAcquireStoreLock ).toHaveBeenCalledTimes(
119
+ 1
120
+ );
121
+ expect( dispatch.__unstableReleaseStoreLock ).toHaveBeenCalledTimes(
122
+ 1
113
123
  );
114
124
  } );
115
125
  } );
@@ -134,71 +144,97 @@ describe( 'getEntityRecords', () => {
134
144
  },
135
145
  ];
136
146
 
137
- it( 'yields with requested post type', async () => {
138
- const fulfillment = getEntityRecords( 'root', 'postType' );
147
+ beforeEach( async () => {
148
+ triggerFetch.mockReset();
149
+ jest.useFakeTimers();
150
+ } );
139
151
 
140
- // Trigger generator
141
- fulfillment.next();
152
+ it( 'dispatches the requested post type', async () => {
153
+ const dispatch = Object.assign( jest.fn(), {
154
+ receiveEntityRecords: jest.fn(),
155
+ __unstableAcquireStoreLock: jest.fn(),
156
+ __unstableReleaseStoreLock: jest.fn(),
157
+ } );
158
+ // Provide entities
159
+ dispatch.mockReturnValueOnce( ENTITIES );
142
160
 
143
- // Provide entities and acquire lock
144
- fulfillment.next( ENTITIES );
161
+ // Provide response
162
+ triggerFetch.mockImplementation( () => POST_TYPES );
145
163
 
146
- // trigger apiFetch
147
- const { value: apiFetchAction } = fulfillment.next();
164
+ await getEntityRecords( 'root', 'postType' )( { dispatch } );
148
165
 
149
- expect( apiFetchAction.request ).toEqual( {
166
+ // Fetch request should have been issued
167
+ expect( triggerFetch ).toHaveBeenCalledWith( {
150
168
  path: '/wp/v2/types?context=edit',
151
169
  } );
152
- // Provide response and trigger action
153
- const { value: received } = fulfillment.next( POST_TYPES );
154
- expect( received ).toEqual(
155
- receiveEntityRecords(
156
- 'root',
157
- 'postType',
158
- Object.values( POST_TYPES ),
159
- {}
160
- )
170
+
171
+ // The record should have been received
172
+ expect( dispatch.receiveEntityRecords ).toHaveBeenCalledWith(
173
+ 'root',
174
+ 'postType',
175
+ Object.values( POST_TYPES ),
176
+ {}
161
177
  );
162
178
  } );
163
179
 
164
180
  it( 'Uses state locks', async () => {
165
- const fulfillment = getEntityRecords( 'root', 'postType' );
181
+ const dispatch = Object.assign( jest.fn(), {
182
+ receiveEntityRecords: jest.fn(),
183
+ __unstableAcquireStoreLock: jest.fn(),
184
+ __unstableReleaseStoreLock: jest.fn(),
185
+ } );
186
+ // Provide entities
187
+ dispatch.mockReturnValueOnce( ENTITIES );
166
188
 
167
- // Repeat the steps from `yields with requested post type` test
168
- fulfillment.next();
169
- // Provide entities and acquire lock
170
- expect( fulfillment.next( ENTITIES ).value.type ).toEqual(
171
- 'MOCKED_ACQUIRE_LOCK'
172
- );
173
- fulfillment.next();
174
- fulfillment.next( POST_TYPES );
189
+ // Provide response
190
+ triggerFetch.mockImplementation( () => POST_TYPES );
175
191
 
176
- // Resolve specific entity records
177
- fulfillment.next();
178
- fulfillment.next();
192
+ await getEntityRecords( 'root', 'postType' )( { dispatch } );
179
193
 
180
- // Release lock
181
- expect( fulfillment.next().value.type ).toEqual(
182
- 'MOCKED_RELEASE_LOCK'
194
+ // Fetch request should have been issued
195
+ expect( triggerFetch ).toHaveBeenCalledWith( {
196
+ path: '/wp/v2/types?context=edit',
197
+ } );
198
+
199
+ // The record should have been received
200
+ expect(
201
+ dispatch.__unstableAcquireStoreLock
202
+ ).toHaveBeenCalledWith(
203
+ 'core',
204
+ [ 'entities', 'data', 'root', 'postType' ],
205
+ { exclusive: false }
206
+ );
207
+ expect( dispatch.__unstableReleaseStoreLock ).toHaveBeenCalledTimes(
208
+ 1
183
209
  );
184
210
  } );
185
211
 
186
212
  it( 'marks specific entity records as resolved', async () => {
187
- const fulfillment = getEntityRecords( 'root', 'postType' );
213
+ const dispatch = Object.assign( jest.fn(), {
214
+ receiveEntityRecords: jest.fn(),
215
+ __unstableAcquireStoreLock: jest.fn(),
216
+ __unstableReleaseStoreLock: jest.fn(),
217
+ } );
218
+ // Provide entities
219
+ dispatch.mockReturnValueOnce( ENTITIES );
220
+
221
+ // Provide response
222
+ triggerFetch.mockImplementation( () => POST_TYPES );
223
+
224
+ await getEntityRecords( 'root', 'postType' )( { dispatch } );
188
225
 
189
- // Repeat the steps from `yields with requested post type` test
190
- fulfillment.next();
191
- fulfillment.next( ENTITIES );
192
- fulfillment.next();
193
- fulfillment.next( POST_TYPES );
226
+ // Fetch request should have been issued
227
+ expect( triggerFetch ).toHaveBeenCalledWith( {
228
+ path: '/wp/v2/types?context=edit',
229
+ } );
194
230
 
195
- // It should mark the entity record that has an ID as resolved
196
- expect( fulfillment.next().value ).toEqual( {
231
+ // The record should have been received
232
+ expect( dispatch ).toHaveBeenCalledWith( {
197
233
  type: 'START_RESOLUTIONS',
198
234
  selectorName: 'getEntityRecord',
199
235
  args: [ [ ENTITIES[ 1 ].kind, ENTITIES[ 1 ].name, 2 ] ],
200
236
  } );
201
- expect( fulfillment.next().value ).toEqual( {
237
+ expect( dispatch ).toHaveBeenCalledWith( {
202
238
  type: 'FINISH_RESOLUTIONS',
203
239
  selectorName: 'getEntityRecord',
204
240
  args: [ [ ENTITIES[ 1 ].kind, ENTITIES[ 1 ].name, 2 ] ],
@@ -213,130 +249,136 @@ describe( 'getEmbedPreview', () => {
213
249
  const UNEMBEDDABLE_URL = 'http://example.com/';
214
250
 
215
251
  it( 'yields with fetched embed preview', async () => {
216
- const fulfillment = getEmbedPreview( EMBEDDABLE_URL );
217
- // Trigger generator
218
- fulfillment.next();
219
- // Provide apiFetch response and trigger Action
220
- const received = ( await fulfillment.next( SUCCESSFUL_EMBED_RESPONSE ) )
221
- .value;
222
- expect( received ).toEqual(
223
- receiveEmbedPreview( EMBEDDABLE_URL, SUCCESSFUL_EMBED_RESPONSE )
252
+ const dispatch = Object.assign( jest.fn(), {
253
+ receiveEmbedPreview: jest.fn(),
254
+ } );
255
+
256
+ // Provide response
257
+ triggerFetch.mockResolvedValue( SUCCESSFUL_EMBED_RESPONSE );
258
+
259
+ await getEmbedPreview( EMBEDDABLE_URL )( { dispatch } );
260
+
261
+ expect( dispatch.receiveEmbedPreview ).toHaveBeenCalledWith(
262
+ EMBEDDABLE_URL,
263
+ SUCCESSFUL_EMBED_RESPONSE
224
264
  );
225
265
  } );
226
266
 
227
267
  it( 'yields false if the URL cannot be embedded', async () => {
228
- const fulfillment = getEmbedPreview( UNEMBEDDABLE_URL );
229
- // Trigger generator
230
- fulfillment.next();
231
- // Provide invalid response and trigger Action
232
- const received = ( await fulfillment.throw( { status: 404 } ) ).value;
233
- expect( received ).toEqual(
234
- receiveEmbedPreview( UNEMBEDDABLE_URL, UNEMBEDDABLE_RESPONSE )
268
+ const dispatch = Object.assign( jest.fn(), {
269
+ receiveEmbedPreview: jest.fn(),
270
+ } );
271
+
272
+ // Provide response
273
+ triggerFetch.mockRejectedValue( { status: 404 } );
274
+
275
+ await getEmbedPreview( UNEMBEDDABLE_URL )( { dispatch } );
276
+
277
+ expect( dispatch.receiveEmbedPreview ).toHaveBeenCalledWith(
278
+ UNEMBEDDABLE_URL,
279
+ UNEMBEDDABLE_RESPONSE
235
280
  );
236
281
  } );
237
282
  } );
238
283
 
239
284
  describe( 'canUser', () => {
240
- it( 'does nothing when there is an API error', () => {
241
- const generator = canUser( 'create', 'media' );
242
-
243
- let received = generator.next();
244
- expect( received.done ).toBe( false );
245
- expect( received.value ).toEqual(
246
- apiFetch( {
247
- path: '/wp/v2/media',
248
- method: 'OPTIONS',
249
- parse: false,
250
- } )
285
+ beforeEach( async () => {
286
+ triggerFetch.mockReset();
287
+ } );
288
+
289
+ it( 'does nothing when there is an API error', async () => {
290
+ const dispatch = Object.assign( jest.fn(), {
291
+ receiveUserPermission: jest.fn(),
292
+ } );
293
+
294
+ triggerFetch.mockImplementation( () =>
295
+ Promise.reject( { status: 404 } )
251
296
  );
252
297
 
253
- received = generator.throw( { status: 404 } );
254
- expect( received.done ).toBe( true );
255
- expect( received.value ).toBeUndefined();
298
+ await canUser( 'create', 'media' )( { dispatch } );
299
+
300
+ expect( triggerFetch ).toHaveBeenCalledWith( {
301
+ path: '/wp/v2/media',
302
+ method: 'OPTIONS',
303
+ parse: false,
304
+ } );
305
+
306
+ expect( dispatch.receiveUserPermission ).not.toHaveBeenCalled();
256
307
  } );
257
308
 
258
- it( 'receives false when the user is not allowed to perform an action', () => {
259
- const generator = canUser( 'create', 'media' );
260
-
261
- let received = generator.next();
262
- expect( received.done ).toBe( false );
263
- expect( received.value ).toEqual(
264
- apiFetch( {
265
- path: '/wp/v2/media',
266
- method: 'OPTIONS',
267
- parse: false,
268
- } )
269
- );
309
+ it( 'receives false when the user is not allowed to perform an action', async () => {
310
+ const dispatch = Object.assign( jest.fn(), {
311
+ receiveUserPermission: jest.fn(),
312
+ } );
270
313
 
271
- received = generator.next( {
314
+ triggerFetch.mockImplementation( () => ( {
272
315
  headers: {
273
316
  Allow: 'GET',
274
317
  },
318
+ } ) );
319
+
320
+ await canUser( 'create', 'media' )( { dispatch } );
321
+
322
+ expect( triggerFetch ).toHaveBeenCalledWith( {
323
+ path: '/wp/v2/media',
324
+ method: 'OPTIONS',
325
+ parse: false,
275
326
  } );
276
- expect( received.done ).toBe( false );
277
- expect( received.value ).toEqual(
278
- receiveUserPermission( 'create/media', false )
279
- );
280
327
 
281
- received = generator.next();
282
- expect( received.done ).toBe( true );
283
- expect( received.value ).toBeUndefined();
328
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
329
+ 'create/media',
330
+ false
331
+ );
284
332
  } );
285
333
 
286
- it( 'receives true when the user is allowed to perform an action', () => {
287
- const generator = canUser( 'create', 'media' );
288
-
289
- let received = generator.next();
290
- expect( received.done ).toBe( false );
291
- expect( received.value ).toEqual(
292
- apiFetch( {
293
- path: '/wp/v2/media',
294
- method: 'OPTIONS',
295
- parse: false,
296
- } )
297
- );
334
+ it( 'receives true when the user is allowed to perform an action', async () => {
335
+ const dispatch = Object.assign( jest.fn(), {
336
+ receiveUserPermission: jest.fn(),
337
+ } );
298
338
 
299
- received = generator.next( {
339
+ triggerFetch.mockImplementation( () => ( {
300
340
  headers: {
301
341
  Allow: 'POST, GET, PUT, DELETE',
302
342
  },
343
+ } ) );
344
+
345
+ await canUser( 'create', 'media' )( { dispatch } );
346
+
347
+ expect( triggerFetch ).toHaveBeenCalledWith( {
348
+ path: '/wp/v2/media',
349
+ method: 'OPTIONS',
350
+ parse: false,
303
351
  } );
304
- expect( received.done ).toBe( false );
305
- expect( received.value ).toEqual(
306
- receiveUserPermission( 'create/media', true )
307
- );
308
352
 
309
- received = generator.next();
310
- expect( received.done ).toBe( true );
311
- expect( received.value ).toBeUndefined();
353
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
354
+ 'create/media',
355
+ true
356
+ );
312
357
  } );
313
358
 
314
- it( 'receives true when the user is allowed to perform an action on a specific resource', () => {
315
- const generator = canUser( 'update', 'blocks', 123 );
316
-
317
- let received = generator.next();
318
- expect( received.done ).toBe( false );
319
- expect( received.value ).toEqual(
320
- apiFetch( {
321
- path: '/wp/v2/blocks/123',
322
- method: 'GET',
323
- parse: false,
324
- } )
325
- );
359
+ it( 'receives true when the user is allowed to perform an action on a specific resource', async () => {
360
+ const dispatch = Object.assign( jest.fn(), {
361
+ receiveUserPermission: jest.fn(),
362
+ } );
326
363
 
327
- received = generator.next( {
364
+ triggerFetch.mockImplementation( () => ( {
328
365
  headers: {
329
366
  Allow: 'POST, GET, PUT, DELETE',
330
367
  },
368
+ } ) );
369
+
370
+ await canUser( 'create', 'blocks', 123 )( { dispatch } );
371
+
372
+ expect( triggerFetch ).toHaveBeenCalledWith( {
373
+ path: '/wp/v2/blocks/123',
374
+ method: 'GET',
375
+ parse: false,
331
376
  } );
332
- expect( received.done ).toBe( false );
333
- expect( received.value ).toEqual(
334
- receiveUserPermission( 'update/blocks/123', true )
335
- );
336
377
 
337
- received = generator.next();
338
- expect( received.done ).toBe( true );
339
- expect( received.value ).toBeUndefined();
378
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
379
+ 'create/blocks/123',
380
+ true
381
+ );
340
382
  } );
341
383
  } );
342
384
 
@@ -349,28 +391,31 @@ describe( 'getAutosaves', () => {
349
391
  },
350
392
  ];
351
393
 
394
+ beforeEach( async () => {
395
+ triggerFetch.mockReset();
396
+ } );
397
+
352
398
  it( 'yields with fetched autosaves', async () => {
353
399
  const postType = 'post';
354
400
  const postId = 1;
355
401
  const restBase = 'posts';
356
402
  const postEntity = { rest_base: restBase };
357
- const fulfillment = getAutosaves( postType, postId );
358
403
 
359
- // Trigger generator
360
- fulfillment.next();
404
+ triggerFetch.mockImplementation( () => SUCCESSFUL_RESPONSE );
405
+ const dispatch = Object.assign( jest.fn(), {
406
+ receiveAutosaves: jest.fn(),
407
+ } );
408
+ const resolveSelect = Object.assign( jest.fn(), {
409
+ getPostType: jest.fn( () => postEntity ),
410
+ } );
411
+ await getAutosaves( postType, postId )( { dispatch, resolveSelect } );
361
412
 
362
- // Trigger generator with the postEntity and assert that correct path is formed
363
- // in the apiFetch request.
364
- const { value: apiFetchAction } = fulfillment.next( postEntity );
365
- expect( apiFetchAction.request ).toEqual( {
413
+ expect( triggerFetch ).toHaveBeenCalledWith( {
366
414
  path: `/wp/v2/${ restBase }/${ postId }/autosaves?context=edit`,
367
415
  } );
368
-
369
- // Provide apiFetch response and trigger Action
370
- const received = ( await fulfillment.next( SUCCESSFUL_RESPONSE ) )
371
- .value;
372
- expect( received ).toEqual(
373
- receiveAutosaves( 1, SUCCESSFUL_RESPONSE )
416
+ expect( dispatch.receiveAutosaves ).toHaveBeenCalledWith(
417
+ 1,
418
+ SUCCESSFUL_RESPONSE
374
419
  );
375
420
  } );
376
421
 
@@ -379,21 +424,20 @@ describe( 'getAutosaves', () => {
379
424
  const postId = 1;
380
425
  const restBase = 'posts';
381
426
  const postEntity = { rest_base: restBase };
382
- const fulfillment = getAutosaves( postType, postId );
383
427
 
384
- // Trigger generator
385
- fulfillment.next();
428
+ triggerFetch.mockImplementation( () => [] );
429
+ const dispatch = Object.assign( jest.fn(), {
430
+ receiveAutosaves: jest.fn(),
431
+ } );
432
+ const resolveSelect = Object.assign( jest.fn(), {
433
+ getPostType: jest.fn( () => postEntity ),
434
+ } );
435
+ await getAutosaves( postType, postId )( { dispatch, resolveSelect } );
386
436
 
387
- // Trigger generator with the postEntity and assert that correct path is formed
388
- // in the apiFetch request.
389
- const { value: apiFetchAction } = fulfillment.next( postEntity );
390
- expect( apiFetchAction.request ).toEqual( {
437
+ expect( triggerFetch ).toHaveBeenCalledWith( {
391
438
  path: `/wp/v2/${ restBase }/${ postId }/autosaves?context=edit`,
392
439
  } );
393
-
394
- // Provide apiFetch response and trigger Action
395
- const received = ( await fulfillment.next( [] ) ).value;
396
- expect( received ).toBeUndefined();
440
+ expect( dispatch.receiveAutosaves ).not.toHaveBeenCalled();
397
441
  } );
398
442
  } );
399
443
 
@@ -403,14 +447,17 @@ describe( 'getCurrentUser', () => {
403
447
  };
404
448
 
405
449
  it( 'yields with fetched user', async () => {
406
- const fulfillment = getCurrentUser();
450
+ const dispatch = Object.assign( jest.fn(), {
451
+ receiveCurrentUser: jest.fn(),
452
+ } );
407
453
 
408
- // Trigger generator
409
- fulfillment.next();
454
+ // Provide response
455
+ triggerFetch.mockResolvedValue( SUCCESSFUL_RESPONSE );
410
456
 
411
- // Provide apiFetch response and trigger Action
412
- const received = ( await fulfillment.next( SUCCESSFUL_RESPONSE ) )
413
- .value;
414
- expect( received ).toEqual( receiveCurrentUser( SUCCESSFUL_RESPONSE ) );
457
+ await getCurrentUser()( { dispatch } );
458
+
459
+ expect( dispatch.receiveCurrentUser ).toHaveBeenCalledWith(
460
+ SUCCESSFUL_RESPONSE
461
+ );
415
462
  } );
416
463
  } );