@wordpress/core-data 7.30.1-next.836ecdcae.0 → 7.31.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 +2 -0
  2. package/README.md +23 -1
  3. package/build/actions.js +32 -2
  4. package/build/actions.js.map +1 -1
  5. package/build/entities.js +1 -1
  6. package/build/entities.js.map +1 -1
  7. package/build/entity-types/user.js.map +1 -1
  8. package/build/hooks/index.js.map +1 -1
  9. package/build/hooks/use-entity-records.js.map +1 -1
  10. package/build/private-actions.js +8 -0
  11. package/build/private-actions.js.map +1 -1
  12. package/build/private-selectors.js +8 -1
  13. package/build/private-selectors.js.map +1 -1
  14. package/build/reducer.js +9 -1
  15. package/build/reducer.js.map +1 -1
  16. package/build/resolvers.js +80 -64
  17. package/build/resolvers.js.map +1 -1
  18. package/build/selectors.js +99 -41
  19. package/build/selectors.js.map +1 -1
  20. package/build-module/actions.js +32 -2
  21. package/build-module/actions.js.map +1 -1
  22. package/build-module/entities.js +1 -1
  23. package/build-module/entities.js.map +1 -1
  24. package/build-module/entity-types/user.js.map +1 -1
  25. package/build-module/hooks/index.js +8 -0
  26. package/build-module/hooks/index.js.map +1 -1
  27. package/build-module/hooks/use-entity-records.js.map +1 -1
  28. package/build-module/private-actions.js +7 -0
  29. package/build-module/private-actions.js.map +1 -1
  30. package/build-module/private-selectors.js +7 -1
  31. package/build-module/private-selectors.js.map +1 -1
  32. package/build-module/reducer.js +8 -1
  33. package/build-module/reducer.js.map +1 -1
  34. package/build-module/resolvers.js +76 -61
  35. package/build-module/resolvers.js.map +1 -1
  36. package/build-module/selectors.js +98 -41
  37. package/build-module/selectors.js.map +1 -1
  38. package/build-types/actions.d.ts.map +1 -1
  39. package/build-types/entity-types/user.d.ts +2 -2
  40. package/build-types/entity-types/user.d.ts.map +1 -1
  41. package/build-types/hooks/index.d.ts +8 -0
  42. package/build-types/hooks/index.d.ts.map +1 -1
  43. package/build-types/hooks/use-entity-records.d.ts +12 -2
  44. package/build-types/hooks/use-entity-records.d.ts.map +1 -1
  45. package/build-types/index.d.ts +2 -1
  46. package/build-types/index.d.ts.map +1 -1
  47. package/build-types/private-actions.d.ts +5 -0
  48. package/build-types/private-actions.d.ts.map +1 -1
  49. package/build-types/private-selectors.d.ts +1 -0
  50. package/build-types/private-selectors.d.ts.map +1 -1
  51. package/build-types/reducer.d.ts +3 -0
  52. package/build-types/reducer.d.ts.map +1 -1
  53. package/build-types/resolvers.d.ts +7 -0
  54. package/build-types/resolvers.d.ts.map +1 -1
  55. package/build-types/selectors.d.ts +18 -2
  56. package/build-types/selectors.d.ts.map +1 -1
  57. package/package.json +18 -18
  58. package/src/actions.js +43 -1
  59. package/src/entities.js +1 -1
  60. package/src/entity-types/user.ts +2 -2
  61. package/src/hooks/index.ts +9 -0
  62. package/src/hooks/use-entity-records.ts +12 -2
  63. package/src/private-actions.js +4 -0
  64. package/src/private-selectors.ts +10 -0
  65. package/src/reducer.js +7 -0
  66. package/src/resolvers.js +127 -81
  67. package/src/selectors.ts +110 -40
  68. package/src/test/actions.js +2 -2
  69. package/src/test/entities.js +32 -0
  70. package/src/test/resolvers.js +103 -12
  71. package/src/test/selectors.js +220 -13
  72. package/src/test/store.js +114 -0
  73. package/tsconfig.tsbuildinfo +1 -1
@@ -8,7 +8,7 @@ import deepFreeze from 'deep-freeze';
8
8
  */
9
9
  import {
10
10
  getEntityRecord,
11
- __experimentalGetEntityRecordNoResolver,
11
+ hasEntityRecord,
12
12
  hasEntityRecords,
13
13
  getEntityRecords,
14
14
  getRawEntityRecord,
@@ -25,11 +25,7 @@ import {
25
25
  getRevision,
26
26
  } from '../selectors';
27
27
 
28
- // getEntityRecord and __experimentalGetEntityRecordNoResolver selectors share the same tests.
29
- describe.each( [
30
- [ getEntityRecord ],
31
- [ __experimentalGetEntityRecordNoResolver ],
32
- ] )( '%p', ( selector ) => {
28
+ describe( 'getEntityRecord', () => {
33
29
  describe( 'normalizing Post ID passed as recordKey', () => {
34
30
  it( 'normalizes any Post ID recordKey argument to a Number via `__unstableNormalizeArgs` method', async () => {
35
31
  const normalized = getEntityRecord.__unstableNormalizeArgs( [
@@ -70,7 +66,7 @@ describe.each( [
70
66
  },
71
67
  },
72
68
  } );
73
- expect( selector( state, 'foo', 'bar', 'baz' ) ).toBeUndefined();
69
+ expect( getEntityRecord( state, 'foo', 'bar', 'baz' ) ).toBeUndefined();
74
70
  } );
75
71
 
76
72
  it( 'should return undefined for unknown record’s key', () => {
@@ -89,7 +85,9 @@ describe.each( [
89
85
  },
90
86
  },
91
87
  } );
92
- expect( selector( state, 'root', 'postType', 'post' ) ).toBeUndefined();
88
+ expect(
89
+ getEntityRecord( state, 'root', 'postType', 'post' )
90
+ ).toBeUndefined();
93
91
  } );
94
92
 
95
93
  it( 'should return a record by key', () => {
@@ -116,16 +114,98 @@ describe.each( [
116
114
  },
117
115
  },
118
116
  } );
119
- expect( selector( state, 'root', 'postType', 'post' ) ).toEqual( {
120
- slug: 'post',
117
+ expect( getEntityRecord( state, 'root', 'postType', 'post' ) ).toEqual(
118
+ {
119
+ slug: 'post',
120
+ }
121
+ );
122
+ } );
123
+
124
+ it( 'should return undefined if no item received, filtered item requested', () => {
125
+ const state = deepFreeze( {
126
+ entities: {
127
+ records: {
128
+ root: {
129
+ postType: {
130
+ queriedData: {
131
+ items: {},
132
+ itemIsComplete: {},
133
+ queries: {},
134
+ },
135
+ },
136
+ },
137
+ },
138
+ },
121
139
  } );
140
+
141
+ expect(
142
+ getEntityRecord( state, 'root', 'postType', 'post', {
143
+ _fields: 'content',
144
+ } )
145
+ ).toBeUndefined();
122
146
  } );
123
147
 
124
- it( 'should return null if no item received, filtered item requested', () => {} );
148
+ it( 'should return filtered item if incomplete item received, filtered item requested', () => {
149
+ const state = deepFreeze( {
150
+ entities: {
151
+ records: {
152
+ root: {
153
+ postType: {
154
+ queriedData: {
155
+ items: {
156
+ default: {
157
+ post: {
158
+ content: 'chicken',
159
+ author: 'bob',
160
+ },
161
+ },
162
+ },
163
+ itemIsComplete: {},
164
+ queries: {},
165
+ },
166
+ },
167
+ },
168
+ },
169
+ },
170
+ } );
125
171
 
126
- it( 'should return filtered item if incomplete item received, filtered item requested', () => {} );
172
+ expect(
173
+ getEntityRecord( state, 'root', 'postType', 'post', {
174
+ _fields: 'content',
175
+ } )
176
+ ).toEqual( { content: 'chicken' } );
177
+ } );
178
+
179
+ it( 'should return undefined if incomplete item received, complete item requested', () => {
180
+ const state = deepFreeze( {
181
+ entities: {
182
+ records: {
183
+ root: {
184
+ postType: {
185
+ queriedData: {
186
+ items: {
187
+ default: {
188
+ post: {
189
+ content: 'chicken',
190
+ author: 'bob',
191
+ },
192
+ },
193
+ },
194
+ itemIsComplete: {},
195
+ queries: {},
196
+ },
197
+ },
198
+ },
199
+ },
200
+ },
201
+ } );
127
202
 
128
- it( 'should return null if incomplete item received, complete item requested', () => {} );
203
+ expect(
204
+ getEntityRecord( state, 'root', 'postType', 'post', {
205
+ context: 'default',
206
+ } )
207
+ ).toBeUndefined();
208
+ } );
129
209
 
130
210
  it( 'should return filtered item if complete item received, filtered item requested', () => {
131
211
  const state = deepFreeze( {
@@ -200,6 +280,133 @@ describe.each( [
200
280
  } );
201
281
  } );
202
282
 
283
+ describe( 'hasEntityRecord', () => {
284
+ it( 'returns false if entity record has not been received', () => {
285
+ const state = deepFreeze( {
286
+ entities: {
287
+ records: {
288
+ postType: {
289
+ post: {
290
+ queriedData: {
291
+ items: {},
292
+ itemIsComplete: {},
293
+ queries: {},
294
+ },
295
+ },
296
+ },
297
+ },
298
+ },
299
+ } );
300
+ expect( hasEntityRecord( state, 'postType', 'post', 1 ) ).toBe( false );
301
+ } );
302
+
303
+ it( 'returns true when full record exists and no fields query', () => {
304
+ const state = deepFreeze( {
305
+ entities: {
306
+ records: {
307
+ postType: {
308
+ post: {
309
+ queriedData: {
310
+ items: {
311
+ default: {
312
+ 1: { id: 1, content: 'hello' },
313
+ },
314
+ },
315
+ itemIsComplete: {
316
+ default: {
317
+ 1: true,
318
+ },
319
+ },
320
+ queries: {},
321
+ },
322
+ },
323
+ },
324
+ },
325
+ },
326
+ } );
327
+ expect( hasEntityRecord( state, 'postType', 'post', 1 ) ).toBe( true );
328
+ } );
329
+
330
+ it( 'returns true when requested fields exist on the item', () => {
331
+ const state = deepFreeze( {
332
+ entities: {
333
+ records: {
334
+ postType: {
335
+ post: {
336
+ queriedData: {
337
+ items: {
338
+ default: {
339
+ 1: {
340
+ id: 1,
341
+ content: 'chicken',
342
+ title: { raw: 'egg' },
343
+ author: 'bob',
344
+ },
345
+ },
346
+ },
347
+ itemIsComplete: {
348
+ default: {
349
+ 1: true,
350
+ },
351
+ },
352
+ queries: {},
353
+ },
354
+ },
355
+ },
356
+ },
357
+ },
358
+ } );
359
+ expect(
360
+ hasEntityRecord( state, 'postType', 'post', 1, {
361
+ _fields: [ 'id', 'content' ],
362
+ } )
363
+ ).toBe( true );
364
+ // Test nested field.
365
+ expect(
366
+ hasEntityRecord( state, 'postType', 'post', 1, {
367
+ _fields: [ 'id', 'title.raw' ],
368
+ } )
369
+ ).toBe( true );
370
+ } );
371
+
372
+ it( 'returns false when a requested fields are missing', () => {
373
+ const state = deepFreeze( {
374
+ entities: {
375
+ records: {
376
+ postType: {
377
+ post: {
378
+ queriedData: {
379
+ items: {
380
+ default: {
381
+ 1: { id: 1, author: 'bob' },
382
+ },
383
+ },
384
+ itemIsComplete: {
385
+ default: {
386
+ 1: true,
387
+ },
388
+ },
389
+ queries: {},
390
+ },
391
+ },
392
+ },
393
+ },
394
+ },
395
+ } );
396
+ expect(
397
+ hasEntityRecord( state, 'postType', 'post', 1, {
398
+ _fields: [ 'id', 'content' ],
399
+ } )
400
+ ).toBe( false );
401
+ // Test nested field.
402
+ expect(
403
+ hasEntityRecord( state, 'postType', 'post', 1, {
404
+ _fields: [ 'id', 'title.raw' ],
405
+ } )
406
+ ).toBe( false );
407
+ } );
408
+ } );
409
+
203
410
  describe( 'hasEntityRecords', () => {
204
411
  it( 'returns false if entity records have not been received', () => {
205
412
  const state = deepFreeze( {
@@ -0,0 +1,114 @@
1
+ /**
2
+ * WordPress dependencies
3
+ */
4
+ import triggerFetch from '@wordpress/api-fetch';
5
+ import { createRegistry } from '@wordpress/data';
6
+
7
+ /**
8
+ * Internal dependencies
9
+ */
10
+ import { store as coreDataStore } from '../index';
11
+
12
+ jest.mock( '@wordpress/api-fetch' );
13
+
14
+ function createTestRegistry() {
15
+ const registry = createRegistry();
16
+
17
+ // Register the core-data store
18
+ registry.register( coreDataStore );
19
+
20
+ const postEntityConfig = {
21
+ kind: 'postType',
22
+ baseURL: '/wp/v2/posts',
23
+ baseURLParams: {
24
+ context: 'edit',
25
+ },
26
+ name: 'post',
27
+ label: 'Posts',
28
+ transientEdits: {
29
+ blocks: true,
30
+ selection: true,
31
+ },
32
+ mergedEdits: {
33
+ meta: true,
34
+ },
35
+ rawAttributes: [ 'title', 'excerpt', 'content' ],
36
+ __unstable_rest_base: 'posts',
37
+ supportsPagination: true,
38
+ revisionKey: 'id',
39
+ };
40
+
41
+ // Add the post entity to the store
42
+ registry.dispatch( coreDataStore ).addEntities( [ postEntityConfig ] );
43
+
44
+ return registry;
45
+ }
46
+
47
+ function createTestPost( id = 1, fields = [] ) {
48
+ const post = {
49
+ id,
50
+ author: 1,
51
+ content: {
52
+ raw: '<!-- wp:paragraph -->\n<p>A paragraph</p>\n<!-- /wp:paragraph -->',
53
+ rendered: '\n<p>A paragraph</p>\n',
54
+ },
55
+ excerpt: {
56
+ raw: '',
57
+ rendered: '<p>A paragraph</p>\n',
58
+ },
59
+ title: {
60
+ raw: 'Test',
61
+ rendered: 'Test',
62
+ },
63
+ featured_media: 0,
64
+ type: 'post',
65
+ status: 'draft',
66
+ slug: '',
67
+ };
68
+
69
+ if ( fields.length > 0 ) {
70
+ return Object.fromEntries(
71
+ fields.map( ( field ) => [ field, post[ field ] ] )
72
+ );
73
+ }
74
+
75
+ return post;
76
+ }
77
+
78
+ describe( 'getEntityRecord', () => {
79
+ let registry;
80
+
81
+ beforeEach( () => {
82
+ registry = createTestRegistry();
83
+ triggerFetch.mockReset();
84
+ } );
85
+
86
+ it( 'should not make a request if the record is already in store', async () => {
87
+ const { getEntityRecord } = registry.resolveSelect( coreDataStore );
88
+ const post = createTestPost( 1 );
89
+ triggerFetch.mockResolvedValue( {
90
+ async json() {
91
+ return post;
92
+ },
93
+ } );
94
+
95
+ // Resolve the record.
96
+ await expect(
97
+ getEntityRecord( 'postType', 'post', post.id, { context: 'edit' } )
98
+ ).resolves.toEqual( post );
99
+
100
+ triggerFetch.mockReset();
101
+
102
+ await expect(
103
+ getEntityRecord( 'postType', 'post', post.id, {
104
+ context: 'edit',
105
+ _fields: [ 'id', 'author', 'title' ],
106
+ } )
107
+ ).resolves.toEqual( {
108
+ id: post.id,
109
+ author: post.author,
110
+ title: post.title,
111
+ } );
112
+ expect( triggerFetch ).not.toHaveBeenCalled();
113
+ } );
114
+ } );