@wordpress/core-data 7.40.1 → 7.40.2-next.v.202602241322.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/build/actions.cjs +23 -29
- package/build/actions.cjs.map +2 -2
- package/build/awareness/block-lookup.cjs +103 -0
- package/build/awareness/block-lookup.cjs.map +7 -0
- package/build/awareness/post-editor-awareness.cjs +45 -7
- package/build/awareness/post-editor-awareness.cjs.map +3 -3
- package/build/entities.cjs +60 -63
- package/build/entities.cjs.map +2 -2
- package/build/entity-types/icon.cjs +19 -0
- package/build/entity-types/icon.cjs.map +7 -0
- package/build/entity-types/index.cjs.map +1 -1
- package/build/hooks/use-post-editor-awareness-state.cjs +12 -8
- package/build/hooks/use-post-editor-awareness-state.cjs.map +2 -2
- package/build/private-actions.cjs +0 -8
- package/build/private-actions.cjs.map +2 -2
- package/build/private-apis.cjs +1 -1
- package/build/private-apis.cjs.map +1 -1
- package/build/private-selectors.cjs +1 -9
- package/build/private-selectors.cjs.map +2 -2
- package/build/reducer.cjs +0 -10
- package/build/reducer.cjs.map +2 -2
- package/build/resolvers.cjs +101 -113
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs.map +2 -2
- package/build/sync.cjs +0 -3
- package/build/sync.cjs.map +2 -2
- package/build/types.cjs.map +1 -1
- package/build/utils/crdt-selection.cjs +1 -1
- package/build/utils/crdt-selection.cjs.map +2 -2
- package/build/utils/crdt-user-selections.cjs +78 -22
- package/build/utils/crdt-user-selections.cjs.map +3 -3
- package/build-module/actions.mjs +23 -29
- package/build-module/actions.mjs.map +2 -2
- package/build-module/awareness/block-lookup.mjs +77 -0
- package/build-module/awareness/block-lookup.mjs.map +7 -0
- package/build-module/awareness/post-editor-awareness.mjs +47 -8
- package/build-module/awareness/post-editor-awareness.mjs.map +3 -3
- package/build-module/entities.mjs +60 -63
- package/build-module/entities.mjs.map +2 -2
- package/build-module/entity-types/icon.mjs +1 -0
- package/build-module/entity-types/icon.mjs.map +7 -0
- package/build-module/hooks/use-post-editor-awareness-state.mjs +10 -6
- package/build-module/hooks/use-post-editor-awareness-state.mjs.map +2 -2
- package/build-module/private-actions.mjs +0 -7
- package/build-module/private-actions.mjs.map +2 -2
- package/build-module/private-apis.mjs +2 -2
- package/build-module/private-apis.mjs.map +1 -1
- package/build-module/private-selectors.mjs +2 -12
- package/build-module/private-selectors.mjs.map +2 -2
- package/build-module/reducer.mjs +0 -9
- package/build-module/reducer.mjs.map +2 -2
- package/build-module/resolvers.mjs +101 -112
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/sync.mjs +0 -2
- package/build-module/sync.mjs.map +2 -2
- package/build-module/utils/crdt-selection.mjs +1 -1
- package/build-module/utils/crdt-selection.mjs.map +2 -2
- package/build-module/utils/crdt-user-selections.mjs +77 -22
- package/build-module/utils/crdt-user-selections.mjs.map +2 -2
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/awareness/block-lookup.d.ts +29 -0
- package/build-types/awareness/block-lookup.d.ts.map +1 -0
- package/build-types/awareness/post-editor-awareness.d.ts +18 -5
- package/build-types/awareness/post-editor-awareness.d.ts.map +1 -1
- package/build-types/awareness/test/block-lookup.d.ts +2 -0
- package/build-types/awareness/test/block-lookup.d.ts.map +1 -0
- package/build-types/entities.d.ts +16 -0
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/entity-types/icon.d.ts +25 -0
- package/build-types/entity-types/icon.d.ts.map +1 -0
- package/build-types/entity-types/index.d.ts +3 -2
- package/build-types/entity-types/index.d.ts.map +1 -1
- package/build-types/hooks/use-post-editor-awareness-state.d.ts +11 -6
- package/build-types/hooks/use-post-editor-awareness-state.d.ts.map +1 -1
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-actions.d.ts +0 -8
- package/build-types/private-actions.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +1 -8
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +0 -11
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +0 -3
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +0 -6
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/sync.d.ts +2 -2
- package/build-types/sync.d.ts.map +1 -1
- package/build-types/types.d.ts +13 -5
- package/build-types/types.d.ts.map +1 -1
- package/build-types/utils/crdt-selection.d.ts.map +1 -1
- package/build-types/utils/crdt-user-selections.d.ts +21 -4
- package/build-types/utils/crdt-user-selections.d.ts.map +1 -1
- package/build-types/utils/test/crdt-user-selections.d.ts +2 -0
- package/build-types/utils/test/crdt-user-selections.d.ts.map +1 -0
- package/package.json +18 -18
- package/src/actions.js +39 -45
- package/src/awareness/block-lookup.ts +169 -0
- package/src/awareness/post-editor-awareness.ts +68 -11
- package/src/awareness/test/block-lookup.ts +504 -0
- package/src/awareness/test/post-editor-awareness.ts +662 -38
- package/src/entities.js +63 -66
- package/src/entity-types/icon.ts +30 -0
- package/src/entity-types/index.ts +3 -0
- package/src/hooks/test/use-post-editor-awareness-state.ts +21 -14
- package/src/hooks/use-post-editor-awareness-state.ts +22 -13
- package/src/private-actions.js +0 -14
- package/src/private-apis.js +2 -2
- package/src/private-selectors.ts +3 -22
- package/src/reducer.js +0 -17
- package/src/resolvers.js +137 -156
- package/src/selectors.ts +0 -7
- package/src/sync.ts +0 -2
- package/src/test/resolvers.js +109 -1
- package/src/types.ts +22 -5
- package/src/utils/crdt-selection.ts +3 -1
- package/src/utils/crdt-user-selections.ts +129 -47
- package/src/utils/test/crdt-user-selections.ts +894 -0
|
@@ -0,0 +1,894 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { Y } from '@wordpress/sync';
|
|
5
|
+
import { select } from '@wordpress/data';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
areSelectionsStatesEqual,
|
|
12
|
+
getSelectionState,
|
|
13
|
+
SelectionType,
|
|
14
|
+
} from '../crdt-user-selections';
|
|
15
|
+
import { CRDT_RECORD_MAP_KEY } from '../../sync';
|
|
16
|
+
|
|
17
|
+
jest.mock( '@wordpress/data', () => ( {
|
|
18
|
+
select: jest.fn(),
|
|
19
|
+
} ) );
|
|
20
|
+
|
|
21
|
+
jest.mock( '@wordpress/block-editor', () => ( {
|
|
22
|
+
store: 'core/block-editor',
|
|
23
|
+
} ) );
|
|
24
|
+
import type {
|
|
25
|
+
CursorPosition,
|
|
26
|
+
SelectionNone,
|
|
27
|
+
SelectionCursor,
|
|
28
|
+
SelectionInOneBlock,
|
|
29
|
+
SelectionInMultipleBlocks,
|
|
30
|
+
SelectionWholeBlock,
|
|
31
|
+
SelectionState,
|
|
32
|
+
WPBlockSelection,
|
|
33
|
+
} from '../../types';
|
|
34
|
+
|
|
35
|
+
// Shared Y.Doc and Y.Map for creating Y.Text instances
|
|
36
|
+
const yDoc = new Y.Doc();
|
|
37
|
+
const yMap = yDoc.getMap( 'test-map' );
|
|
38
|
+
let textCounter = 0;
|
|
39
|
+
let blockCounter = 0;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Helper to create a Y.Text instance attached to a Y.Doc.
|
|
43
|
+
* Y.Text objects must be attached to a Y.Doc to create relative positions.
|
|
44
|
+
*
|
|
45
|
+
* @param yTextValue - The value of the Y.Text instance.
|
|
46
|
+
* @param yTextKey - The key of the Y.Text instance.
|
|
47
|
+
* @return The Y.Text instance, attached to the Y.Doc.
|
|
48
|
+
*/
|
|
49
|
+
function createYText( yTextValue: string, yTextKey?: string ): Y.Text {
|
|
50
|
+
textCounter++;
|
|
51
|
+
const key = yTextKey ?? `test-text-${ textCounter }`;
|
|
52
|
+
const yText = new Y.Text( yTextValue );
|
|
53
|
+
yMap.set( key, yText );
|
|
54
|
+
return yText;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Helper to create a Y.RelativePosition pointing to an index in a Y.Array.
|
|
59
|
+
*
|
|
60
|
+
* @param index - The index in the Y.Array.
|
|
61
|
+
* @return The Y.RelativePosition.
|
|
62
|
+
*/
|
|
63
|
+
function createBlockPosition( index: number ): Y.RelativePosition {
|
|
64
|
+
blockCounter++;
|
|
65
|
+
const yArray = new Y.Array();
|
|
66
|
+
yMap.set( `test-array-${ blockCounter }`, yArray );
|
|
67
|
+
|
|
68
|
+
for ( let i = 0; i <= index; i++ ) {
|
|
69
|
+
yArray.push( [ new Y.Map() ] );
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return Y.createRelativePositionFromTypeIndex( yArray, index );
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Helper to create a CursorPosition with a relative position.
|
|
77
|
+
*
|
|
78
|
+
* @param text - The text of the Y.Text instance.
|
|
79
|
+
* @param offset - The offset of the Y.Text instance.
|
|
80
|
+
* @return The CursorPosition.
|
|
81
|
+
*/
|
|
82
|
+
function createCursorPosition( text: string, offset: number ): CursorPosition {
|
|
83
|
+
const yText = createYText( text );
|
|
84
|
+
const relativePosition = Y.createRelativePositionFromTypeIndex(
|
|
85
|
+
yText,
|
|
86
|
+
offset
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
return {
|
|
90
|
+
relativePosition,
|
|
91
|
+
absoluteOffset: offset,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
describe( 'areSelectionsStatesEqual', () => {
|
|
96
|
+
describe( 'different selection types', () => {
|
|
97
|
+
test( 'returns false when comparing different selection types', () => {
|
|
98
|
+
const selection1: SelectionNone = { type: SelectionType.None };
|
|
99
|
+
const selection2: SelectionCursor = {
|
|
100
|
+
type: SelectionType.Cursor,
|
|
101
|
+
cursorPosition: createCursorPosition( 'test', 0 ),
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
105
|
+
false
|
|
106
|
+
);
|
|
107
|
+
} );
|
|
108
|
+
} );
|
|
109
|
+
|
|
110
|
+
describe( 'SelectionType.None', () => {
|
|
111
|
+
test( 'returns true when both selections are None', () => {
|
|
112
|
+
const selection1: SelectionNone = { type: SelectionType.None };
|
|
113
|
+
const selection2: SelectionNone = { type: SelectionType.None };
|
|
114
|
+
|
|
115
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
116
|
+
true
|
|
117
|
+
);
|
|
118
|
+
} );
|
|
119
|
+
} );
|
|
120
|
+
|
|
121
|
+
describe( 'SelectionType.Cursor', () => {
|
|
122
|
+
test( 'returns true when cursor selections are identical', () => {
|
|
123
|
+
const cursorPosition = createCursorPosition( 'test text', 5 );
|
|
124
|
+
const selection1: SelectionCursor = {
|
|
125
|
+
type: SelectionType.Cursor,
|
|
126
|
+
cursorPosition,
|
|
127
|
+
};
|
|
128
|
+
const selection2: SelectionCursor = {
|
|
129
|
+
type: SelectionType.Cursor,
|
|
130
|
+
cursorPosition,
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
134
|
+
true
|
|
135
|
+
);
|
|
136
|
+
} );
|
|
137
|
+
|
|
138
|
+
test( 'returns false when relative position differs', () => {
|
|
139
|
+
const selection1: SelectionCursor = {
|
|
140
|
+
type: SelectionType.Cursor,
|
|
141
|
+
cursorPosition: createCursorPosition( 'test text', 5 ),
|
|
142
|
+
};
|
|
143
|
+
const selection2: SelectionCursor = {
|
|
144
|
+
type: SelectionType.Cursor,
|
|
145
|
+
cursorPosition: createCursorPosition( 'test text', 3 ),
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
149
|
+
false
|
|
150
|
+
);
|
|
151
|
+
} );
|
|
152
|
+
|
|
153
|
+
test( 'returns false when absolute offset differs', () => {
|
|
154
|
+
const yText = createYText( 'test text' );
|
|
155
|
+
const relativePosition = Y.createRelativePositionFromTypeIndex(
|
|
156
|
+
yText,
|
|
157
|
+
5
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const selection1: SelectionCursor = {
|
|
161
|
+
type: SelectionType.Cursor,
|
|
162
|
+
cursorPosition: {
|
|
163
|
+
relativePosition,
|
|
164
|
+
absoluteOffset: 5,
|
|
165
|
+
},
|
|
166
|
+
};
|
|
167
|
+
const selection2: SelectionCursor = {
|
|
168
|
+
type: SelectionType.Cursor,
|
|
169
|
+
cursorPosition: {
|
|
170
|
+
relativePosition,
|
|
171
|
+
absoluteOffset: 6,
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
176
|
+
false
|
|
177
|
+
);
|
|
178
|
+
} );
|
|
179
|
+
} );
|
|
180
|
+
|
|
181
|
+
describe( 'SelectionType.SelectionInOneBlock', () => {
|
|
182
|
+
test( 'returns true when selections in one block are identical', () => {
|
|
183
|
+
const cursorStartPosition = createCursorPosition( 'test text', 0 );
|
|
184
|
+
const cursorEndPosition = createCursorPosition( 'test text', 4 );
|
|
185
|
+
const selection1: SelectionInOneBlock = {
|
|
186
|
+
type: SelectionType.SelectionInOneBlock,
|
|
187
|
+
cursorStartPosition,
|
|
188
|
+
cursorEndPosition,
|
|
189
|
+
};
|
|
190
|
+
const selection2: SelectionInOneBlock = {
|
|
191
|
+
type: SelectionType.SelectionInOneBlock,
|
|
192
|
+
cursorStartPosition,
|
|
193
|
+
cursorEndPosition,
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
197
|
+
true
|
|
198
|
+
);
|
|
199
|
+
} );
|
|
200
|
+
|
|
201
|
+
test( 'returns false when start position differs', () => {
|
|
202
|
+
const selection1: SelectionInOneBlock = {
|
|
203
|
+
type: SelectionType.SelectionInOneBlock,
|
|
204
|
+
cursorStartPosition: createCursorPosition( 'test text', 0 ),
|
|
205
|
+
cursorEndPosition: createCursorPosition( 'test text', 4 ),
|
|
206
|
+
};
|
|
207
|
+
const selection2: SelectionInOneBlock = {
|
|
208
|
+
type: SelectionType.SelectionInOneBlock,
|
|
209
|
+
cursorStartPosition: createCursorPosition( 'test text', 1 ),
|
|
210
|
+
cursorEndPosition: createCursorPosition( 'test text', 4 ),
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
214
|
+
false
|
|
215
|
+
);
|
|
216
|
+
} );
|
|
217
|
+
|
|
218
|
+
test( 'returns false when end position differs', () => {
|
|
219
|
+
const selection1: SelectionInOneBlock = {
|
|
220
|
+
type: SelectionType.SelectionInOneBlock,
|
|
221
|
+
cursorStartPosition: createCursorPosition( 'test text', 0 ),
|
|
222
|
+
cursorEndPosition: createCursorPosition( 'test text', 4 ),
|
|
223
|
+
};
|
|
224
|
+
const selection2: SelectionInOneBlock = {
|
|
225
|
+
type: SelectionType.SelectionInOneBlock,
|
|
226
|
+
cursorStartPosition: createCursorPosition( 'test text', 0 ),
|
|
227
|
+
cursorEndPosition: createCursorPosition( 'test text', 5 ),
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
231
|
+
false
|
|
232
|
+
);
|
|
233
|
+
} );
|
|
234
|
+
|
|
235
|
+
test( 'returns false when absolute offset differs in start position', () => {
|
|
236
|
+
const yText = createYText( 'test text' );
|
|
237
|
+
const relativePosition = Y.createRelativePositionFromTypeIndex(
|
|
238
|
+
yText,
|
|
239
|
+
0
|
|
240
|
+
);
|
|
241
|
+
const cursorEndPosition = createCursorPosition( 'test text', 4 );
|
|
242
|
+
|
|
243
|
+
const selection1: SelectionInOneBlock = {
|
|
244
|
+
type: SelectionType.SelectionInOneBlock,
|
|
245
|
+
cursorStartPosition: {
|
|
246
|
+
relativePosition,
|
|
247
|
+
absoluteOffset: 0,
|
|
248
|
+
},
|
|
249
|
+
cursorEndPosition,
|
|
250
|
+
};
|
|
251
|
+
const selection2: SelectionInOneBlock = {
|
|
252
|
+
type: SelectionType.SelectionInOneBlock,
|
|
253
|
+
cursorStartPosition: {
|
|
254
|
+
relativePosition,
|
|
255
|
+
absoluteOffset: 1,
|
|
256
|
+
},
|
|
257
|
+
cursorEndPosition,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
261
|
+
false
|
|
262
|
+
);
|
|
263
|
+
} );
|
|
264
|
+
} );
|
|
265
|
+
|
|
266
|
+
describe( 'SelectionType.SelectionInMultipleBlocks', () => {
|
|
267
|
+
test( 'returns true when selections in multiple blocks are identical', () => {
|
|
268
|
+
const cursorStartPosition = createCursorPosition(
|
|
269
|
+
'first block',
|
|
270
|
+
5
|
|
271
|
+
);
|
|
272
|
+
const cursorEndPosition = createCursorPosition( 'second block', 3 );
|
|
273
|
+
const selection1: SelectionInMultipleBlocks = {
|
|
274
|
+
type: SelectionType.SelectionInMultipleBlocks,
|
|
275
|
+
cursorStartPosition,
|
|
276
|
+
cursorEndPosition,
|
|
277
|
+
};
|
|
278
|
+
const selection2: SelectionInMultipleBlocks = {
|
|
279
|
+
type: SelectionType.SelectionInMultipleBlocks,
|
|
280
|
+
cursorStartPosition,
|
|
281
|
+
cursorEndPosition,
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
285
|
+
true
|
|
286
|
+
);
|
|
287
|
+
} );
|
|
288
|
+
|
|
289
|
+
test( 'returns false when start cursor position differs', () => {
|
|
290
|
+
const selection1: SelectionInMultipleBlocks = {
|
|
291
|
+
type: SelectionType.SelectionInMultipleBlocks,
|
|
292
|
+
cursorStartPosition: createCursorPosition( 'first block', 5 ),
|
|
293
|
+
cursorEndPosition: createCursorPosition( 'second block', 3 ),
|
|
294
|
+
};
|
|
295
|
+
const selection2: SelectionInMultipleBlocks = {
|
|
296
|
+
type: SelectionType.SelectionInMultipleBlocks,
|
|
297
|
+
cursorStartPosition: createCursorPosition( 'first block', 6 ),
|
|
298
|
+
cursorEndPosition: createCursorPosition( 'second block', 3 ),
|
|
299
|
+
};
|
|
300
|
+
|
|
301
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
302
|
+
false
|
|
303
|
+
);
|
|
304
|
+
} );
|
|
305
|
+
|
|
306
|
+
test( 'returns false when end cursor position differs', () => {
|
|
307
|
+
const selection1: SelectionInMultipleBlocks = {
|
|
308
|
+
type: SelectionType.SelectionInMultipleBlocks,
|
|
309
|
+
cursorStartPosition: createCursorPosition( 'first block', 5 ),
|
|
310
|
+
cursorEndPosition: createCursorPosition( 'second block', 3 ),
|
|
311
|
+
};
|
|
312
|
+
const selection2: SelectionInMultipleBlocks = {
|
|
313
|
+
type: SelectionType.SelectionInMultipleBlocks,
|
|
314
|
+
cursorStartPosition: createCursorPosition( 'first block', 5 ),
|
|
315
|
+
cursorEndPosition: createCursorPosition( 'second block', 4 ),
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
319
|
+
false
|
|
320
|
+
);
|
|
321
|
+
} );
|
|
322
|
+
} );
|
|
323
|
+
|
|
324
|
+
describe( 'SelectionType.WholeBlock', () => {
|
|
325
|
+
test( 'returns true when whole block selections are identical', () => {
|
|
326
|
+
const blockPosition = createBlockPosition( 0 );
|
|
327
|
+
const selection1: SelectionWholeBlock = {
|
|
328
|
+
type: SelectionType.WholeBlock,
|
|
329
|
+
blockPosition,
|
|
330
|
+
};
|
|
331
|
+
const selection2: SelectionWholeBlock = {
|
|
332
|
+
type: SelectionType.WholeBlock,
|
|
333
|
+
blockPosition,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
337
|
+
true
|
|
338
|
+
);
|
|
339
|
+
} );
|
|
340
|
+
|
|
341
|
+
test( 'returns false when blockPosition differs', () => {
|
|
342
|
+
const selection1: SelectionWholeBlock = {
|
|
343
|
+
type: SelectionType.WholeBlock,
|
|
344
|
+
blockPosition: createBlockPosition( 0 ),
|
|
345
|
+
};
|
|
346
|
+
const selection2: SelectionWholeBlock = {
|
|
347
|
+
type: SelectionType.WholeBlock,
|
|
348
|
+
blockPosition: createBlockPosition( 1 ),
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
352
|
+
false
|
|
353
|
+
);
|
|
354
|
+
} );
|
|
355
|
+
} );
|
|
356
|
+
|
|
357
|
+
describe( 'unknown selection type', () => {
|
|
358
|
+
test( 'returns false for unknown selection type', () => {
|
|
359
|
+
const selection1: SelectionState = {
|
|
360
|
+
type: 'unknown-type' as SelectionType,
|
|
361
|
+
} as SelectionState;
|
|
362
|
+
const selection2: SelectionNone = { type: SelectionType.None };
|
|
363
|
+
|
|
364
|
+
expect( areSelectionsStatesEqual( selection1, selection2 ) ).toBe(
|
|
365
|
+
false
|
|
366
|
+
);
|
|
367
|
+
} );
|
|
368
|
+
} );
|
|
369
|
+
} );
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Helper function to create a Y.Doc with blocks for testing getSelectionState.
|
|
373
|
+
*/
|
|
374
|
+
function createTestDocWithBlocks() {
|
|
375
|
+
const testDoc = new Y.Doc();
|
|
376
|
+
const documentMap = testDoc.getMap( CRDT_RECORD_MAP_KEY );
|
|
377
|
+
const blocks = new Y.Array();
|
|
378
|
+
documentMap.set( 'blocks', blocks );
|
|
379
|
+
|
|
380
|
+
// Create block 1 with a content attribute
|
|
381
|
+
const block1 = new Y.Map();
|
|
382
|
+
block1.set( 'clientId', 'block-1' );
|
|
383
|
+
const block1Attrs = new Y.Map();
|
|
384
|
+
block1Attrs.set( 'content', new Y.Text( 'Hello world' ) );
|
|
385
|
+
block1.set( 'attributes', block1Attrs );
|
|
386
|
+
block1.set( 'innerBlocks', new Y.Array() );
|
|
387
|
+
blocks.push( [ block1 ] );
|
|
388
|
+
|
|
389
|
+
// Create block 2 with a content attribute
|
|
390
|
+
const block2 = new Y.Map();
|
|
391
|
+
block2.set( 'clientId', 'block-2' );
|
|
392
|
+
const block2Attrs = new Y.Map();
|
|
393
|
+
block2Attrs.set( 'content', new Y.Text( 'Second block content' ) );
|
|
394
|
+
block2.set( 'attributes', block2Attrs );
|
|
395
|
+
block2.set( 'innerBlocks', new Y.Array() );
|
|
396
|
+
blocks.push( [ block2 ] );
|
|
397
|
+
|
|
398
|
+
// Create block 3 with nested inner blocks
|
|
399
|
+
const block3 = new Y.Map();
|
|
400
|
+
block3.set( 'clientId', 'block-3' );
|
|
401
|
+
const block3Attrs = new Y.Map();
|
|
402
|
+
block3Attrs.set( 'content', new Y.Text( 'Parent block' ) );
|
|
403
|
+
block3.set( 'attributes', block3Attrs );
|
|
404
|
+
|
|
405
|
+
// Add inner block to block 3
|
|
406
|
+
const innerBlocks = new Y.Array();
|
|
407
|
+
const innerBlock = new Y.Map();
|
|
408
|
+
innerBlock.set( 'clientId', 'inner-block-1' );
|
|
409
|
+
const innerBlockAttrs = new Y.Map();
|
|
410
|
+
innerBlockAttrs.set( 'content', new Y.Text( 'Inner block content' ) );
|
|
411
|
+
innerBlock.set( 'attributes', innerBlockAttrs );
|
|
412
|
+
innerBlock.set( 'innerBlocks', new Y.Array() );
|
|
413
|
+
innerBlocks.push( [ innerBlock ] );
|
|
414
|
+
|
|
415
|
+
block3.set( 'innerBlocks', innerBlocks );
|
|
416
|
+
blocks.push( [ block3 ] );
|
|
417
|
+
|
|
418
|
+
return testDoc;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
describe( 'getSelectionState', () => {
|
|
422
|
+
let testDoc: Y.Doc;
|
|
423
|
+
|
|
424
|
+
const blockIndexMap: Record< string, number > = {
|
|
425
|
+
'block-1': 0,
|
|
426
|
+
'block-2': 1,
|
|
427
|
+
'block-3': 2,
|
|
428
|
+
'inner-block-1': 0,
|
|
429
|
+
};
|
|
430
|
+
|
|
431
|
+
const blockParentMap: Record< string, string > = {
|
|
432
|
+
'block-1': '',
|
|
433
|
+
'block-2': '',
|
|
434
|
+
'block-3': '',
|
|
435
|
+
'inner-block-1': 'block-3',
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
beforeEach( () => {
|
|
439
|
+
testDoc = createTestDocWithBlocks();
|
|
440
|
+
|
|
441
|
+
( select as jest.Mock ).mockReturnValue( {
|
|
442
|
+
getBlockIndex: jest
|
|
443
|
+
.fn()
|
|
444
|
+
.mockImplementation(
|
|
445
|
+
( clientId: string ) => blockIndexMap[ clientId ] ?? -1
|
|
446
|
+
),
|
|
447
|
+
getBlockRootClientId: jest
|
|
448
|
+
.fn()
|
|
449
|
+
.mockImplementation(
|
|
450
|
+
( clientId: string ) => blockParentMap[ clientId ] ?? ''
|
|
451
|
+
),
|
|
452
|
+
getBlockName: jest.fn().mockReturnValue( 'core/paragraph' ),
|
|
453
|
+
} );
|
|
454
|
+
} );
|
|
455
|
+
|
|
456
|
+
afterEach( () => {
|
|
457
|
+
testDoc.destroy();
|
|
458
|
+
} );
|
|
459
|
+
|
|
460
|
+
describe( 'SelectionType.None', () => {
|
|
461
|
+
test( 'returns None when selectionStart is empty object', () => {
|
|
462
|
+
const selectionStart = {} as WPBlockSelection;
|
|
463
|
+
const selectionEnd = {} as WPBlockSelection;
|
|
464
|
+
|
|
465
|
+
const result = getSelectionState(
|
|
466
|
+
selectionStart,
|
|
467
|
+
selectionEnd,
|
|
468
|
+
testDoc
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
472
|
+
} );
|
|
473
|
+
} );
|
|
474
|
+
|
|
475
|
+
describe( 'SelectionType.WholeBlock', () => {
|
|
476
|
+
test( 'returns WholeBlock when same clientId and no offsets', () => {
|
|
477
|
+
const selectionStart: WPBlockSelection = {
|
|
478
|
+
clientId: 'block-1',
|
|
479
|
+
attributeKey: 'content',
|
|
480
|
+
offset: undefined,
|
|
481
|
+
};
|
|
482
|
+
const selectionEnd: WPBlockSelection = {
|
|
483
|
+
clientId: 'block-1',
|
|
484
|
+
attributeKey: 'content',
|
|
485
|
+
offset: undefined,
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const result = getSelectionState(
|
|
489
|
+
selectionStart,
|
|
490
|
+
selectionEnd,
|
|
491
|
+
testDoc
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
expect( result.type ).toBe( SelectionType.WholeBlock );
|
|
495
|
+
expect(
|
|
496
|
+
( result as SelectionWholeBlock ).blockPosition
|
|
497
|
+
).toBeDefined();
|
|
498
|
+
} );
|
|
499
|
+
} );
|
|
500
|
+
|
|
501
|
+
describe( 'SelectionType.Cursor', () => {
|
|
502
|
+
test( 'returns Cursor when same clientId and same offset', () => {
|
|
503
|
+
const selectionStart: WPBlockSelection = {
|
|
504
|
+
clientId: 'block-1',
|
|
505
|
+
attributeKey: 'content',
|
|
506
|
+
offset: 5,
|
|
507
|
+
};
|
|
508
|
+
const selectionEnd: WPBlockSelection = {
|
|
509
|
+
clientId: 'block-1',
|
|
510
|
+
attributeKey: 'content',
|
|
511
|
+
offset: 5,
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const result = getSelectionState(
|
|
515
|
+
selectionStart,
|
|
516
|
+
selectionEnd,
|
|
517
|
+
testDoc
|
|
518
|
+
);
|
|
519
|
+
|
|
520
|
+
expect( result.type ).toBe( SelectionType.Cursor );
|
|
521
|
+
expect(
|
|
522
|
+
( result as SelectionCursor ).cursorPosition
|
|
523
|
+
).toBeDefined();
|
|
524
|
+
expect(
|
|
525
|
+
( result as SelectionCursor ).cursorPosition.absoluteOffset
|
|
526
|
+
).toBe( 5 );
|
|
527
|
+
} );
|
|
528
|
+
|
|
529
|
+
test( 'returns Cursor at start of block (offset 0)', () => {
|
|
530
|
+
const selectionStart: WPBlockSelection = {
|
|
531
|
+
clientId: 'block-1',
|
|
532
|
+
attributeKey: 'content',
|
|
533
|
+
offset: 0,
|
|
534
|
+
};
|
|
535
|
+
const selectionEnd: WPBlockSelection = {
|
|
536
|
+
clientId: 'block-1',
|
|
537
|
+
attributeKey: 'content',
|
|
538
|
+
offset: 0,
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
const result = getSelectionState(
|
|
542
|
+
selectionStart,
|
|
543
|
+
selectionEnd,
|
|
544
|
+
testDoc
|
|
545
|
+
);
|
|
546
|
+
|
|
547
|
+
expect( result.type ).toBe( SelectionType.Cursor );
|
|
548
|
+
expect(
|
|
549
|
+
( result as SelectionCursor ).cursorPosition.absoluteOffset
|
|
550
|
+
).toBe( 0 );
|
|
551
|
+
} );
|
|
552
|
+
|
|
553
|
+
test( 'returns None when block does not exist', () => {
|
|
554
|
+
const selectionStart: WPBlockSelection = {
|
|
555
|
+
clientId: 'non-existent-block',
|
|
556
|
+
attributeKey: 'content',
|
|
557
|
+
offset: 5,
|
|
558
|
+
};
|
|
559
|
+
const selectionEnd: WPBlockSelection = {
|
|
560
|
+
clientId: 'non-existent-block',
|
|
561
|
+
attributeKey: 'content',
|
|
562
|
+
offset: 5,
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const result = getSelectionState(
|
|
566
|
+
selectionStart,
|
|
567
|
+
selectionEnd,
|
|
568
|
+
testDoc
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
572
|
+
} );
|
|
573
|
+
|
|
574
|
+
test( 'returns None when attributeKey does not exist on block', () => {
|
|
575
|
+
const selectionStart: WPBlockSelection = {
|
|
576
|
+
clientId: 'block-1',
|
|
577
|
+
attributeKey: 'non-existent-attribute',
|
|
578
|
+
offset: 5,
|
|
579
|
+
};
|
|
580
|
+
const selectionEnd: WPBlockSelection = {
|
|
581
|
+
clientId: 'block-1',
|
|
582
|
+
attributeKey: 'non-existent-attribute',
|
|
583
|
+
offset: 5,
|
|
584
|
+
};
|
|
585
|
+
|
|
586
|
+
const result = getSelectionState(
|
|
587
|
+
selectionStart,
|
|
588
|
+
selectionEnd,
|
|
589
|
+
testDoc
|
|
590
|
+
);
|
|
591
|
+
|
|
592
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
593
|
+
} );
|
|
594
|
+
|
|
595
|
+
test( 'returns None when attributeKey is missing', () => {
|
|
596
|
+
const selectionStart: WPBlockSelection = {
|
|
597
|
+
clientId: 'block-1',
|
|
598
|
+
attributeKey: undefined as unknown as string,
|
|
599
|
+
offset: 5,
|
|
600
|
+
};
|
|
601
|
+
const selectionEnd: WPBlockSelection = {
|
|
602
|
+
clientId: 'block-1',
|
|
603
|
+
attributeKey: undefined as unknown as string,
|
|
604
|
+
offset: 5,
|
|
605
|
+
};
|
|
606
|
+
|
|
607
|
+
const result = getSelectionState(
|
|
608
|
+
selectionStart,
|
|
609
|
+
selectionEnd,
|
|
610
|
+
testDoc
|
|
611
|
+
);
|
|
612
|
+
|
|
613
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
614
|
+
} );
|
|
615
|
+
} );
|
|
616
|
+
|
|
617
|
+
describe( 'SelectionType.SelectionInOneBlock', () => {
|
|
618
|
+
test( 'returns SelectionInOneBlock when same clientId but different offsets', () => {
|
|
619
|
+
const selectionStart: WPBlockSelection = {
|
|
620
|
+
clientId: 'block-1',
|
|
621
|
+
attributeKey: 'content',
|
|
622
|
+
offset: 0,
|
|
623
|
+
};
|
|
624
|
+
const selectionEnd: WPBlockSelection = {
|
|
625
|
+
clientId: 'block-1',
|
|
626
|
+
attributeKey: 'content',
|
|
627
|
+
offset: 5,
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
const result = getSelectionState(
|
|
631
|
+
selectionStart,
|
|
632
|
+
selectionEnd,
|
|
633
|
+
testDoc
|
|
634
|
+
);
|
|
635
|
+
|
|
636
|
+
expect( result.type ).toBe( SelectionType.SelectionInOneBlock );
|
|
637
|
+
expect(
|
|
638
|
+
( result as SelectionInOneBlock ).cursorStartPosition
|
|
639
|
+
.absoluteOffset
|
|
640
|
+
).toBe( 0 );
|
|
641
|
+
expect(
|
|
642
|
+
( result as SelectionInOneBlock ).cursorEndPosition
|
|
643
|
+
.absoluteOffset
|
|
644
|
+
).toBe( 5 );
|
|
645
|
+
} );
|
|
646
|
+
|
|
647
|
+
test( 'returns None when start block does not exist', () => {
|
|
648
|
+
const selectionStart: WPBlockSelection = {
|
|
649
|
+
clientId: 'non-existent-block',
|
|
650
|
+
attributeKey: 'content',
|
|
651
|
+
offset: 0,
|
|
652
|
+
};
|
|
653
|
+
const selectionEnd: WPBlockSelection = {
|
|
654
|
+
clientId: 'non-existent-block',
|
|
655
|
+
attributeKey: 'content',
|
|
656
|
+
offset: 5,
|
|
657
|
+
};
|
|
658
|
+
|
|
659
|
+
const result = getSelectionState(
|
|
660
|
+
selectionStart,
|
|
661
|
+
selectionEnd,
|
|
662
|
+
testDoc
|
|
663
|
+
);
|
|
664
|
+
|
|
665
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
666
|
+
} );
|
|
667
|
+
} );
|
|
668
|
+
|
|
669
|
+
describe( 'SelectionType.SelectionInMultipleBlocks', () => {
|
|
670
|
+
test( 'returns SelectionInMultipleBlocks when different clientIds', () => {
|
|
671
|
+
const selectionStart: WPBlockSelection = {
|
|
672
|
+
clientId: 'block-1',
|
|
673
|
+
attributeKey: 'content',
|
|
674
|
+
offset: 5,
|
|
675
|
+
};
|
|
676
|
+
const selectionEnd: WPBlockSelection = {
|
|
677
|
+
clientId: 'block-2',
|
|
678
|
+
attributeKey: 'content',
|
|
679
|
+
offset: 3,
|
|
680
|
+
};
|
|
681
|
+
|
|
682
|
+
const result = getSelectionState(
|
|
683
|
+
selectionStart,
|
|
684
|
+
selectionEnd,
|
|
685
|
+
testDoc
|
|
686
|
+
);
|
|
687
|
+
|
|
688
|
+
expect( result.type ).toBe(
|
|
689
|
+
SelectionType.SelectionInMultipleBlocks
|
|
690
|
+
);
|
|
691
|
+
expect(
|
|
692
|
+
( result as SelectionInMultipleBlocks ).cursorStartPosition
|
|
693
|
+
.absoluteOffset
|
|
694
|
+
).toBe( 5 );
|
|
695
|
+
expect(
|
|
696
|
+
( result as SelectionInMultipleBlocks ).cursorEndPosition
|
|
697
|
+
.absoluteOffset
|
|
698
|
+
).toBe( 3 );
|
|
699
|
+
} );
|
|
700
|
+
|
|
701
|
+
test( 'returns None when start block does not exist in multi-block selection', () => {
|
|
702
|
+
const selectionStart: WPBlockSelection = {
|
|
703
|
+
clientId: 'non-existent-block',
|
|
704
|
+
attributeKey: 'content',
|
|
705
|
+
offset: 5,
|
|
706
|
+
};
|
|
707
|
+
const selectionEnd: WPBlockSelection = {
|
|
708
|
+
clientId: 'block-2',
|
|
709
|
+
attributeKey: 'content',
|
|
710
|
+
offset: 3,
|
|
711
|
+
};
|
|
712
|
+
|
|
713
|
+
const result = getSelectionState(
|
|
714
|
+
selectionStart,
|
|
715
|
+
selectionEnd,
|
|
716
|
+
testDoc
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
720
|
+
} );
|
|
721
|
+
|
|
722
|
+
test( 'returns None when end block does not exist in multi-block selection', () => {
|
|
723
|
+
const selectionStart: WPBlockSelection = {
|
|
724
|
+
clientId: 'block-1',
|
|
725
|
+
attributeKey: 'content',
|
|
726
|
+
offset: 5,
|
|
727
|
+
};
|
|
728
|
+
const selectionEnd: WPBlockSelection = {
|
|
729
|
+
clientId: 'non-existent-block',
|
|
730
|
+
attributeKey: 'content',
|
|
731
|
+
offset: 3,
|
|
732
|
+
};
|
|
733
|
+
|
|
734
|
+
const result = getSelectionState(
|
|
735
|
+
selectionStart,
|
|
736
|
+
selectionEnd,
|
|
737
|
+
testDoc
|
|
738
|
+
);
|
|
739
|
+
|
|
740
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
741
|
+
} );
|
|
742
|
+
} );
|
|
743
|
+
|
|
744
|
+
describe( 'inner blocks', () => {
|
|
745
|
+
test( 'returns Cursor for selection in inner block', () => {
|
|
746
|
+
const selectionStart: WPBlockSelection = {
|
|
747
|
+
clientId: 'inner-block-1',
|
|
748
|
+
attributeKey: 'content',
|
|
749
|
+
offset: 3,
|
|
750
|
+
};
|
|
751
|
+
const selectionEnd: WPBlockSelection = {
|
|
752
|
+
clientId: 'inner-block-1',
|
|
753
|
+
attributeKey: 'content',
|
|
754
|
+
offset: 3,
|
|
755
|
+
};
|
|
756
|
+
|
|
757
|
+
const result = getSelectionState(
|
|
758
|
+
selectionStart,
|
|
759
|
+
selectionEnd,
|
|
760
|
+
testDoc
|
|
761
|
+
);
|
|
762
|
+
|
|
763
|
+
expect( result.type ).toBe( SelectionType.Cursor );
|
|
764
|
+
} );
|
|
765
|
+
|
|
766
|
+
test( 'returns SelectionInMultipleBlocks spanning parent and inner block', () => {
|
|
767
|
+
const selectionStart: WPBlockSelection = {
|
|
768
|
+
clientId: 'block-3',
|
|
769
|
+
attributeKey: 'content',
|
|
770
|
+
offset: 2,
|
|
771
|
+
};
|
|
772
|
+
const selectionEnd: WPBlockSelection = {
|
|
773
|
+
clientId: 'inner-block-1',
|
|
774
|
+
attributeKey: 'content',
|
|
775
|
+
offset: 5,
|
|
776
|
+
};
|
|
777
|
+
|
|
778
|
+
const result = getSelectionState(
|
|
779
|
+
selectionStart,
|
|
780
|
+
selectionEnd,
|
|
781
|
+
testDoc
|
|
782
|
+
);
|
|
783
|
+
|
|
784
|
+
expect( result.type ).toBe(
|
|
785
|
+
SelectionType.SelectionInMultipleBlocks
|
|
786
|
+
);
|
|
787
|
+
} );
|
|
788
|
+
} );
|
|
789
|
+
|
|
790
|
+
describe( 'relative position accuracy', () => {
|
|
791
|
+
test( 'cursor position survives text insertion before it', () => {
|
|
792
|
+
const selectionStart: WPBlockSelection = {
|
|
793
|
+
clientId: 'block-1',
|
|
794
|
+
attributeKey: 'content',
|
|
795
|
+
offset: 5,
|
|
796
|
+
};
|
|
797
|
+
const selectionEnd: WPBlockSelection = {
|
|
798
|
+
clientId: 'block-1',
|
|
799
|
+
attributeKey: 'content',
|
|
800
|
+
offset: 5,
|
|
801
|
+
};
|
|
802
|
+
|
|
803
|
+
const result = getSelectionState(
|
|
804
|
+
selectionStart,
|
|
805
|
+
selectionEnd,
|
|
806
|
+
testDoc
|
|
807
|
+
);
|
|
808
|
+
|
|
809
|
+
expect( result.type ).toBe( SelectionType.Cursor );
|
|
810
|
+
const cursorResult = result as SelectionCursor;
|
|
811
|
+
|
|
812
|
+
// Insert text before the cursor position
|
|
813
|
+
const documentMap = testDoc.getMap( CRDT_RECORD_MAP_KEY );
|
|
814
|
+
const blocks = documentMap.get( 'blocks' ) as Y.Array<
|
|
815
|
+
Y.Map< unknown >
|
|
816
|
+
>;
|
|
817
|
+
const block1 = blocks.get( 0 );
|
|
818
|
+
const attrs = block1.get( 'attributes' ) as Y.Map< Y.Text >;
|
|
819
|
+
const ytext = attrs.get( 'content' );
|
|
820
|
+
|
|
821
|
+
if ( ! ytext ) {
|
|
822
|
+
throw new Error( 'Y.Text not found' );
|
|
823
|
+
}
|
|
824
|
+
|
|
825
|
+
ytext.insert( 0, 'PREFIX ' ); // Insert at beginning
|
|
826
|
+
|
|
827
|
+
// Convert relative position back to absolute
|
|
828
|
+
const absolutePosition =
|
|
829
|
+
Y.createAbsolutePositionFromRelativePosition(
|
|
830
|
+
cursorResult.cursorPosition.relativePosition,
|
|
831
|
+
testDoc
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
// The absolute index should have shifted by 7 (length of 'PREFIX ')
|
|
835
|
+
expect( absolutePosition?.index ).toBe( 12 ); // 5 + 7
|
|
836
|
+
} );
|
|
837
|
+
} );
|
|
838
|
+
|
|
839
|
+
describe( 'edge cases', () => {
|
|
840
|
+
test( 'handles empty blocks array', () => {
|
|
841
|
+
const emptyDoc = new Y.Doc();
|
|
842
|
+
const documentMap = emptyDoc.getMap( CRDT_RECORD_MAP_KEY );
|
|
843
|
+
documentMap.set( 'blocks', new Y.Array() );
|
|
844
|
+
|
|
845
|
+
const selectionStart: WPBlockSelection = {
|
|
846
|
+
clientId: 'block-1',
|
|
847
|
+
attributeKey: 'content',
|
|
848
|
+
offset: 5,
|
|
849
|
+
};
|
|
850
|
+
const selectionEnd: WPBlockSelection = {
|
|
851
|
+
clientId: 'block-1',
|
|
852
|
+
attributeKey: 'content',
|
|
853
|
+
offset: 5,
|
|
854
|
+
};
|
|
855
|
+
|
|
856
|
+
const result = getSelectionState(
|
|
857
|
+
selectionStart,
|
|
858
|
+
selectionEnd,
|
|
859
|
+
emptyDoc
|
|
860
|
+
);
|
|
861
|
+
|
|
862
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
863
|
+
|
|
864
|
+
emptyDoc.destroy();
|
|
865
|
+
} );
|
|
866
|
+
|
|
867
|
+
test( 'handles missing blocks in document', () => {
|
|
868
|
+
const docWithoutBlocks = new Y.Doc();
|
|
869
|
+
docWithoutBlocks.getMap( CRDT_RECORD_MAP_KEY );
|
|
870
|
+
// Note: not setting 'blocks' at all
|
|
871
|
+
|
|
872
|
+
const selectionStart: WPBlockSelection = {
|
|
873
|
+
clientId: 'block-1',
|
|
874
|
+
attributeKey: 'content',
|
|
875
|
+
offset: 5,
|
|
876
|
+
};
|
|
877
|
+
const selectionEnd: WPBlockSelection = {
|
|
878
|
+
clientId: 'block-1',
|
|
879
|
+
attributeKey: 'content',
|
|
880
|
+
offset: 5,
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
const result = getSelectionState(
|
|
884
|
+
selectionStart,
|
|
885
|
+
selectionEnd,
|
|
886
|
+
docWithoutBlocks
|
|
887
|
+
);
|
|
888
|
+
|
|
889
|
+
expect( result.type ).toBe( SelectionType.None );
|
|
890
|
+
|
|
891
|
+
docWithoutBlocks.destroy();
|
|
892
|
+
} );
|
|
893
|
+
} );
|
|
894
|
+
} );
|