@wordpress/core-data 7.48.0 → 7.48.2-next.v.202606191442.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.
- package/CHANGELOG.md +7 -1
- package/build/actions.cjs +1 -7
- package/build/actions.cjs.map +3 -3
- package/build/awareness/block-lookup.cjs +14 -26
- package/build/awareness/block-lookup.cjs.map +2 -2
- package/build/awareness/post-editor-awareness.cjs +4 -3
- package/build/awareness/post-editor-awareness.cjs.map +2 -2
- package/build/entities.cjs +6 -3
- package/build/entities.cjs.map +2 -2
- package/build/entity-types/helpers.cjs.map +1 -1
- package/build/hooks/use-entity-record.cjs +21 -19
- package/build/hooks/use-entity-record.cjs.map +3 -3
- package/build/hooks/use-entity-records.cjs +22 -20
- package/build/hooks/use-entity-records.cjs.map +3 -3
- package/build/hooks/use-post-editor-awareness-state.cjs +8 -2
- package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
- package/build/hooks/use-query-select.cjs +2 -20
- package/build/hooks/use-query-select.cjs.map +2 -2
- package/build/hooks/utils.cjs +53 -0
- package/build/hooks/utils.cjs.map +7 -0
- package/build/private-actions.cjs +8 -0
- package/build/private-actions.cjs.map +2 -2
- package/build/private-selectors.cjs.map +2 -2
- package/build/reducer.cjs +23 -7
- package/build/reducer.cjs.map +2 -2
- package/build/resolvers.cjs +13 -8
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs +7 -0
- package/build/selectors.cjs.map +2 -2
- package/build/types.cjs.map +1 -1
- package/build/utils/clear-unchanged-edits.cjs +51 -0
- package/build/utils/clear-unchanged-edits.cjs.map +7 -0
- package/build/utils/crdt-blocks.cjs +12 -2
- package/build/utils/crdt-blocks.cjs.map +2 -2
- package/build/utils/crdt-user-selections.cjs.map +1 -1
- package/build/utils/crdt-utils.cjs.map +1 -1
- package/build/utils/crdt.cjs +2 -1
- package/build/utils/crdt.cjs.map +2 -2
- package/build/utils/index.cjs +6 -0
- package/build/utils/index.cjs.map +2 -2
- package/build/utils/save-crdt-doc.cjs +75 -0
- package/build/utils/save-crdt-doc.cjs.map +7 -0
- package/build/utils/set-nested-value.cjs.map +1 -1
- package/build-module/actions.mjs +2 -8
- package/build-module/actions.mjs.map +2 -2
- package/build-module/awareness/block-lookup.mjs +13 -26
- package/build-module/awareness/block-lookup.mjs.map +2 -2
- package/build-module/awareness/post-editor-awareness.mjs +4 -3
- package/build-module/awareness/post-editor-awareness.mjs.map +2 -2
- package/build-module/entities.mjs +6 -3
- package/build-module/entities.mjs.map +2 -2
- package/build-module/hooks/use-entity-record.mjs +21 -19
- package/build-module/hooks/use-entity-record.mjs.map +2 -2
- package/build-module/hooks/use-entity-records.mjs +20 -18
- package/build-module/hooks/use-entity-records.mjs.map +2 -2
- package/build-module/hooks/use-post-editor-awareness-state.mjs +9 -3
- package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
- package/build-module/hooks/use-query-select.mjs +2 -20
- package/build-module/hooks/use-query-select.mjs.map +2 -2
- package/build-module/hooks/utils.mjs +28 -0
- package/build-module/hooks/utils.mjs.map +7 -0
- package/build-module/private-actions.mjs +7 -0
- package/build-module/private-actions.mjs.map +2 -2
- package/build-module/private-selectors.mjs.map +2 -2
- package/build-module/reducer.mjs +23 -8
- package/build-module/reducer.mjs.map +2 -2
- package/build-module/resolvers.mjs +15 -9
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs +7 -0
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/utils/clear-unchanged-edits.mjs +20 -0
- package/build-module/utils/clear-unchanged-edits.mjs.map +7 -0
- package/build-module/utils/crdt-blocks.mjs +12 -2
- package/build-module/utils/crdt-blocks.mjs.map +2 -2
- package/build-module/utils/crdt-user-selections.mjs.map +1 -1
- package/build-module/utils/crdt-utils.mjs.map +1 -1
- package/build-module/utils/crdt.mjs +2 -1
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-module/utils/index.mjs +24 -20
- package/build-module/utils/index.mjs.map +2 -2
- package/build-module/utils/save-crdt-doc.mjs +40 -0
- package/build-module/utils/save-crdt-doc.mjs.map +7 -0
- package/build-module/utils/set-nested-value.mjs.map +1 -1
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/awareness/block-lookup.d.ts +27 -7
- package/build-types/awareness/block-lookup.d.ts.map +1 -1
- package/build-types/awareness/post-editor-awareness.d.ts +3 -1
- package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/hooks/use-entity-record.d.ts +4 -0
- package/build-types/hooks/use-entity-record.d.ts.map +1 -1
- package/build-types/hooks/use-entity-records.d.ts +5 -1
- package/build-types/hooks/use-entity-records.d.ts.map +1 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
- package/build-types/hooks/utils.d.ts +22 -0
- package/build-types/hooks/utils.d.ts.map +1 -0
- package/build-types/index.d.ts +8 -8
- package/build-types/private-actions.d.ts +15 -0
- package/build-types/private-actions.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +0 -12
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +15 -0
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +12 -8
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/clear-unchanged-edits.d.ts +12 -0
- package/build-types/utils/clear-unchanged-edits.d.ts.map +1 -0
- package/build-types/utils/crdt-blocks.d.ts +5 -1
- package/build-types/utils/crdt-blocks.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/build-types/utils/index.d.ts +2 -0
- package/build-types/utils/index.d.ts.map +1 -1
- package/build-types/utils/save-crdt-doc.d.ts +8 -0
- package/build-types/utils/save-crdt-doc.d.ts.map +1 -0
- package/package.json +27 -20
- package/src/actions.js +2 -10
- package/src/awareness/block-lookup.ts +21 -62
- package/src/awareness/post-editor-awareness.ts +8 -3
- package/src/awareness/test/block-lookup.ts +98 -94
- package/src/awareness/test/post-editor-awareness.ts +177 -180
- package/src/entities.js +14 -3
- package/src/hooks/test/use-entity-record.js +5 -1
- package/src/hooks/test/use-post-editor-awareness-state.ts +10 -2
- package/src/hooks/use-entity-record.ts +26 -19
- package/src/hooks/use-entity-records.ts +26 -18
- package/src/hooks/use-post-editor-awareness-state.ts +20 -7
- package/src/hooks/use-query-select.ts +2 -23
- package/src/hooks/utils.ts +40 -0
- package/src/private-actions.js +18 -0
- package/src/private-selectors.ts +0 -12
- package/src/reducer.js +30 -9
- package/src/resolvers.js +20 -13
- package/src/selectors.ts +11 -0
- package/src/test/entities.js +51 -0
- package/src/test/private-selectors.js +66 -0
- package/src/test/reducer.js +44 -0
- package/src/test/resolvers.js +121 -113
- package/src/test/selectors.js +48 -0
- package/src/utils/clear-unchanged-edits.ts +34 -0
- package/src/utils/crdt-blocks.ts +27 -22
- package/src/utils/crdt.ts +2 -1
- package/src/utils/index.js +2 -0
- package/src/utils/save-crdt-doc.js +64 -0
- package/src/utils/test/clear-unchanged-edits.js +42 -0
- package/src/utils/test/crdt-blocks.ts +57 -2
- package/src/utils/test/rtc-rich-text-cursor-scope.test.js +2 -2
- package/src/utils/test/save-crdt-doc.js +185 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { getSyncManager } from '../sync';
|
|
10
|
+
|
|
11
|
+
const SYNC_SAVE_API_PATH = '/wp-sync/v1/save';
|
|
12
|
+
const saveCRDTDocQueues = new Map();
|
|
13
|
+
|
|
14
|
+
async function serializeAndSaveCRDTDoc( objectType, objectId, room ) {
|
|
15
|
+
const serializedDoc = await getSyncManager()?.createPersistedCRDTDoc(
|
|
16
|
+
objectType,
|
|
17
|
+
objectId
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if ( ! serializedDoc ) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
await apiFetch( {
|
|
25
|
+
path: SYNC_SAVE_API_PATH,
|
|
26
|
+
method: 'POST',
|
|
27
|
+
data: {
|
|
28
|
+
room,
|
|
29
|
+
doc: serializedDoc,
|
|
30
|
+
},
|
|
31
|
+
} );
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Persist the current CRDT document through the sync /save endpoint.
|
|
36
|
+
*
|
|
37
|
+
* @param {import('@wordpress/sync').ObjectType} objectType Object type.
|
|
38
|
+
* @param {import('@wordpress/sync').ObjectID} objectId Object ID.
|
|
39
|
+
*/
|
|
40
|
+
export async function saveCRDTDoc( objectType, objectId ) {
|
|
41
|
+
const room = `${ objectType }:${ objectId }`;
|
|
42
|
+
|
|
43
|
+
// Saves are chained per-room, which forms a queue.
|
|
44
|
+
// Without a queue, two /save calls might fire close together with a risk
|
|
45
|
+
// that the older serialized CRDT snapshot completes after the newer one and
|
|
46
|
+
// overwrites it with stale data.
|
|
47
|
+
// Wait for the prior request chain to complete before firing the next save.
|
|
48
|
+
const previousSave = saveCRDTDocQueues.get( room ) || Promise.resolve();
|
|
49
|
+
|
|
50
|
+
const currentSave = previousSave
|
|
51
|
+
// A failed save should reject its caller, but not block later saves.
|
|
52
|
+
.catch( () => {} )
|
|
53
|
+
.then( () => serializeAndSaveCRDTDoc( objectType, objectId, room ) );
|
|
54
|
+
|
|
55
|
+
saveCRDTDocQueues.set( room, currentSave );
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await currentSave;
|
|
59
|
+
} finally {
|
|
60
|
+
if ( saveCRDTDocQueues.get( room ) === currentSave ) {
|
|
61
|
+
saveCRDTDocQueues.delete( room );
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Internal dependencies
|
|
3
|
+
*/
|
|
4
|
+
import clearUnchangedEdits from '../clear-unchanged-edits';
|
|
5
|
+
|
|
6
|
+
describe( 'clearUnchangedEdits', () => {
|
|
7
|
+
it( 'sets edits matching the persisted record to undefined', () => {
|
|
8
|
+
const result = clearUnchangedEdits(
|
|
9
|
+
{ title: 'Hello', status: 'draft' },
|
|
10
|
+
{ title: 'Hello', status: 'publish' }
|
|
11
|
+
);
|
|
12
|
+
|
|
13
|
+
expect( result ).toEqual( { title: undefined, status: 'draft' } );
|
|
14
|
+
} );
|
|
15
|
+
|
|
16
|
+
it( 'unwraps the persisted value from its `raw` subfield', () => {
|
|
17
|
+
const result = clearUnchangedEdits(
|
|
18
|
+
{ title: 'Hello', content: 'World' },
|
|
19
|
+
{
|
|
20
|
+
title: { raw: 'Hello', rendered: '<p>Hello</p>' },
|
|
21
|
+
content: { raw: 'Changed', rendered: '<p>Changed</p>' },
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
expect( result ).toEqual( { title: undefined, content: 'World' } );
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
it( 'compares deeply for object values', () => {
|
|
29
|
+
const result = clearUnchangedEdits(
|
|
30
|
+
{ meta: { a: 1 }, other: { b: 1 } },
|
|
31
|
+
{ meta: { a: 1 }, other: { b: 2 } }
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
expect( result ).toEqual( { meta: undefined, other: { b: 1 } } );
|
|
35
|
+
} );
|
|
36
|
+
|
|
37
|
+
it( 'keeps all edits when there is no persisted record', () => {
|
|
38
|
+
const result = clearUnchangedEdits( { title: 'Hello' }, undefined );
|
|
39
|
+
|
|
40
|
+
expect( result ).toEqual( { title: 'Hello' } );
|
|
41
|
+
} );
|
|
42
|
+
} );
|
|
@@ -196,7 +196,40 @@ describe( 'crdt-blocks', () => {
|
|
|
196
196
|
expect( content.toString() ).toBe( 'Updated content' );
|
|
197
197
|
} );
|
|
198
198
|
|
|
199
|
-
it( '
|
|
199
|
+
it( 'updates the clientId when an updated block arrives with a different clientId', () => {
|
|
200
|
+
const initialBlocks: Block[] = [
|
|
201
|
+
{
|
|
202
|
+
name: 'core/paragraph',
|
|
203
|
+
attributes: { content: 'Initial content' },
|
|
204
|
+
innerBlocks: [],
|
|
205
|
+
clientId: 'initial-id',
|
|
206
|
+
},
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
mergeCrdtBlocks( yblocks, initialBlocks, null );
|
|
210
|
+
expect( yblocks.get( 0 ).get( 'clientId' ) ).toBe( 'initial-id' );
|
|
211
|
+
|
|
212
|
+
const updatedBlocks: Block[] = [
|
|
213
|
+
{
|
|
214
|
+
name: 'core/paragraph',
|
|
215
|
+
attributes: { content: 'Updated content' },
|
|
216
|
+
innerBlocks: [],
|
|
217
|
+
clientId: 'updated-id',
|
|
218
|
+
},
|
|
219
|
+
];
|
|
220
|
+
|
|
221
|
+
mergeCrdtBlocks( yblocks, updatedBlocks, null );
|
|
222
|
+
|
|
223
|
+
expect( yblocks.length ).toBe( 1 );
|
|
224
|
+
const block = yblocks.get( 0 );
|
|
225
|
+
expect( block.get( 'clientId' ) ).toBe( 'updated-id' );
|
|
226
|
+
const content = (
|
|
227
|
+
block.get( 'attributes' ) as YBlockAttributes
|
|
228
|
+
).get( 'content' ) as Y.Text;
|
|
229
|
+
expect( content.toString() ).toBe( 'Updated content' );
|
|
230
|
+
} );
|
|
231
|
+
|
|
232
|
+
it( 'preserves the local clientId when requested for reparsed content blocks', () => {
|
|
200
233
|
// Simulates the Code Editor flow: the sender re-parses raw HTML on
|
|
201
234
|
// every keystroke, which mints a fresh clientId for every block.
|
|
202
235
|
// The Y.Doc's clientId should stay stable so remote peers don't
|
|
@@ -222,7 +255,9 @@ describe( 'crdt-blocks', () => {
|
|
|
222
255
|
},
|
|
223
256
|
];
|
|
224
257
|
|
|
225
|
-
mergeCrdtBlocks( yblocks, reparsedBlocks, null
|
|
258
|
+
mergeCrdtBlocks( yblocks, reparsedBlocks, null, {
|
|
259
|
+
preserveClientIds: true,
|
|
260
|
+
} );
|
|
226
261
|
|
|
227
262
|
expect( yblocks.length ).toBe( 1 );
|
|
228
263
|
const block = yblocks.get( 0 );
|
|
@@ -2867,6 +2902,26 @@ describe( 'crdt-blocks', () => {
|
|
|
2867
2902
|
} );
|
|
2868
2903
|
} );
|
|
2869
2904
|
|
|
2905
|
+
describe( 'mergeRichTextUpdate - rapid typing', () => {
|
|
2906
|
+
it( 'appends repeated text one character at a time with cursor hints', () => {
|
|
2907
|
+
const text =
|
|
2908
|
+
'987654321098765432109876543210987654321098765432109876543210';
|
|
2909
|
+
const yText = doc.getText( 'test' );
|
|
2910
|
+
yText.insert( 0, 'p1' );
|
|
2911
|
+
|
|
2912
|
+
for ( let i = 1; i <= text.length; i++ ) {
|
|
2913
|
+
const value = `p1${ text.slice( 0, i ) }`;
|
|
2914
|
+
mergeRichTextUpdate(
|
|
2915
|
+
yText,
|
|
2916
|
+
value,
|
|
2917
|
+
asHtmlStringIndex( value.length )
|
|
2918
|
+
);
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2921
|
+
expect( yText.toString() ).toBe( `p1${ text }` );
|
|
2922
|
+
} );
|
|
2923
|
+
} );
|
|
2924
|
+
|
|
2870
2925
|
describe( 'supplementary plane characters (non-emoji)', () => {
|
|
2871
2926
|
// Characters above U+FFFF are stored as surrogate pairs in UTF-16,
|
|
2872
2927
|
// so .length === 2 per character. The diff library v8 counts them
|
|
@@ -254,8 +254,8 @@ describe( 'RTC rich-text cursor scope bug', () => {
|
|
|
254
254
|
},
|
|
255
255
|
'LOCAL_EDITOR_ORIGIN'
|
|
256
256
|
);
|
|
257
|
-
//
|
|
258
|
-
//
|
|
257
|
+
// Selection history writes are deferred. Wait one tick before
|
|
258
|
+
// inspecting the document.
|
|
259
259
|
await waitForNextTick();
|
|
260
260
|
}
|
|
261
261
|
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WordPress dependencies
|
|
3
|
+
*/
|
|
4
|
+
import apiFetch from '@wordpress/api-fetch';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Internal dependencies
|
|
8
|
+
*/
|
|
9
|
+
import { getSyncManager } from '../../sync';
|
|
10
|
+
import { saveCRDTDoc } from '../save-crdt-doc';
|
|
11
|
+
|
|
12
|
+
jest.mock( '@wordpress/api-fetch' );
|
|
13
|
+
jest.mock( '../../sync', () => ( {
|
|
14
|
+
getSyncManager: jest.fn(),
|
|
15
|
+
} ) );
|
|
16
|
+
|
|
17
|
+
function createDeferred() {
|
|
18
|
+
let resolve;
|
|
19
|
+
let reject;
|
|
20
|
+
const promise = new Promise( ( _resolve, _reject ) => {
|
|
21
|
+
resolve = _resolve;
|
|
22
|
+
reject = _reject;
|
|
23
|
+
} );
|
|
24
|
+
|
|
25
|
+
return { promise, resolve, reject };
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function flushPromises() {
|
|
29
|
+
await new Promise( ( resolve ) => setTimeout( resolve, 0 ) );
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe( 'saveCRDTDoc', () => {
|
|
33
|
+
let syncManager;
|
|
34
|
+
|
|
35
|
+
beforeEach( () => {
|
|
36
|
+
apiFetch.mockReset();
|
|
37
|
+
syncManager = {
|
|
38
|
+
createPersistedCRDTDoc: jest.fn(),
|
|
39
|
+
};
|
|
40
|
+
getSyncManager.mockReturnValue( syncManager );
|
|
41
|
+
} );
|
|
42
|
+
|
|
43
|
+
it( 'saves the serialized CRDT document through the sync endpoint', async () => {
|
|
44
|
+
const fetch = createDeferred();
|
|
45
|
+
syncManager.createPersistedCRDTDoc.mockResolvedValue( 'doc' );
|
|
46
|
+
apiFetch.mockImplementation( () => fetch.promise );
|
|
47
|
+
|
|
48
|
+
const save = saveCRDTDoc( 'postType/post', 1 );
|
|
49
|
+
|
|
50
|
+
await flushPromises();
|
|
51
|
+
|
|
52
|
+
fetch.resolve( {} );
|
|
53
|
+
await save;
|
|
54
|
+
|
|
55
|
+
expect( apiFetch ).toHaveBeenCalledWith( {
|
|
56
|
+
path: '/wp-sync/v1/save',
|
|
57
|
+
method: 'POST',
|
|
58
|
+
data: {
|
|
59
|
+
room: 'postType/post:1',
|
|
60
|
+
doc: 'doc',
|
|
61
|
+
},
|
|
62
|
+
} );
|
|
63
|
+
} );
|
|
64
|
+
|
|
65
|
+
it( 'does not call the sync endpoint when there is no serialized CRDT document', async () => {
|
|
66
|
+
syncManager.createPersistedCRDTDoc.mockResolvedValue( null );
|
|
67
|
+
|
|
68
|
+
await saveCRDTDoc( 'postType/post', 1 );
|
|
69
|
+
|
|
70
|
+
expect( apiFetch ).not.toHaveBeenCalled();
|
|
71
|
+
} );
|
|
72
|
+
|
|
73
|
+
it( 'serializes save requests for the same room', async () => {
|
|
74
|
+
const firstFetch = createDeferred();
|
|
75
|
+
syncManager.createPersistedCRDTDoc
|
|
76
|
+
.mockResolvedValueOnce( 'doc-1' )
|
|
77
|
+
.mockResolvedValueOnce( 'doc-2' );
|
|
78
|
+
apiFetch
|
|
79
|
+
.mockImplementationOnce( () => firstFetch.promise )
|
|
80
|
+
.mockResolvedValueOnce( {} );
|
|
81
|
+
|
|
82
|
+
const firstSave = saveCRDTDoc( 'postType/post', 1 );
|
|
83
|
+
const secondSave = saveCRDTDoc( 'postType/post', 1 );
|
|
84
|
+
|
|
85
|
+
await flushPromises();
|
|
86
|
+
|
|
87
|
+
expect( syncManager.createPersistedCRDTDoc ).toHaveBeenCalledTimes( 1 );
|
|
88
|
+
expect( apiFetch ).toHaveBeenCalledTimes( 1 );
|
|
89
|
+
expect( apiFetch ).toHaveBeenLastCalledWith( {
|
|
90
|
+
path: '/wp-sync/v1/save',
|
|
91
|
+
method: 'POST',
|
|
92
|
+
data: {
|
|
93
|
+
room: 'postType/post:1',
|
|
94
|
+
doc: 'doc-1',
|
|
95
|
+
},
|
|
96
|
+
} );
|
|
97
|
+
|
|
98
|
+
firstFetch.resolve( {} );
|
|
99
|
+
await firstSave;
|
|
100
|
+
await flushPromises();
|
|
101
|
+
|
|
102
|
+
expect( syncManager.createPersistedCRDTDoc ).toHaveBeenCalledTimes( 2 );
|
|
103
|
+
expect( apiFetch ).toHaveBeenCalledTimes( 2 );
|
|
104
|
+
expect( apiFetch ).toHaveBeenLastCalledWith( {
|
|
105
|
+
path: '/wp-sync/v1/save',
|
|
106
|
+
method: 'POST',
|
|
107
|
+
data: {
|
|
108
|
+
room: 'postType/post:1',
|
|
109
|
+
doc: 'doc-2',
|
|
110
|
+
},
|
|
111
|
+
} );
|
|
112
|
+
|
|
113
|
+
await secondSave;
|
|
114
|
+
} );
|
|
115
|
+
|
|
116
|
+
it( 'does not serialize save requests for different rooms', async () => {
|
|
117
|
+
const firstFetch = createDeferred();
|
|
118
|
+
syncManager.createPersistedCRDTDoc.mockImplementation(
|
|
119
|
+
( objectType, objectId ) => Promise.resolve( `doc-${ objectId }` )
|
|
120
|
+
);
|
|
121
|
+
apiFetch
|
|
122
|
+
.mockImplementationOnce( () => firstFetch.promise )
|
|
123
|
+
.mockResolvedValueOnce( {} );
|
|
124
|
+
|
|
125
|
+
const firstSave = saveCRDTDoc( 'postType/post', 1 );
|
|
126
|
+
const secondSave = saveCRDTDoc( 'postType/post', 2 );
|
|
127
|
+
|
|
128
|
+
await flushPromises();
|
|
129
|
+
|
|
130
|
+
expect( syncManager.createPersistedCRDTDoc ).toHaveBeenCalledTimes( 2 );
|
|
131
|
+
expect( apiFetch ).toHaveBeenCalledTimes( 2 );
|
|
132
|
+
expect( apiFetch ).toHaveBeenNthCalledWith( 1, {
|
|
133
|
+
path: '/wp-sync/v1/save',
|
|
134
|
+
method: 'POST',
|
|
135
|
+
data: {
|
|
136
|
+
room: 'postType/post:1',
|
|
137
|
+
doc: 'doc-1',
|
|
138
|
+
},
|
|
139
|
+
} );
|
|
140
|
+
expect( apiFetch ).toHaveBeenNthCalledWith( 2, {
|
|
141
|
+
path: '/wp-sync/v1/save',
|
|
142
|
+
method: 'POST',
|
|
143
|
+
data: {
|
|
144
|
+
room: 'postType/post:2',
|
|
145
|
+
doc: 'doc-2',
|
|
146
|
+
},
|
|
147
|
+
} );
|
|
148
|
+
|
|
149
|
+
await secondSave;
|
|
150
|
+
firstFetch.resolve( {} );
|
|
151
|
+
await firstSave;
|
|
152
|
+
} );
|
|
153
|
+
|
|
154
|
+
it( 'continues a same-room queue after a failed save', async () => {
|
|
155
|
+
const firstFetch = createDeferred();
|
|
156
|
+
syncManager.createPersistedCRDTDoc
|
|
157
|
+
.mockResolvedValueOnce( 'doc-1' )
|
|
158
|
+
.mockResolvedValueOnce( 'doc-2' );
|
|
159
|
+
apiFetch
|
|
160
|
+
.mockImplementationOnce( () => firstFetch.promise )
|
|
161
|
+
.mockResolvedValueOnce( {} );
|
|
162
|
+
|
|
163
|
+
const firstSave = saveCRDTDoc( 'postType/post', 1 );
|
|
164
|
+
const secondSave = saveCRDTDoc( 'postType/post', 1 );
|
|
165
|
+
|
|
166
|
+
await flushPromises();
|
|
167
|
+
|
|
168
|
+
firstFetch.reject( new Error( 'save failed' ) );
|
|
169
|
+
await expect( firstSave ).rejects.toThrow( 'save failed' );
|
|
170
|
+
await flushPromises();
|
|
171
|
+
|
|
172
|
+
expect( syncManager.createPersistedCRDTDoc ).toHaveBeenCalledTimes( 2 );
|
|
173
|
+
expect( apiFetch ).toHaveBeenCalledTimes( 2 );
|
|
174
|
+
expect( apiFetch ).toHaveBeenLastCalledWith( {
|
|
175
|
+
path: '/wp-sync/v1/save',
|
|
176
|
+
method: 'POST',
|
|
177
|
+
data: {
|
|
178
|
+
room: 'postType/post:1',
|
|
179
|
+
doc: 'doc-2',
|
|
180
|
+
},
|
|
181
|
+
} );
|
|
182
|
+
|
|
183
|
+
await secondSave;
|
|
184
|
+
} );
|
|
185
|
+
} );
|