@wordpress/core-data 7.39.1-next.v.202602091733.0 → 7.40.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 +2 -0
- package/README.md +41 -0
- package/build/actions.cjs +52 -0
- package/build/actions.cjs.map +2 -2
- package/build/awareness/base-awareness.cjs +1 -8
- package/build/awareness/base-awareness.cjs.map +2 -2
- package/build/awareness/types.cjs.map +1 -1
- package/build/awareness/utils.cjs +8 -51
- package/build/awareness/utils.cjs.map +2 -2
- package/build/entities.cjs +7 -1
- package/build/entities.cjs.map +2 -2
- package/build/hooks/use-entity-block-editor.cjs +13 -19
- package/build/hooks/use-entity-block-editor.cjs.map +2 -2
- package/build/index.cjs +6 -1
- package/build/index.cjs.map +2 -2
- package/build/private-actions.cjs +8 -0
- package/build/private-actions.cjs.map +2 -2
- package/build/private-apis.cjs +2 -1
- package/build/private-apis.cjs.map +2 -2
- package/build/private-selectors.cjs +5 -0
- package/build/private-selectors.cjs.map +2 -2
- package/build/reducer.cjs +31 -1
- package/build/reducer.cjs.map +2 -2
- package/build/resolvers.cjs +26 -1
- package/build/resolvers.cjs.map +2 -2
- package/build/selectors.cjs +15 -0
- package/build/selectors.cjs.map +2 -2
- package/build/utils/crdt-blocks.cjs +5 -3
- package/build/utils/crdt-blocks.cjs.map +2 -2
- package/build/utils/crdt.cjs +23 -19
- package/build/utils/crdt.cjs.map +2 -2
- package/build-module/actions.mjs +50 -0
- package/build-module/actions.mjs.map +2 -2
- package/build-module/awareness/base-awareness.mjs +1 -8
- package/build-module/awareness/base-awareness.mjs.map +2 -2
- package/build-module/awareness/utils.mjs +8 -51
- package/build-module/awareness/utils.mjs.map +2 -2
- package/build-module/entities.mjs +7 -1
- package/build-module/entities.mjs.map +2 -2
- package/build-module/hooks/use-entity-block-editor.mjs +13 -19
- package/build-module/hooks/use-entity-block-editor.mjs.map +2 -2
- package/build-module/index.mjs +3 -0
- package/build-module/index.mjs.map +2 -2
- package/build-module/private-actions.mjs +7 -0
- package/build-module/private-actions.mjs.map +2 -2
- package/build-module/private-apis.mjs +6 -2
- package/build-module/private-apis.mjs.map +2 -2
- package/build-module/private-selectors.mjs +8 -1
- package/build-module/private-selectors.mjs.map +2 -2
- package/build-module/reducer.mjs +29 -1
- package/build-module/reducer.mjs.map +2 -2
- package/build-module/resolvers.mjs +25 -1
- package/build-module/resolvers.mjs.map +2 -2
- package/build-module/selectors.mjs +14 -0
- package/build-module/selectors.mjs.map +2 -2
- package/build-module/utils/crdt-blocks.mjs +3 -2
- package/build-module/utils/crdt-blocks.mjs.map +2 -2
- package/build-module/utils/crdt.mjs +25 -20
- package/build-module/utils/crdt.mjs.map +2 -2
- package/build-types/actions.d.ts +12 -0
- package/build-types/actions.d.ts.map +1 -1
- package/build-types/awareness/base-awareness.d.ts.map +1 -1
- package/build-types/awareness/test/awareness-state.d.ts +2 -0
- package/build-types/awareness/test/awareness-state.d.ts.map +1 -0
- package/build-types/awareness/test/base-awareness.d.ts +2 -0
- package/build-types/awareness/test/base-awareness.d.ts.map +1 -0
- package/build-types/awareness/test/post-editor-awareness.d.ts +2 -0
- package/build-types/awareness/test/post-editor-awareness.d.ts.map +1 -0
- package/build-types/awareness/test/typed-awareness.d.ts +2 -0
- package/build-types/awareness/test/typed-awareness.d.ts.map +1 -0
- package/build-types/awareness/test/utils.d.ts +2 -0
- package/build-types/awareness/test/utils.d.ts.map +1 -0
- package/build-types/awareness/types.d.ts +0 -1
- package/build-types/awareness/types.d.ts.map +1 -1
- package/build-types/awareness/utils.d.ts +2 -3
- package/build-types/awareness/utils.d.ts.map +1 -1
- package/build-types/entities.d.ts.map +1 -1
- package/build-types/hooks/test/use-post-editor-awareness-state.d.ts +2 -0
- package/build-types/hooks/test/use-post-editor-awareness-state.d.ts.map +1 -0
- package/build-types/hooks/use-entity-block-editor.d.ts.map +1 -1
- package/build-types/index.d.ts +5 -0
- package/build-types/index.d.ts.map +1 -1
- package/build-types/private-actions.d.ts +8 -0
- package/build-types/private-actions.d.ts.map +1 -1
- package/build-types/private-apis.d.ts.map +1 -1
- package/build-types/private-selectors.d.ts +8 -1
- package/build-types/private-selectors.d.ts.map +1 -1
- package/build-types/reducer.d.ts +23 -0
- package/build-types/reducer.d.ts.map +1 -1
- package/build-types/resolvers.d.ts +3 -0
- package/build-types/resolvers.d.ts.map +1 -1
- package/build-types/selectors.d.ts +17 -0
- package/build-types/selectors.d.ts.map +1 -1
- package/build-types/utils/crdt-blocks.d.ts +9 -0
- package/build-types/utils/crdt-blocks.d.ts.map +1 -1
- package/build-types/utils/crdt.d.ts +6 -4
- package/build-types/utils/crdt.d.ts.map +1 -1
- package/package.json +18 -18
- package/src/actions.js +78 -0
- package/src/awareness/base-awareness.ts +1 -14
- package/src/awareness/test/awareness-state.ts +417 -0
- package/src/awareness/test/base-awareness.ts +321 -0
- package/src/awareness/test/post-editor-awareness.ts +561 -0
- package/src/awareness/test/typed-awareness.ts +148 -0
- package/src/awareness/test/utils.ts +305 -0
- package/src/awareness/types.ts +0 -1
- package/src/awareness/utils.ts +8 -82
- package/src/entities.js +7 -1
- package/src/hooks/test/use-post-editor-awareness-state.ts +477 -0
- package/src/hooks/use-entity-block-editor.js +15 -21
- package/src/index.js +7 -0
- package/src/private-actions.js +14 -0
- package/src/private-apis.js +5 -1
- package/src/private-selectors.ts +16 -1
- package/src/reducer.js +45 -0
- package/src/resolvers.js +31 -2
- package/src/selectors.ts +41 -0
- package/src/test/actions.js +79 -0
- package/src/test/entity-provider.js +74 -0
- package/src/test/resolvers.js +2 -0
- package/src/test/store.js +30 -0
- package/src/utils/crdt-blocks.ts +2 -2
- package/src/utils/crdt.ts +44 -29
- package/src/utils/test/crdt.ts +212 -7
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External dependencies
|
|
3
|
+
*/
|
|
4
|
+
import { Y } from '@wordpress/sync';
|
|
5
|
+
import { resolveSelect } from '@wordpress/data';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal dependencies
|
|
9
|
+
*/
|
|
10
|
+
import {
|
|
11
|
+
BaseAwarenessState,
|
|
12
|
+
BaseAwareness,
|
|
13
|
+
baseEqualityFieldChecks,
|
|
14
|
+
} from '../base-awareness';
|
|
15
|
+
import { areCollaboratorInfosEqual } from '../utils';
|
|
16
|
+
import type { BaseState, CollaboratorInfo } from '../types';
|
|
17
|
+
|
|
18
|
+
// Mock WordPress data
|
|
19
|
+
jest.mock( '@wordpress/data', () => ( {
|
|
20
|
+
resolveSelect: jest.fn(),
|
|
21
|
+
} ) );
|
|
22
|
+
|
|
23
|
+
// Mock window.navigator.userAgent
|
|
24
|
+
const mockUserAgent = ( userAgent: string ) => {
|
|
25
|
+
Object.defineProperty( window.navigator, 'userAgent', {
|
|
26
|
+
value: userAgent,
|
|
27
|
+
configurable: true,
|
|
28
|
+
} );
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const mockAvatarUrls = {
|
|
32
|
+
'24': 'https://example.com/avatar-24.png',
|
|
33
|
+
'48': 'https://example.com/avatar-48.png',
|
|
34
|
+
'96': 'https://example.com/avatar-96.png',
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const createMockCollaboratorInfo = () => ( {
|
|
38
|
+
id: 1,
|
|
39
|
+
name: 'Test User',
|
|
40
|
+
slug: 'test-user',
|
|
41
|
+
avatar_urls: mockAvatarUrls,
|
|
42
|
+
} );
|
|
43
|
+
|
|
44
|
+
describe( 'BaseAwareness', () => {
|
|
45
|
+
let doc: Y.Doc;
|
|
46
|
+
|
|
47
|
+
beforeEach( () => {
|
|
48
|
+
jest.useFakeTimers();
|
|
49
|
+
doc = new Y.Doc();
|
|
50
|
+
|
|
51
|
+
// Reset to Chrome
|
|
52
|
+
mockUserAgent(
|
|
53
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Mock Date.now for consistent timestamps
|
|
57
|
+
jest.spyOn( Date, 'now' ).mockReturnValue( 1704067200000 );
|
|
58
|
+
|
|
59
|
+
// Mock resolveSelect to return getCurrentUser
|
|
60
|
+
( resolveSelect as jest.Mock ).mockReturnValue( {
|
|
61
|
+
getCurrentUser: jest
|
|
62
|
+
.fn()
|
|
63
|
+
.mockResolvedValue( createMockCollaboratorInfo() ),
|
|
64
|
+
} );
|
|
65
|
+
} );
|
|
66
|
+
|
|
67
|
+
afterEach( () => {
|
|
68
|
+
jest.useRealTimers();
|
|
69
|
+
jest.restoreAllMocks();
|
|
70
|
+
doc.destroy();
|
|
71
|
+
} );
|
|
72
|
+
|
|
73
|
+
describe( 'construction', () => {
|
|
74
|
+
test( 'should create instance with Y.Doc', () => {
|
|
75
|
+
const awareness = new BaseAwareness( doc );
|
|
76
|
+
expect( awareness ).toBeInstanceOf( BaseAwareness );
|
|
77
|
+
} );
|
|
78
|
+
|
|
79
|
+
test( 'should have correct clientID from doc', () => {
|
|
80
|
+
const awareness = new BaseAwareness( doc );
|
|
81
|
+
expect( awareness.clientID ).toBe( doc.clientID );
|
|
82
|
+
} );
|
|
83
|
+
} );
|
|
84
|
+
|
|
85
|
+
describe( 'setUp', () => {
|
|
86
|
+
test( 'should be idempotent', () => {
|
|
87
|
+
const awareness = new BaseAwareness( doc );
|
|
88
|
+
|
|
89
|
+
awareness.setUp();
|
|
90
|
+
awareness.setUp();
|
|
91
|
+
|
|
92
|
+
// Should not throw and should only call getCurrentUser once
|
|
93
|
+
expect( resolveSelect ).toHaveBeenCalledTimes( 1 );
|
|
94
|
+
} );
|
|
95
|
+
|
|
96
|
+
test( 'should fetch current user and set userInfo', async () => {
|
|
97
|
+
const awareness = new BaseAwareness( doc );
|
|
98
|
+
|
|
99
|
+
awareness.setUp();
|
|
100
|
+
|
|
101
|
+
// Wait for async operations
|
|
102
|
+
await Promise.resolve();
|
|
103
|
+
|
|
104
|
+
const collaboratorInfo =
|
|
105
|
+
awareness.getLocalStateField( 'collaboratorInfo' );
|
|
106
|
+
expect( collaboratorInfo ).toBeDefined();
|
|
107
|
+
expect( collaboratorInfo?.id ).toBe( 1 );
|
|
108
|
+
expect( collaboratorInfo?.name ).toBe( 'Test User' );
|
|
109
|
+
expect( collaboratorInfo?.browserType ).toBe( 'Chrome' );
|
|
110
|
+
} );
|
|
111
|
+
} );
|
|
112
|
+
|
|
113
|
+
describe( 'baseEqualityFieldChecks', () => {
|
|
114
|
+
test( 'should have userInfo equality check', () => {
|
|
115
|
+
expect( baseEqualityFieldChecks.collaboratorInfo ).toBe(
|
|
116
|
+
areCollaboratorInfosEqual
|
|
117
|
+
);
|
|
118
|
+
} );
|
|
119
|
+
} );
|
|
120
|
+
} );
|
|
121
|
+
|
|
122
|
+
describe( 'BaseAwarenessState', () => {
|
|
123
|
+
/**
|
|
124
|
+
* Concrete implementation for testing the abstract class
|
|
125
|
+
*/
|
|
126
|
+
class TestBaseAwarenessState extends BaseAwarenessState< BaseState > {
|
|
127
|
+
protected equalityFieldChecks = {
|
|
128
|
+
collaboratorInfo: areCollaboratorInfosEqual,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let doc: Y.Doc;
|
|
133
|
+
|
|
134
|
+
beforeEach( () => {
|
|
135
|
+
jest.useFakeTimers();
|
|
136
|
+
doc = new Y.Doc();
|
|
137
|
+
|
|
138
|
+
mockUserAgent(
|
|
139
|
+
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
jest.spyOn( Date, 'now' ).mockReturnValue( 1704067200000 );
|
|
143
|
+
|
|
144
|
+
( resolveSelect as jest.Mock ).mockReturnValue( {
|
|
145
|
+
getCurrentUser: jest
|
|
146
|
+
.fn()
|
|
147
|
+
.mockResolvedValue( createMockCollaboratorInfo() ),
|
|
148
|
+
} );
|
|
149
|
+
} );
|
|
150
|
+
|
|
151
|
+
afterEach( () => {
|
|
152
|
+
jest.useRealTimers();
|
|
153
|
+
jest.restoreAllMocks();
|
|
154
|
+
doc.destroy();
|
|
155
|
+
} );
|
|
156
|
+
|
|
157
|
+
describe( 'onSetUp', () => {
|
|
158
|
+
test( 'should call setCurrentUserInfo', async () => {
|
|
159
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
160
|
+
|
|
161
|
+
awareness.setUp();
|
|
162
|
+
await Promise.resolve();
|
|
163
|
+
|
|
164
|
+
expect( resolveSelect ).toHaveBeenCalledWith( 'core' );
|
|
165
|
+
} );
|
|
166
|
+
|
|
167
|
+
test( 'should set collaboratorInfo for other users', async () => {
|
|
168
|
+
// Set up another user state first
|
|
169
|
+
const doc2 = new Y.Doc();
|
|
170
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
171
|
+
|
|
172
|
+
// Manually add another user's state
|
|
173
|
+
awareness.setLocalStateField( 'collaboratorInfo', {
|
|
174
|
+
id: 2,
|
|
175
|
+
name: 'Other User',
|
|
176
|
+
slug: 'other-user',
|
|
177
|
+
avatar_urls: mockAvatarUrls,
|
|
178
|
+
browserType: 'Firefox',
|
|
179
|
+
enteredAt: 1704067200000,
|
|
180
|
+
} );
|
|
181
|
+
|
|
182
|
+
awareness.setUp();
|
|
183
|
+
await Promise.resolve();
|
|
184
|
+
|
|
185
|
+
const collaboratorInfo =
|
|
186
|
+
awareness.getLocalStateField( 'collaboratorInfo' );
|
|
187
|
+
expect( collaboratorInfo ).toBeDefined();
|
|
188
|
+
|
|
189
|
+
doc2.destroy();
|
|
190
|
+
} );
|
|
191
|
+
} );
|
|
192
|
+
|
|
193
|
+
describe( 'getLocalStateField', () => {
|
|
194
|
+
test( 'should return null when field not set', () => {
|
|
195
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
196
|
+
expect(
|
|
197
|
+
awareness.getLocalStateField( 'collaboratorInfo' )
|
|
198
|
+
).toBeNull();
|
|
199
|
+
} );
|
|
200
|
+
|
|
201
|
+
test( 'should return collaboratorInfo after setUp', async () => {
|
|
202
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
203
|
+
awareness.setUp();
|
|
204
|
+
await Promise.resolve();
|
|
205
|
+
|
|
206
|
+
const collaboratorInfo =
|
|
207
|
+
awareness.getLocalStateField( 'collaboratorInfo' );
|
|
208
|
+
expect( collaboratorInfo ).not.toBeNull();
|
|
209
|
+
expect( collaboratorInfo?.name ).toBe( 'Test User' );
|
|
210
|
+
} );
|
|
211
|
+
} );
|
|
212
|
+
|
|
213
|
+
describe( 'setLocalStateField', () => {
|
|
214
|
+
test( 'should set collaboratorInfo field', () => {
|
|
215
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
216
|
+
const collaboratorInfo: CollaboratorInfo = {
|
|
217
|
+
id: 42,
|
|
218
|
+
name: 'Custom User',
|
|
219
|
+
slug: 'custom-user',
|
|
220
|
+
avatar_urls: mockAvatarUrls,
|
|
221
|
+
browserType: 'Safari',
|
|
222
|
+
enteredAt: 1704067200000,
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
awareness.setLocalStateField(
|
|
226
|
+
'collaboratorInfo',
|
|
227
|
+
collaboratorInfo
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
expect(
|
|
231
|
+
awareness.getLocalStateField( 'collaboratorInfo' )
|
|
232
|
+
).toEqual( collaboratorInfo );
|
|
233
|
+
} );
|
|
234
|
+
|
|
235
|
+
test( 'should not update if collaboratorInfo is equal', () => {
|
|
236
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
237
|
+
const collaboratorInfo: CollaboratorInfo = {
|
|
238
|
+
id: 42,
|
|
239
|
+
name: 'Custom User',
|
|
240
|
+
slug: 'custom-user',
|
|
241
|
+
avatar_urls: mockAvatarUrls,
|
|
242
|
+
browserType: 'Safari',
|
|
243
|
+
enteredAt: 1704067200000,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
awareness.setLocalStateField(
|
|
247
|
+
'collaboratorInfo',
|
|
248
|
+
collaboratorInfo
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
// Subscribe to detect updates
|
|
252
|
+
const callback = jest.fn();
|
|
253
|
+
awareness.onStateChange( callback );
|
|
254
|
+
|
|
255
|
+
// Set same collaboratorInfo
|
|
256
|
+
awareness.setLocalStateField( 'collaboratorInfo', {
|
|
257
|
+
...collaboratorInfo,
|
|
258
|
+
} );
|
|
259
|
+
|
|
260
|
+
// Trigger awareness change event to test if callback is called
|
|
261
|
+
awareness.emit( 'change', [
|
|
262
|
+
{
|
|
263
|
+
added: [],
|
|
264
|
+
updated: [ awareness.clientID ],
|
|
265
|
+
removed: [],
|
|
266
|
+
},
|
|
267
|
+
] );
|
|
268
|
+
|
|
269
|
+
// Callback should not be called for equal values
|
|
270
|
+
expect( callback ).not.toHaveBeenCalled();
|
|
271
|
+
} );
|
|
272
|
+
} );
|
|
273
|
+
|
|
274
|
+
describe( 'state subscription', () => {
|
|
275
|
+
test( 'should notify subscribers on state change', async () => {
|
|
276
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
277
|
+
const callback = jest.fn();
|
|
278
|
+
|
|
279
|
+
awareness.onStateChange( callback );
|
|
280
|
+
awareness.setUp();
|
|
281
|
+
await Promise.resolve();
|
|
282
|
+
|
|
283
|
+
// Trigger awareness change event
|
|
284
|
+
awareness.emit( 'change', [
|
|
285
|
+
{
|
|
286
|
+
added: [],
|
|
287
|
+
updated: [ awareness.clientID ],
|
|
288
|
+
removed: [],
|
|
289
|
+
},
|
|
290
|
+
] );
|
|
291
|
+
|
|
292
|
+
expect( callback ).toHaveBeenCalled();
|
|
293
|
+
} );
|
|
294
|
+
|
|
295
|
+
test( 'should include enhanced state with isMe and isConnected', async () => {
|
|
296
|
+
const awareness = new TestBaseAwarenessState( doc );
|
|
297
|
+
let receivedStates: any[] = [];
|
|
298
|
+
|
|
299
|
+
awareness.onStateChange( ( states ) => {
|
|
300
|
+
receivedStates = states;
|
|
301
|
+
} );
|
|
302
|
+
|
|
303
|
+
awareness.setUp();
|
|
304
|
+
await Promise.resolve();
|
|
305
|
+
|
|
306
|
+
// Trigger awareness change event
|
|
307
|
+
awareness.emit( 'change', [
|
|
308
|
+
{
|
|
309
|
+
added: [],
|
|
310
|
+
updated: [ awareness.clientID ],
|
|
311
|
+
removed: [],
|
|
312
|
+
},
|
|
313
|
+
] );
|
|
314
|
+
|
|
315
|
+
expect( receivedStates.length ).toBeGreaterThan( 0 );
|
|
316
|
+
const myState = receivedStates.find( ( s ) => s.isMe );
|
|
317
|
+
expect( myState ).toBeDefined();
|
|
318
|
+
expect( myState.isConnected ).toBe( true );
|
|
319
|
+
} );
|
|
320
|
+
} );
|
|
321
|
+
} );
|