@wordpress/core-data 7.40.2-next.v.202602271551.0 → 7.40.2

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 (182) hide show
  1. package/README.md +0 -27
  2. package/build/actions.cjs +9 -26
  3. package/build/actions.cjs.map +2 -2
  4. package/build/awareness/post-editor-awareness.cjs +45 -6
  5. package/build/awareness/post-editor-awareness.cjs.map +2 -2
  6. package/build/awareness/types.cjs.map +1 -1
  7. package/build/entities.cjs +36 -16
  8. package/build/entities.cjs.map +2 -2
  9. package/build/entity-provider.cjs +15 -6
  10. package/build/entity-provider.cjs.map +2 -2
  11. package/build/hooks/use-entity-prop.cjs +33 -2
  12. package/build/hooks/use-entity-prop.cjs.map +2 -2
  13. package/build/hooks/use-post-editor-awareness-state.cjs +120 -1
  14. package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
  15. package/build/index.cjs +0 -3
  16. package/build/index.cjs.map +2 -2
  17. package/build/private-actions.cjs +27 -3
  18. package/build/private-actions.cjs.map +2 -2
  19. package/build/private-apis.cjs +14 -5
  20. package/build/private-apis.cjs.map +3 -3
  21. package/build/private-selectors.cjs +22 -2
  22. package/build/private-selectors.cjs.map +2 -2
  23. package/build/queried-data/actions.cjs +1 -1
  24. package/build/queried-data/actions.cjs.map +2 -2
  25. package/build/queried-data/reducer.cjs +19 -13
  26. package/build/queried-data/reducer.cjs.map +2 -2
  27. package/build/reducer.cjs +19 -2
  28. package/build/reducer.cjs.map +2 -2
  29. package/build/resolvers.cjs +24 -17
  30. package/build/resolvers.cjs.map +2 -2
  31. package/build/selectors.cjs +0 -15
  32. package/build/selectors.cjs.map +2 -2
  33. package/build/sync.cjs +12 -3
  34. package/build/sync.cjs.map +2 -2
  35. package/build/types.cjs.map +1 -1
  36. package/build/utils/block-selection-history.cjs +1 -1
  37. package/build/utils/block-selection-history.cjs.map +2 -2
  38. package/build/utils/crdt-blocks.cjs +85 -28
  39. package/build/utils/crdt-blocks.cjs.map +2 -2
  40. package/build/utils/crdt-selection.cjs +4 -1
  41. package/build/utils/crdt-selection.cjs.map +2 -2
  42. package/build/utils/crdt-text.cjs +52 -0
  43. package/build/utils/crdt-text.cjs.map +7 -0
  44. package/build/utils/crdt-user-selections.cjs +16 -6
  45. package/build/utils/crdt-user-selections.cjs.map +3 -3
  46. package/build/utils/crdt-utils.cjs +54 -2
  47. package/build/utils/crdt-utils.cjs.map +2 -2
  48. package/build/utils/crdt.cjs +26 -28
  49. package/build/utils/crdt.cjs.map +2 -2
  50. package/build-module/actions.mjs +14 -26
  51. package/build-module/actions.mjs.map +2 -2
  52. package/build-module/awareness/post-editor-awareness.mjs +47 -7
  53. package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
  54. package/build-module/entities.mjs +37 -16
  55. package/build-module/entities.mjs.map +2 -2
  56. package/build-module/entity-provider.mjs +15 -6
  57. package/build-module/entity-provider.mjs.map +2 -2
  58. package/build-module/hooks/use-entity-prop.mjs +34 -3
  59. package/build-module/hooks/use-entity-prop.mjs.map +2 -2
  60. package/build-module/hooks/use-post-editor-awareness-state.mjs +117 -1
  61. package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
  62. package/build-module/index.mjs +0 -2
  63. package/build-module/index.mjs.map +2 -2
  64. package/build-module/private-actions.mjs +24 -2
  65. package/build-module/private-actions.mjs.map +2 -2
  66. package/build-module/private-apis.mjs +21 -6
  67. package/build-module/private-apis.mjs.map +3 -3
  68. package/build-module/private-selectors.mjs +19 -1
  69. package/build-module/private-selectors.mjs.map +2 -2
  70. package/build-module/queried-data/actions.mjs +1 -1
  71. package/build-module/queried-data/actions.mjs.map +2 -2
  72. package/build-module/queried-data/reducer.mjs +19 -13
  73. package/build-module/queried-data/reducer.mjs.map +2 -2
  74. package/build-module/reducer.mjs +18 -2
  75. package/build-module/reducer.mjs.map +2 -2
  76. package/build-module/resolvers.mjs +24 -17
  77. package/build-module/resolvers.mjs.map +2 -2
  78. package/build-module/selectors.mjs +0 -14
  79. package/build-module/selectors.mjs.map +2 -2
  80. package/build-module/sync.mjs +8 -2
  81. package/build-module/sync.mjs.map +2 -2
  82. package/build-module/utils/block-selection-history.mjs +5 -2
  83. package/build-module/utils/block-selection-history.mjs.map +2 -2
  84. package/build-module/utils/crdt-blocks.mjs +84 -28
  85. package/build-module/utils/crdt-blocks.mjs.map +2 -2
  86. package/build-module/utils/crdt-selection.mjs +8 -2
  87. package/build-module/utils/crdt-selection.mjs.map +2 -2
  88. package/build-module/utils/crdt-text.mjs +26 -0
  89. package/build-module/utils/crdt-text.mjs.map +7 -0
  90. package/build-module/utils/crdt-user-selections.mjs +16 -7
  91. package/build-module/utils/crdt-user-selections.mjs.map +3 -3
  92. package/build-module/utils/crdt-utils.mjs +51 -1
  93. package/build-module/utils/crdt-utils.mjs.map +2 -2
  94. package/build-module/utils/crdt.mjs +26 -28
  95. package/build-module/utils/crdt.mjs.map +2 -2
  96. package/build-types/actions.d.ts +0 -11
  97. package/build-types/actions.d.ts.map +1 -1
  98. package/build-types/awareness/post-editor-awareness.d.ts +2 -2
  99. package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
  100. package/build-types/awareness/types.d.ts +6 -1
  101. package/build-types/awareness/types.d.ts.map +1 -1
  102. package/build-types/entities.d.ts +20 -1
  103. package/build-types/entities.d.ts.map +1 -1
  104. package/build-types/entity-provider.d.ts +11 -6
  105. package/build-types/entity-provider.d.ts.map +1 -1
  106. package/build-types/hooks/use-entity-prop.d.ts.map +1 -1
  107. package/build-types/hooks/use-post-editor-awareness-state.d.ts +40 -7
  108. package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
  109. package/build-types/index.d.ts +0 -3
  110. package/build-types/index.d.ts.map +1 -1
  111. package/build-types/parsed-blocks-cache.d.ts +10 -0
  112. package/build-types/parsed-blocks-cache.d.ts.map +1 -0
  113. package/build-types/private-actions.d.ts +12 -0
  114. package/build-types/private-actions.d.ts.map +1 -1
  115. package/build-types/private-apis.d.ts +21 -1
  116. package/build-types/private-apis.d.ts.map +1 -1
  117. package/build-types/private-selectors.d.ts +17 -0
  118. package/build-types/private-selectors.d.ts.map +1 -1
  119. package/build-types/queried-data/reducer.d.ts.map +1 -1
  120. package/build-types/reducer.d.ts +15 -0
  121. package/build-types/reducer.d.ts.map +1 -1
  122. package/build-types/resolvers.d.ts.map +1 -1
  123. package/build-types/selectors.d.ts +1 -9
  124. package/build-types/selectors.d.ts.map +1 -1
  125. package/build-types/sync.d.ts +2 -2
  126. package/build-types/sync.d.ts.map +1 -1
  127. package/build-types/types.d.ts +10 -2
  128. package/build-types/types.d.ts.map +1 -1
  129. package/build-types/utils/block-selection-history.d.ts.map +1 -1
  130. package/build-types/utils/crdt-blocks.d.ts +11 -0
  131. package/build-types/utils/crdt-blocks.d.ts.map +1 -1
  132. package/build-types/utils/crdt-selection.d.ts.map +1 -1
  133. package/build-types/utils/crdt-text.d.ts +16 -0
  134. package/build-types/utils/crdt-text.d.ts.map +1 -0
  135. package/build-types/utils/crdt-user-selections.d.ts +17 -4
  136. package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
  137. package/build-types/utils/crdt-utils.d.ts +20 -0
  138. package/build-types/utils/crdt-utils.d.ts.map +1 -1
  139. package/build-types/utils/crdt.d.ts +13 -7
  140. package/build-types/utils/crdt.d.ts.map +1 -1
  141. package/build-types/utils/normalize-query-for-resolution.d.ts +12 -0
  142. package/build-types/utils/normalize-query-for-resolution.d.ts.map +1 -0
  143. package/build-types/utils/save-crdt-doc.d.ts +8 -0
  144. package/build-types/utils/save-crdt-doc.d.ts.map +1 -0
  145. package/build-types/utils/test/crdt-utils.d.ts +2 -0
  146. package/build-types/utils/test/crdt-utils.d.ts.map +1 -0
  147. package/package.json +18 -18
  148. package/src/actions.js +26 -41
  149. package/src/awareness/post-editor-awareness.ts +106 -7
  150. package/src/awareness/test/post-editor-awareness.ts +50 -10
  151. package/src/awareness/types.ts +7 -1
  152. package/src/entities.js +43 -17
  153. package/src/entity-provider.js +24 -11
  154. package/src/hooks/test/use-post-editor-awareness-state.ts +446 -3
  155. package/src/hooks/use-entity-prop.js +43 -3
  156. package/src/hooks/use-post-editor-awareness-state.ts +229 -7
  157. package/src/index.js +0 -6
  158. package/src/private-actions.js +43 -1
  159. package/src/{private-apis.js → private-apis.ts} +21 -3
  160. package/src/private-selectors.ts +43 -0
  161. package/src/queried-data/actions.js +1 -1
  162. package/src/queried-data/reducer.js +26 -14
  163. package/src/reducer.js +36 -1
  164. package/src/resolvers.js +33 -23
  165. package/src/selectors.ts +1 -32
  166. package/src/sync.ts +6 -0
  167. package/src/test/entities.js +204 -16
  168. package/src/test/private-actions.js +1 -1
  169. package/src/test/resolvers.js +144 -57
  170. package/src/test/store.js +116 -0
  171. package/src/types.ts +20 -2
  172. package/src/utils/block-selection-history.ts +5 -2
  173. package/src/utils/crdt-blocks.ts +159 -56
  174. package/src/utils/crdt-selection.ts +8 -2
  175. package/src/utils/crdt-text.ts +43 -0
  176. package/src/utils/crdt-user-selections.ts +29 -8
  177. package/src/utils/crdt-utils.ts +99 -0
  178. package/src/utils/crdt.ts +50 -40
  179. package/src/utils/test/crdt-blocks.ts +588 -24
  180. package/src/utils/test/crdt-user-selections.ts +5 -0
  181. package/src/utils/test/crdt-utils.ts +387 -0
  182. package/src/utils/test/crdt.ts +291 -54
package/src/sync.ts CHANGED
@@ -12,18 +12,24 @@ import {
12
12
  import { unlock } from './lock-unlock';
13
13
 
14
14
  const {
15
+ ConnectionErrorCode,
15
16
  createSyncManager,
16
17
  Delta,
17
18
  CRDT_DOC_META_PERSISTENCE_KEY,
18
19
  CRDT_RECORD_MAP_KEY,
19
20
  LOCAL_EDITOR_ORIGIN,
21
+ LOCAL_UNDO_IGNORED_ORIGIN,
22
+ retrySyncConnection,
20
23
  } = unlock( syncPrivateApis );
21
24
 
22
25
  export {
26
+ ConnectionErrorCode,
23
27
  Delta,
24
28
  CRDT_DOC_META_PERSISTENCE_KEY,
25
29
  CRDT_RECORD_MAP_KEY,
26
30
  LOCAL_EDITOR_ORIGIN,
31
+ LOCAL_UNDO_IGNORED_ORIGIN,
32
+ retrySyncConnection,
27
33
  };
28
34
 
29
35
  let syncManager: SyncManager;
@@ -8,6 +8,10 @@ jest.mock( '../sync', () => ( {
8
8
  ...jest.requireActual( '../sync' ),
9
9
  getSyncManager: jest.fn(),
10
10
  } ) );
11
+ jest.mock( '../utils/crdt', () => ( {
12
+ ...jest.requireActual( '../utils/crdt' ),
13
+ applyPostChangesToCRDTDoc: jest.fn(),
14
+ } ) );
11
15
 
12
16
  /**
13
17
  * Internal dependencies
@@ -19,7 +23,10 @@ import {
19
23
  additionalEntityConfigLoaders,
20
24
  } from '../entities';
21
25
  import { getSyncManager } from '../sync';
22
- import { POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE } from '../utils/crdt';
26
+ import {
27
+ applyPostChangesToCRDTDoc,
28
+ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE,
29
+ } from '../utils/crdt';
23
30
 
24
31
  describe( 'getMethodName', () => {
25
32
  it( 'should return the right method name for an entity with the root kind', () => {
@@ -51,12 +58,14 @@ describe( 'getMethodName', () => {
51
58
  } );
52
59
 
53
60
  describe( 'prePersistPostType', () => {
54
- it( 'set the status to draft and empty the title when saving auto-draft posts', () => {
61
+ it( 'set the status to draft and empty the title when saving auto-draft posts', async () => {
55
62
  let record = {
56
63
  status: 'auto-draft',
57
64
  };
58
65
  const edits = {};
59
- expect( prePersistPostType( record, edits, 'post', false ) ).toEqual( {
66
+ expect(
67
+ await prePersistPostType( record, edits, 'post', false )
68
+ ).toEqual( {
60
69
  status: 'draft',
61
70
  title: '',
62
71
  } );
@@ -64,15 +73,17 @@ describe( 'prePersistPostType', () => {
64
73
  record = {
65
74
  status: 'publish',
66
75
  };
67
- expect( prePersistPostType( record, edits, 'post', false ) ).toEqual(
68
- {}
69
- );
76
+ expect(
77
+ await prePersistPostType( record, edits, 'post', false )
78
+ ).toEqual( {} );
70
79
 
71
80
  record = {
72
81
  status: 'auto-draft',
73
82
  title: 'Auto Draft',
74
83
  };
75
- expect( prePersistPostType( record, edits, 'post', false ) ).toEqual( {
84
+ expect(
85
+ await prePersistPostType( record, edits, 'post', false )
86
+ ).toEqual( {
76
87
  status: 'draft',
77
88
  title: '',
78
89
  } );
@@ -81,23 +92,23 @@ describe( 'prePersistPostType', () => {
81
92
  status: 'publish',
82
93
  title: 'My Title',
83
94
  };
84
- expect( prePersistPostType( record, edits, 'post', false ) ).toEqual(
85
- {}
86
- );
95
+ expect(
96
+ await prePersistPostType( record, edits, 'post', false )
97
+ ).toEqual( {} );
87
98
  } );
88
99
 
89
- it( 'does not set the status to draft and empty the title when saving templates', () => {
100
+ it( 'does not set the status to draft and empty the title when saving templates', async () => {
90
101
  const record = {
91
102
  status: 'auto-draft',
92
103
  title: 'Auto Draft',
93
104
  };
94
105
  const edits = {};
95
- expect( prePersistPostType( record, edits, 'post', true ) ).toEqual(
96
- {}
97
- );
106
+ expect(
107
+ await prePersistPostType( record, edits, 'post', true )
108
+ ).toEqual( {} );
98
109
  } );
99
110
 
100
- it( 'adds meta with serialized CRDT doc when createPersistedCRDTDoc returns a value', () => {
111
+ it( 'adds meta with serialized CRDT doc when createPersistedCRDTDoc returns a value', async () => {
101
112
  const mockSerializedDoc = 'serialized-crdt-doc-data';
102
113
  getSyncManager.mockReturnValue( {
103
114
  createPersistedCRDTDoc: jest
@@ -107,7 +118,7 @@ describe( 'prePersistPostType', () => {
107
118
 
108
119
  const record = { id: 123, status: 'publish' };
109
120
  const edits = {};
110
- const result = prePersistPostType( record, edits, 'post', false );
121
+ const result = await prePersistPostType( record, edits, 'post', false );
111
122
 
112
123
  expect( result.meta ).toEqual( {
113
124
  [ POST_META_KEY_FOR_CRDT_DOC_PERSISTENCE ]: mockSerializedDoc,
@@ -123,6 +134,183 @@ describe( 'prePersistPostType', () => {
123
134
  } );
124
135
  } );
125
136
 
137
+ describe( 'loadPostTypeEntities', () => {
138
+ let originalCollaborationEnabled;
139
+
140
+ beforeEach( () => {
141
+ apiFetch.mockReset();
142
+ applyPostChangesToCRDTDoc.mockReset();
143
+ originalCollaborationEnabled = window._wpCollaborationEnabled;
144
+ } );
145
+
146
+ afterEach( () => {
147
+ window._wpCollaborationEnabled = originalCollaborationEnabled;
148
+ } );
149
+
150
+ it( 'should include custom taxonomy rest_bases in synced properties when collaboration is enabled', async () => {
151
+ window._wpCollaborationEnabled = true;
152
+
153
+ const mockPostTypes = {
154
+ book: {
155
+ name: 'Books',
156
+ rest_base: 'books',
157
+ rest_namespace: 'wp/v2',
158
+ taxonomies: [ 'genre', 'audience' ],
159
+ },
160
+ };
161
+ const mockTaxonomies = {
162
+ genre: {
163
+ name: 'Genres',
164
+ rest_base: 'genres',
165
+ rest_namespace: 'wp/v2',
166
+ },
167
+ audience: {
168
+ name: 'Audiences',
169
+ rest_base: 'audiences',
170
+ rest_namespace: 'wp/v2',
171
+ },
172
+ };
173
+
174
+ apiFetch
175
+ .mockResolvedValueOnce( mockPostTypes )
176
+ .mockResolvedValueOnce( mockTaxonomies );
177
+
178
+ const postTypeLoader = additionalEntityConfigLoaders.find(
179
+ ( loader ) => loader.kind === 'postType'
180
+ );
181
+ const entities = await postTypeLoader.loadEntities();
182
+ const bookEntity = entities.find( ( e ) => e.name === 'book' );
183
+
184
+ bookEntity.syncConfig.applyChangesToCRDTDoc( {}, {} );
185
+
186
+ expect( applyPostChangesToCRDTDoc ).toHaveBeenCalledWith(
187
+ {},
188
+ {},
189
+ expect.any( Set )
190
+ );
191
+
192
+ const syncedProperties = applyPostChangesToCRDTDoc.mock.calls[ 0 ][ 2 ];
193
+ expect( syncedProperties ).toContain( 'genres' );
194
+ expect( syncedProperties ).toContain( 'audiences' );
195
+ } );
196
+
197
+ it( 'should not fetch taxonomies when collaboration is disabled', async () => {
198
+ window._wpCollaborationEnabled = false;
199
+
200
+ const mockPostTypes = {
201
+ post: {
202
+ name: 'Posts',
203
+ rest_base: 'posts',
204
+ rest_namespace: 'wp/v2',
205
+ taxonomies: [ 'category', 'post_tag' ],
206
+ },
207
+ };
208
+
209
+ apiFetch.mockResolvedValueOnce( mockPostTypes );
210
+
211
+ const postTypeLoader = additionalEntityConfigLoaders.find(
212
+ ( loader ) => loader.kind === 'postType'
213
+ );
214
+ const entities = await postTypeLoader.loadEntities();
215
+ const postEntity = entities.find( ( e ) => e.name === 'post' );
216
+
217
+ postEntity.syncConfig.applyChangesToCRDTDoc( {}, {} );
218
+
219
+ // Only one apiFetch call (post types), no taxonomy fetch.
220
+ expect( apiFetch ).toHaveBeenCalledTimes( 1 );
221
+
222
+ const syncedProperties = applyPostChangesToCRDTDoc.mock.calls[ 0 ][ 2 ];
223
+ expect( syncedProperties ).not.toContain( 'categories' );
224
+ expect( syncedProperties ).not.toContain( 'tags' );
225
+ } );
226
+
227
+ it( 'should skip taxonomy rest_base when taxonomy is not found in fetched taxonomies', async () => {
228
+ window._wpCollaborationEnabled = true;
229
+
230
+ const mockPostTypes = {
231
+ book: {
232
+ name: 'Books',
233
+ rest_base: 'books',
234
+ rest_namespace: 'wp/v2',
235
+ taxonomies: [ 'genre', 'missing_taxonomy' ],
236
+ },
237
+ };
238
+ const mockTaxonomies = {
239
+ genre: {
240
+ name: 'Genres',
241
+ rest_base: 'genres',
242
+ rest_namespace: 'wp/v2',
243
+ },
244
+ // 'missing_taxonomy' is intentionally absent.
245
+ };
246
+
247
+ apiFetch
248
+ .mockResolvedValueOnce( mockPostTypes )
249
+ .mockResolvedValueOnce( mockTaxonomies );
250
+
251
+ const postTypeLoader = additionalEntityConfigLoaders.find(
252
+ ( loader ) => loader.kind === 'postType'
253
+ );
254
+ const entities = await postTypeLoader.loadEntities();
255
+ const bookEntity = entities.find( ( e ) => e.name === 'book' );
256
+
257
+ bookEntity.syncConfig.applyChangesToCRDTDoc( {}, {} );
258
+
259
+ const syncedProperties = applyPostChangesToCRDTDoc.mock.calls[ 0 ][ 2 ];
260
+ expect( syncedProperties ).toContain( 'genres' );
261
+ // missing_taxonomy has no rest_base entry, so nothing should be added for it.
262
+ expect( syncedProperties.size ).toBe( 16 ); // 15 base + 1 taxonomy (genres)
263
+ } );
264
+
265
+ it( 'should include base synced properties regardless of taxonomies', async () => {
266
+ window._wpCollaborationEnabled = true;
267
+
268
+ const mockPostTypes = {
269
+ page: {
270
+ name: 'Pages',
271
+ rest_base: 'pages',
272
+ rest_namespace: 'wp/v2',
273
+ taxonomies: [],
274
+ },
275
+ };
276
+
277
+ apiFetch
278
+ .mockResolvedValueOnce( mockPostTypes )
279
+ .mockResolvedValueOnce( {} );
280
+
281
+ const postTypeLoader = additionalEntityConfigLoaders.find(
282
+ ( loader ) => loader.kind === 'postType'
283
+ );
284
+ const entities = await postTypeLoader.loadEntities();
285
+ const pageEntity = entities.find( ( e ) => e.name === 'page' );
286
+
287
+ pageEntity.syncConfig.applyChangesToCRDTDoc( {}, {} );
288
+
289
+ const syncedProperties = applyPostChangesToCRDTDoc.mock.calls[ 0 ][ 2 ];
290
+ const expectedBase = [
291
+ 'author',
292
+ 'blocks',
293
+ 'content',
294
+ 'comment_status',
295
+ 'date',
296
+ 'excerpt',
297
+ 'featured_media',
298
+ 'format',
299
+ 'meta',
300
+ 'ping_status',
301
+ 'slug',
302
+ 'status',
303
+ 'sticky',
304
+ 'template',
305
+ 'title',
306
+ ];
307
+ for ( const prop of expectedBase ) {
308
+ expect( syncedProperties ).toContain( prop );
309
+ }
310
+ expect( syncedProperties.size ).toBe( 15 );
311
+ } );
312
+ } );
313
+
126
314
  describe( 'loadTaxonomyEntities', () => {
127
315
  beforeEach( () => {
128
316
  apiFetch.mockReset();
@@ -102,7 +102,7 @@ describe( 'editMediaEntity', () => {
102
102
  expect( dispatch.receiveEntityRecords ).toHaveBeenCalledWith(
103
103
  'postType',
104
104
  'attachment',
105
- [ updatedRecord ],
105
+ updatedRecord,
106
106
  undefined,
107
107
  true,
108
108
  undefined,
@@ -172,16 +172,69 @@ describe( 'getEntityRecord', () => {
172
172
  editRecord: expect.any( Function ),
173
173
  getEditedRecord: expect.any( Function ),
174
174
  onStatusChange: expect.any( Function ),
175
+ persistCRDTDoc: expect.any( Function ),
175
176
  refetchRecord: expect.any( Function ),
176
177
  restoreUndoMeta: expect.any( Function ),
177
- saveRecord: expect.any( Function ),
178
178
  }
179
179
  );
180
180
  } );
181
181
 
182
- it( 'saveRecord fetches edited record and saves full entity record', async () => {
183
- const POST_RECORD = { id: 1, title: 'Test Post' };
184
- const EDITED_RECORD = { id: 1, title: 'Edited Post' };
182
+ it( 'persistCRDTDoc fetches edited record and does not save full entity record when the entity does not support meta', async () => {
183
+ const ENTITY_RECORD = { id: 1, title: 'Test Record' };
184
+ const EDITED_RECORD = { id: 1, title: 'Edited Record' };
185
+ const ENTITY_RESPONSE = {
186
+ json: () => Promise.resolve( ENTITY_RECORD ),
187
+ };
188
+ const ENTITIES_WITH_SYNC = [
189
+ {
190
+ name: 'bar',
191
+ kind: 'foo',
192
+ baseURL: '/wp/v2/foo',
193
+ baseURLParams: { context: 'edit' },
194
+ syncConfig: {},
195
+ },
196
+ ];
197
+
198
+ dispatch.saveEntityRecord = jest.fn();
199
+
200
+ const resolveSelectWithSync = {
201
+ getEntitiesConfig: jest.fn( () => ENTITIES_WITH_SYNC ),
202
+ getEditedEntityRecord: jest.fn( () =>
203
+ Promise.resolve( EDITED_RECORD )
204
+ ),
205
+ };
206
+
207
+ triggerFetch.mockImplementation( () => ENTITY_RESPONSE );
208
+
209
+ await getEntityRecord(
210
+ 'foo',
211
+ 'bar',
212
+ 1
213
+ )( {
214
+ dispatch,
215
+ registry,
216
+ resolveSelect: resolveSelectWithSync,
217
+ } );
218
+
219
+ // Extract the handlers passed to syncManager.load.
220
+ const handlers = syncManager.load.mock.calls[ 0 ][ 4 ];
221
+
222
+ // Call persistCRDTDoc and wait for the internal promise chain.
223
+ handlers.persistCRDTDoc();
224
+ await resolveSelectWithSync.getEditedEntityRecord();
225
+
226
+ // Should have fetched the full edited entity record.
227
+ expect(
228
+ resolveSelectWithSync.getEditedEntityRecord
229
+ ).toHaveBeenCalledWith( 'foo', 'bar', 1 );
230
+
231
+ // Should not have called saveEntityRecord.
232
+ expect( dispatch.saveEntityRecord ).not.toHaveBeenCalled();
233
+ } );
234
+
235
+ it( 'persistCRDTDoc fetches edited record and saves full entity record', async () => {
236
+ const POST_RECORD = { id: 1, title: 'Test Post', meta: {} };
237
+ const EDITED_RECORD = { id: 1, title: 'Edited Post', meta: {} };
185
238
  const POST_RESPONSE = {
186
239
  json: () => Promise.resolve( POST_RECORD ),
187
240
  };
@@ -219,8 +272,8 @@ describe( 'getEntityRecord', () => {
219
272
  // Extract the handlers passed to syncManager.load.
220
273
  const handlers = syncManager.load.mock.calls[ 0 ][ 4 ];
221
274
 
222
- // Call saveRecord and wait for the internal promise chain.
223
- handlers.saveRecord();
275
+ // Call persistCRDTDoc and wait for the internal promise chain.
276
+ handlers.persistCRDTDoc();
224
277
  await resolveSelectWithSync.getEditedEntityRecord();
225
278
 
226
279
  // Should have fetched the full edited entity record.
@@ -236,8 +289,8 @@ describe( 'getEntityRecord', () => {
236
289
  );
237
290
  } );
238
291
 
239
- it( 'saveRecord saves even when there are no unsaved edits', async () => {
240
- const POST_RECORD = { id: 1, title: 'Test Post' };
292
+ it( 'persistCRDTDoc saves even when there are no unsaved edits', async () => {
293
+ const POST_RECORD = { id: 1, title: 'Test Post', meta: {} };
241
294
  const POST_RESPONSE = {
242
295
  json: () => Promise.resolve( POST_RECORD ),
243
296
  };
@@ -275,8 +328,8 @@ describe( 'getEntityRecord', () => {
275
328
 
276
329
  const handlers = syncManager.load.mock.calls[ 0 ][ 4 ];
277
330
 
278
- // Call saveRecord and wait for the internal promise chain.
279
- handlers.saveRecord();
331
+ // Call persistCRDTDoc and wait for the internal promise chain.
332
+ handlers.persistCRDTDoc();
280
333
  await resolveSelectWithSync.getEditedEntityRecord();
281
334
 
282
335
  // Should save the record even with no edits (the whole point of the fix).
@@ -336,9 +389,9 @@ describe( 'getEntityRecord', () => {
336
389
  editRecord: expect.any( Function ),
337
390
  getEditedRecord: expect.any( Function ),
338
391
  onStatusChange: expect.any( Function ),
392
+ persistCRDTDoc: expect.any( Function ),
339
393
  refetchRecord: expect.any( Function ),
340
394
  restoreUndoMeta: expect.any( Function ),
341
- saveRecord: expect.any( Function ),
342
395
  }
343
396
  );
344
397
  } );
@@ -832,8 +885,8 @@ describe( 'canUser', () => {
832
885
  batch: ( callback ) => callback(),
833
886
  };
834
887
  dispatch = Object.assign( jest.fn(), {
835
- receiveUserPermissions: jest.fn(),
836
- finishResolutions: jest.fn(),
888
+ receiveUserPermission: jest.fn(),
889
+ finishResolution: jest.fn(),
837
890
  } );
838
891
  triggerFetch.mockReset();
839
892
  } );
@@ -859,7 +912,7 @@ describe( 'canUser', () => {
859
912
  parse: false,
860
913
  } );
861
914
 
862
- expect( dispatch.receiveUserPermissions ).not.toHaveBeenCalled();
915
+ expect( dispatch.receiveUserPermission ).not.toHaveBeenCalled();
863
916
  } );
864
917
 
865
918
  it( 'throws an error when an entity resource object is malformed', async () => {
@@ -888,8 +941,9 @@ describe( 'canUser', () => {
888
941
  parse: false,
889
942
  } );
890
943
 
891
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
892
- expect.objectContaining( { 'create/media': false } )
944
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
945
+ 'create/media',
946
+ false
893
947
  );
894
948
  } );
895
949
 
@@ -910,8 +964,9 @@ describe( 'canUser', () => {
910
964
  parse: false,
911
965
  } );
912
966
 
913
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
914
- expect.objectContaining( { 'create/postType/attachment': false } )
967
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
968
+ 'create/postType/attachment',
969
+ false
915
970
  );
916
971
  } );
917
972
 
@@ -931,8 +986,9 @@ describe( 'canUser', () => {
931
986
  parse: false,
932
987
  } );
933
988
 
934
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
935
- expect.objectContaining( { 'create/media': true } )
989
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
990
+ 'create/media',
991
+ true
936
992
  );
937
993
  } );
938
994
 
@@ -953,8 +1009,9 @@ describe( 'canUser', () => {
953
1009
  parse: false,
954
1010
  } );
955
1011
 
956
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
957
- expect.objectContaining( { 'create/postType/attachment': true } )
1012
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1013
+ 'create/postType/attachment',
1014
+ true
958
1015
  );
959
1016
  } );
960
1017
 
@@ -975,8 +1032,9 @@ describe( 'canUser', () => {
975
1032
  parse: false,
976
1033
  } );
977
1034
 
978
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
979
- expect.objectContaining( { 'create/blocks/123': true } )
1035
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1036
+ 'create/blocks/123',
1037
+ true
980
1038
  );
981
1039
  } );
982
1040
 
@@ -1001,8 +1059,9 @@ describe( 'canUser', () => {
1001
1059
  parse: false,
1002
1060
  } );
1003
1061
 
1004
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
1005
- expect.objectContaining( { 'create/postType/wp_block/123': true } )
1062
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1063
+ 'create/postType/wp_block/123',
1064
+ true
1006
1065
  );
1007
1066
  } );
1008
1067
 
@@ -1029,11 +1088,13 @@ describe( 'canUser', () => {
1029
1088
 
1030
1089
  expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
1031
1090
 
1032
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
1033
- expect.objectContaining( {
1034
- 'create/blocks': true,
1035
- 'read/blocks': true,
1036
- } )
1091
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1092
+ 'create/blocks',
1093
+ true
1094
+ );
1095
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1096
+ 'read/blocks',
1097
+ true
1037
1098
  );
1038
1099
  } );
1039
1100
 
@@ -1068,11 +1129,13 @@ describe( 'canUser', () => {
1068
1129
 
1069
1130
  expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
1070
1131
 
1071
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
1072
- expect.objectContaining( {
1073
- 'create/postType/wp_block': true,
1074
- 'read/postType/wp_block': true,
1075
- } )
1132
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1133
+ 'create/postType/wp_block',
1134
+ true
1135
+ );
1136
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1137
+ 'read/postType/wp_block',
1138
+ true
1076
1139
  );
1077
1140
  } );
1078
1141
 
@@ -1105,13 +1168,21 @@ describe( 'canUser', () => {
1105
1168
  'blocks'
1106
1169
  )( { dispatch, registry, resolveSelect } );
1107
1170
 
1108
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
1109
- expect.objectContaining( {
1110
- 'create/blocks': true,
1111
- 'read/blocks': true,
1112
- 'update/blocks': false,
1113
- 'delete/blocks': false,
1114
- } )
1171
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1172
+ 'create/blocks',
1173
+ true
1174
+ );
1175
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1176
+ 'read/blocks',
1177
+ true
1178
+ );
1179
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1180
+ 'update/blocks',
1181
+ false
1182
+ );
1183
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1184
+ 'delete/blocks',
1185
+ false
1115
1186
  );
1116
1187
  } );
1117
1188
 
@@ -1150,13 +1221,21 @@ describe( 'canUser', () => {
1150
1221
 
1151
1222
  expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
1152
1223
 
1153
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
1154
- expect.objectContaining( {
1155
- 'create/blocks/123': true,
1156
- 'read/blocks/123': true,
1157
- 'update/blocks/123': true,
1158
- 'delete/blocks/123': true,
1159
- } )
1224
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1225
+ 'create/blocks/123',
1226
+ true
1227
+ );
1228
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1229
+ 'read/blocks/123',
1230
+ true
1231
+ );
1232
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1233
+ 'update/blocks/123',
1234
+ true
1235
+ );
1236
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1237
+ 'delete/blocks/123',
1238
+ true
1160
1239
  );
1161
1240
  } );
1162
1241
 
@@ -1195,13 +1274,21 @@ describe( 'canUser', () => {
1195
1274
 
1196
1275
  expect( triggerFetch ).toHaveBeenCalledTimes( 1 );
1197
1276
 
1198
- expect( dispatch.receiveUserPermissions ).toHaveBeenCalledWith(
1199
- expect.objectContaining( {
1200
- 'create/postType/wp_block/123': true,
1201
- 'read/postType/wp_block/123': true,
1202
- 'update/postType/wp_block/123': true,
1203
- 'delete/postType/wp_block/123': true,
1204
- } )
1277
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1278
+ 'create/postType/wp_block/123',
1279
+ true
1280
+ );
1281
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1282
+ 'read/postType/wp_block/123',
1283
+ true
1284
+ );
1285
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1286
+ 'update/postType/wp_block/123',
1287
+ true
1288
+ );
1289
+ expect( dispatch.receiveUserPermission ).toHaveBeenCalledWith(
1290
+ 'delete/postType/wp_block/123',
1291
+ true
1205
1292
  );
1206
1293
  } );
1207
1294
  } );