@wordpress/core-data 4.0.1-next.253d9b6e21.0 → 4.0.3

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 (106) hide show
  1. package/README.md +11 -3
  2. package/build/actions.js +124 -117
  3. package/build/actions.js.map +1 -1
  4. package/build/batch/default-processor.js +58 -27
  5. package/build/batch/default-processor.js.map +1 -1
  6. package/build/entities.js +24 -18
  7. package/build/entities.js.map +1 -1
  8. package/build/index.js +9 -17
  9. package/build/index.js.map +1 -1
  10. package/build/locks/actions.js +17 -77
  11. package/build/locks/actions.js.map +1 -1
  12. package/build/locks/engine.js +77 -0
  13. package/build/locks/engine.js.map +1 -0
  14. package/build/locks/reducer.js +1 -5
  15. package/build/locks/reducer.js.map +1 -1
  16. package/build/locks/selectors.js +6 -6
  17. package/build/locks/selectors.js.map +1 -1
  18. package/build/queried-data/get-query-parts.js +9 -4
  19. package/build/queried-data/get-query-parts.js.map +1 -1
  20. package/build/queried-data/selectors.js +3 -9
  21. package/build/queried-data/selectors.js.map +1 -1
  22. package/build/reducer.js +1 -4
  23. package/build/reducer.js.map +1 -1
  24. package/build/resolvers.js +120 -91
  25. package/build/resolvers.js.map +1 -1
  26. package/build/selectors.js +31 -11
  27. package/build/selectors.js.map +1 -1
  28. package/build/utils/if-not-resolved.js +6 -21
  29. package/build/utils/if-not-resolved.js.map +1 -1
  30. package/build/utils/index.js +8 -0
  31. package/build/utils/index.js.map +1 -1
  32. package/build/utils/is-raw-attribute.js +19 -0
  33. package/build/utils/is-raw-attribute.js.map +1 -0
  34. package/build-module/actions.js +106 -107
  35. package/build-module/actions.js.map +1 -1
  36. package/build-module/batch/default-processor.js +57 -27
  37. package/build-module/batch/default-processor.js.map +1 -1
  38. package/build-module/entities.js +19 -14
  39. package/build-module/entities.js.map +1 -1
  40. package/build-module/index.js +10 -14
  41. package/build-module/index.js.map +1 -1
  42. package/build-module/locks/actions.js +14 -68
  43. package/build-module/locks/actions.js.map +1 -1
  44. package/build-module/locks/engine.js +66 -0
  45. package/build-module/locks/engine.js.map +1 -0
  46. package/build-module/locks/reducer.js +1 -2
  47. package/build-module/locks/reducer.js.map +1 -1
  48. package/build-module/locks/selectors.js +4 -4
  49. package/build-module/locks/selectors.js.map +1 -1
  50. package/build-module/queried-data/get-query-parts.js +9 -4
  51. package/build-module/queried-data/get-query-parts.js.map +1 -1
  52. package/build-module/queried-data/selectors.js +3 -9
  53. package/build-module/queried-data/selectors.js.map +1 -1
  54. package/build-module/reducer.js +1 -3
  55. package/build-module/reducer.js.map +1 -1
  56. package/build-module/resolvers.js +94 -74
  57. package/build-module/resolvers.js.map +1 -1
  58. package/build-module/selectors.js +30 -10
  59. package/build-module/selectors.js.map +1 -1
  60. package/build-module/utils/if-not-resolved.js +6 -19
  61. package/build-module/utils/if-not-resolved.js.map +1 -1
  62. package/build-module/utils/index.js +1 -0
  63. package/build-module/utils/index.js.map +1 -1
  64. package/build-module/utils/is-raw-attribute.js +12 -0
  65. package/build-module/utils/is-raw-attribute.js.map +1 -0
  66. package/package.json +11 -12
  67. package/src/actions.js +112 -189
  68. package/src/batch/default-processor.js +57 -26
  69. package/src/batch/test/default-processor.js +53 -26
  70. package/src/entities.js +15 -16
  71. package/src/index.js +7 -10
  72. package/src/locks/actions.js +10 -61
  73. package/src/locks/engine.js +43 -0
  74. package/src/locks/reducer.js +1 -3
  75. package/src/locks/selectors.js +4 -4
  76. package/src/locks/test/engine.js +135 -0
  77. package/src/locks/test/reducer.js +1 -1
  78. package/src/locks/test/selectors.js +105 -124
  79. package/src/queried-data/get-query-parts.js +11 -6
  80. package/src/queried-data/selectors.js +2 -9
  81. package/src/queried-data/test/get-query-parts.js +1 -1
  82. package/src/queried-data/test/selectors.js +1 -0
  83. package/src/reducer.js +0 -2
  84. package/src/resolvers.js +86 -106
  85. package/src/selectors.js +113 -40
  86. package/src/test/actions.js +243 -172
  87. package/src/test/entities.js +40 -26
  88. package/src/test/resolvers.js +270 -223
  89. package/src/test/selectors.js +71 -0
  90. package/src/utils/if-not-resolved.js +8 -26
  91. package/src/utils/index.js +1 -0
  92. package/src/utils/is-raw-attribute.js +11 -0
  93. package/src/utils/test/if-not-resolved.js +28 -27
  94. package/src/utils/test/is-raw-attribute.js +22 -0
  95. package/build/controls.js +0 -44
  96. package/build/controls.js.map +0 -1
  97. package/build/locks/index.js +0 -47
  98. package/build/locks/index.js.map +0 -1
  99. package/build-module/controls.js +0 -31
  100. package/build-module/controls.js.map +0 -1
  101. package/build-module/locks/index.js +0 -4
  102. package/build-module/locks/index.js.map +0 -1
  103. package/src/controls.js +0 -31
  104. package/src/locks/index.js +0 -3
  105. package/src/locks/test/actions.js +0 -307
  106. package/src/test/integration.js +0 -264
@@ -11,6 +11,18 @@ import defaultProcessor from '../default-processor';
11
11
  jest.mock( '@wordpress/api-fetch' );
12
12
 
13
13
  describe( 'defaultProcessor', () => {
14
+ const preflightResponse = {
15
+ endpoints: [
16
+ {
17
+ args: {
18
+ requests: {
19
+ maxItems: 25,
20
+ },
21
+ },
22
+ },
23
+ ],
24
+ };
25
+
14
26
  const requests = [
15
27
  {
16
28
  path: '/v1/cricketers',
@@ -26,7 +38,12 @@ describe( 'defaultProcessor', () => {
26
38
  },
27
39
  ];
28
40
 
29
- const expectedFetchOptions = {
41
+ const expectedPreflightOptions = {
42
+ path: '/batch/v1',
43
+ method: 'OPTIONS',
44
+ };
45
+
46
+ const expectedBatchOptions = {
30
47
  path: '/batch/v1',
31
48
  method: 'POST',
32
49
  data: {
@@ -49,21 +66,26 @@ describe( 'defaultProcessor', () => {
49
66
  };
50
67
 
51
68
  it( 'handles a successful request', async () => {
52
- apiFetch.mockImplementation( async () => ( {
53
- failed: false,
54
- responses: [
55
- {
56
- status: 200,
57
- body: 'Lyon',
58
- },
59
- {
60
- status: 400,
61
- body: 'Error!',
62
- },
63
- ],
64
- } ) );
69
+ apiFetch.mockImplementation( async ( { method } ) =>
70
+ method === 'OPTIONS'
71
+ ? preflightResponse
72
+ : {
73
+ failed: false,
74
+ responses: [
75
+ {
76
+ status: 200,
77
+ body: 'Lyon',
78
+ },
79
+ {
80
+ status: 400,
81
+ body: 'Error!',
82
+ },
83
+ ],
84
+ }
85
+ );
65
86
  const results = await defaultProcessor( requests );
66
- expect( apiFetch ).toHaveBeenCalledWith( expectedFetchOptions );
87
+ expect( apiFetch ).toHaveBeenCalledWith( expectedPreflightOptions );
88
+ expect( apiFetch ).toHaveBeenCalledWith( expectedBatchOptions );
67
89
  expect( results ).toEqual( [
68
90
  { output: 'Lyon' },
69
91
  { error: 'Error!' },
@@ -71,18 +93,23 @@ describe( 'defaultProcessor', () => {
71
93
  } );
72
94
 
73
95
  it( 'handles a failed request', async () => {
74
- apiFetch.mockImplementation( async () => ( {
75
- failed: true,
76
- responses: [
77
- null,
78
- {
79
- status: 400,
80
- body: 'Error!',
81
- },
82
- ],
83
- } ) );
96
+ apiFetch.mockImplementation( async ( { method } ) =>
97
+ method === 'OPTIONS'
98
+ ? preflightResponse
99
+ : {
100
+ failed: true,
101
+ responses: [
102
+ null,
103
+ {
104
+ status: 400,
105
+ body: 'Error!',
106
+ },
107
+ ],
108
+ }
109
+ );
84
110
  const results = await defaultProcessor( requests );
85
- expect( apiFetch ).toHaveBeenCalledWith( expectedFetchOptions );
111
+ expect( apiFetch ).toHaveBeenCalledWith( expectedPreflightOptions );
112
+ expect( apiFetch ).toHaveBeenCalledWith( expectedBatchOptions );
86
113
  expect( results ).toEqual( [
87
114
  { error: undefined },
88
115
  { error: 'Error!' },
package/src/entities.js CHANGED
@@ -6,18 +6,18 @@ import { upperFirst, camelCase, map, find, get, startCase } from 'lodash';
6
6
  /**
7
7
  * WordPress dependencies
8
8
  */
9
- import { controls } from '@wordpress/data';
10
- import { apiFetch } from '@wordpress/data-controls';
9
+ import apiFetch from '@wordpress/api-fetch';
11
10
  import { __ } from '@wordpress/i18n';
12
11
 
13
12
  /**
14
13
  * Internal dependencies
15
14
  */
16
15
  import { addEntities } from './actions';
17
- import { STORE_NAME } from './name';
18
16
 
19
17
  export const DEFAULT_ENTITY_KEY = 'id';
20
18
 
19
+ const POST_RAW_ATTRIBUTES = [ 'title', 'excerpt', 'content' ];
20
+
21
21
  export const defaultEntities = [
22
22
  {
23
23
  label: __( 'Base' ),
@@ -41,6 +41,7 @@ export const defaultEntities = [
41
41
  key: 'slug',
42
42
  baseURL: '/wp/v2/types',
43
43
  baseURLParams: { context: 'edit' },
44
+ rawAttributes: POST_RAW_ATTRIBUTES,
44
45
  },
45
46
  {
46
47
  name: 'media',
@@ -115,6 +116,7 @@ export const defaultEntities = [
115
116
  baseURLParams: { context: 'edit' },
116
117
  plural: 'menuItems',
117
118
  label: __( 'Menu Item' ),
119
+ rawAttributes: [ 'title', 'content' ],
118
120
  },
119
121
  {
120
122
  name: 'menuLocation',
@@ -167,8 +169,8 @@ export const prePersistPostType = ( persistedRecord, edits ) => {
167
169
  *
168
170
  * @return {Promise} Entities promise
169
171
  */
170
- function* loadPostTypeEntities() {
171
- const postTypes = yield apiFetch( { path: '/wp/v2/types?context=edit' } );
172
+ async function loadPostTypeEntities() {
173
+ const postTypes = await apiFetch( { path: '/wp/v2/types?context=edit' } );
172
174
  return map( postTypes, ( postType, name ) => {
173
175
  const isTemplate = [ 'wp_template', 'wp_template_part' ].includes(
174
176
  name
@@ -184,6 +186,7 @@ function* loadPostTypeEntities() {
184
186
  selection: true,
185
187
  },
186
188
  mergedEdits: { meta: true },
189
+ rawAttributes: POST_RAW_ATTRIBUTES,
187
190
  getTitle: ( record ) =>
188
191
  record?.title?.rendered ||
189
192
  record?.title ||
@@ -199,8 +202,8 @@ function* loadPostTypeEntities() {
199
202
  *
200
203
  * @return {Promise} Entities promise
201
204
  */
202
- function* loadTaxonomyEntities() {
203
- const taxonomies = yield apiFetch( {
205
+ async function loadTaxonomyEntities() {
206
+ const taxonomies = await apiFetch( {
204
207
  path: '/wp/v2/taxonomies?context=edit',
205
208
  } );
206
209
  return map( taxonomies, ( taxonomy, name ) => {
@@ -248,12 +251,8 @@ export const getMethodName = (
248
251
  *
249
252
  * @return {Array} Entities
250
253
  */
251
- export function* getKindEntities( kind ) {
252
- let entities = yield controls.select(
253
- STORE_NAME,
254
- 'getEntitiesByKind',
255
- kind
256
- );
254
+ export const getKindEntities = ( kind ) => async ( { select, dispatch } ) => {
255
+ let entities = select.getEntitiesByKind( kind );
257
256
  if ( entities && entities.length !== 0 ) {
258
257
  return entities;
259
258
  }
@@ -263,8 +262,8 @@ export function* getKindEntities( kind ) {
263
262
  return [];
264
263
  }
265
264
 
266
- entities = yield kindConfig.loadEntities();
267
- yield addEntities( entities );
265
+ entities = await kindConfig.loadEntities();
266
+ dispatch( addEntities( entities ) );
268
267
 
269
268
  return entities;
270
- }
269
+ };
package/src/index.js CHANGED
@@ -2,7 +2,6 @@
2
2
  * WordPress dependencies
3
3
  */
4
4
  import { createReduxStore, register } from '@wordpress/data';
5
- import { controls } from '@wordpress/data-controls';
6
5
 
7
6
  /**
8
7
  * Internal dependencies
@@ -11,9 +10,7 @@ import reducer from './reducer';
11
10
  import * as selectors from './selectors';
12
11
  import * as actions from './actions';
13
12
  import * as resolvers from './resolvers';
14
- import * as locksSelectors from './locks/selectors';
15
- import * as locksActions from './locks/actions';
16
- import customControls from './controls';
13
+ import createLocksActions from './locks/actions';
17
14
  import { defaultEntities, getMethodName } from './entities';
18
15
  import { STORE_NAME } from './name';
19
16
 
@@ -57,13 +54,13 @@ const entityActions = defaultEntities.reduce( ( result, entity ) => {
57
54
  return result;
58
55
  }, {} );
59
56
 
60
- const storeConfig = {
57
+ const storeConfig = () => ( {
61
58
  reducer,
62
- controls: { ...customControls, ...controls },
63
- actions: { ...actions, ...entityActions, ...locksActions },
64
- selectors: { ...selectors, ...entitySelectors, ...locksSelectors },
59
+ actions: { ...actions, ...entityActions, ...createLocksActions() },
60
+ selectors: { ...selectors, ...entitySelectors },
65
61
  resolvers: { ...resolvers, ...entityResolvers },
66
- };
62
+ __experimentalUseThunks: true,
63
+ } );
67
64
 
68
65
  /**
69
66
  * Store definition for the code data namespace.
@@ -72,7 +69,7 @@ const storeConfig = {
72
69
  *
73
70
  * @type {Object}
74
71
  */
75
- export const store = createReduxStore( STORE_NAME, storeConfig );
72
+ export const store = createReduxStore( STORE_NAME, storeConfig() );
76
73
 
77
74
  register( store );
78
75
 
@@ -1,69 +1,18 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import { __unstableAwaitPromise } from '@wordpress/data-controls';
5
- import { controls } from '@wordpress/data';
6
-
7
1
  /**
8
2
  * Internal dependencies
9
3
  */
10
- import { STORE_NAME } from '../name';
4
+ import createLocks from './engine';
11
5
 
12
- export function* __unstableAcquireStoreLock( store, path, { exclusive } ) {
13
- const promise = yield* __unstableEnqueueLockRequest( store, path, {
14
- exclusive,
15
- } );
16
- yield* __unstableProcessPendingLockRequests();
17
- return yield __unstableAwaitPromise( promise );
18
- }
6
+ export default function createLocksActions() {
7
+ const locks = createLocks();
19
8
 
20
- export function* __unstableEnqueueLockRequest( store, path, { exclusive } ) {
21
- let notifyAcquired;
22
- const promise = new Promise( ( resolve ) => {
23
- notifyAcquired = resolve;
24
- } );
25
- yield {
26
- type: 'ENQUEUE_LOCK_REQUEST',
27
- request: { store, path, exclusive, notifyAcquired },
28
- };
29
- return promise;
30
- }
31
-
32
- export function* __unstableReleaseStoreLock( lock ) {
33
- yield {
34
- type: 'RELEASE_LOCK',
35
- lock,
36
- };
37
- yield* __unstableProcessPendingLockRequests();
38
- }
9
+ function __unstableAcquireStoreLock( store, path, { exclusive } ) {
10
+ return () => locks.acquire( store, path, exclusive );
11
+ }
39
12
 
40
- export function* __unstableProcessPendingLockRequests() {
41
- yield {
42
- type: 'PROCESS_PENDING_LOCK_REQUESTS',
43
- };
44
- const lockRequests = yield controls.select(
45
- STORE_NAME,
46
- '__unstableGetPendingLockRequests'
47
- );
48
- for ( const request of lockRequests ) {
49
- const { store, path, exclusive, notifyAcquired } = request;
50
- const isAvailable = yield controls.select(
51
- STORE_NAME,
52
- '__unstableIsLockAvailable',
53
- store,
54
- path,
55
- {
56
- exclusive,
57
- }
58
- );
59
- if ( isAvailable ) {
60
- const lock = { store, path, exclusive };
61
- yield {
62
- type: 'GRANT_LOCK_REQUEST',
63
- lock,
64
- request,
65
- };
66
- notifyAcquired( lock );
67
- }
13
+ function __unstableReleaseStoreLock( lock ) {
14
+ return () => locks.release( lock );
68
15
  }
16
+
17
+ return { __unstableAcquireStoreLock, __unstableReleaseStoreLock };
69
18
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import reducer from './reducer';
5
+ import { isLockAvailable, getPendingLockRequests } from './selectors';
6
+
7
+ export default function createLocks() {
8
+ let state = reducer( undefined, { type: '@@INIT' } );
9
+
10
+ function processPendingLockRequests() {
11
+ for ( const request of getPendingLockRequests( state ) ) {
12
+ const { store, path, exclusive, notifyAcquired } = request;
13
+ if ( isLockAvailable( state, store, path, { exclusive } ) ) {
14
+ const lock = { store, path, exclusive };
15
+ state = reducer( state, {
16
+ type: 'GRANT_LOCK_REQUEST',
17
+ lock,
18
+ request,
19
+ } );
20
+ notifyAcquired( lock );
21
+ }
22
+ }
23
+ }
24
+
25
+ function acquire( store, path, exclusive ) {
26
+ return new Promise( ( resolve ) => {
27
+ state = reducer( state, {
28
+ type: 'ENQUEUE_LOCK_REQUEST',
29
+ request: { store, path, exclusive, notifyAcquired: resolve },
30
+ } );
31
+ processPendingLockRequests();
32
+ } );
33
+ }
34
+ function release( lock ) {
35
+ state = reducer( state, {
36
+ type: 'RELEASE_LOCK',
37
+ lock,
38
+ } );
39
+ processPendingLockRequests();
40
+ }
41
+
42
+ return { acquire, release };
43
+ }
@@ -19,7 +19,7 @@ const DEFAULT_STATE = {
19
19
  *
20
20
  * @return {Object} Updated state.
21
21
  */
22
- export function locks( state = DEFAULT_STATE, action ) {
22
+ export default function locks( state = DEFAULT_STATE, action ) {
23
23
  switch ( action.type ) {
24
24
  case 'ENQUEUE_LOCK_REQUEST': {
25
25
  const { request } = action;
@@ -60,5 +60,3 @@ export function locks( state = DEFAULT_STATE, action ) {
60
60
 
61
61
  return state;
62
62
  }
63
-
64
- export default locks;
@@ -8,13 +8,13 @@ import {
8
8
  getNode,
9
9
  } from './utils';
10
10
 
11
- export function __unstableGetPendingLockRequests( state ) {
12
- return state.locks.requests;
11
+ export function getPendingLockRequests( state ) {
12
+ return state.requests;
13
13
  }
14
14
 
15
- export function __unstableIsLockAvailable( state, store, path, { exclusive } ) {
15
+ export function isLockAvailable( state, store, path, { exclusive } ) {
16
16
  const storePath = [ store, ...path ];
17
- const locks = state.locks.tree;
17
+ const locks = state.tree;
18
18
 
19
19
  // Validate all parents and the node itself
20
20
  for ( const node of iteratePath( locks, storePath ) ) {
@@ -0,0 +1,135 @@
1
+ /**
2
+ * Internal dependencies
3
+ */
4
+ import createLocks from '../engine';
5
+
6
+ jest.useRealTimers();
7
+
8
+ // we correctly await all promises with expect calls, but the rule doesn't detect that
9
+ /* eslint-disable jest/valid-expect-in-promise */
10
+
11
+ describe( 'Locks engine', () => {
12
+ it( 'does not grant two exclusive locks at once', async () => {
13
+ const locks = createLocks();
14
+
15
+ let l1Granted = false;
16
+ let l2Granted = false;
17
+
18
+ // request two locks
19
+ const l1 = locks.acquire( 'store', [ 'root' ], true );
20
+ const l2 = locks.acquire( 'store', [ 'root' ], true );
21
+
22
+ // on each grant, verify that the other lock is not granted at the same time
23
+ const check1 = l1.then( () => {
24
+ l1Granted = true;
25
+ expect( l2Granted ).toBe( false );
26
+ } );
27
+
28
+ const check2 = l2.then( () => {
29
+ l2Granted = true;
30
+ expect( l1Granted ).toBe( false );
31
+ } );
32
+
33
+ // unlock both
34
+ const lock1 = await l1;
35
+ locks.release( lock1 );
36
+ l1Granted = false;
37
+
38
+ const lock2 = await l2;
39
+ locks.release( lock2 );
40
+ l2Granted = false;
41
+
42
+ // ensure that both locks were granted and checked
43
+ return await Promise.all( [ check1, check2 ] );
44
+ } );
45
+
46
+ it( 'does not grant an exclusive lock if a non-exclusive one already exists', async () => {
47
+ const locks = createLocks();
48
+
49
+ let l1Granted = false;
50
+ let l2Granted = false;
51
+
52
+ // request two locks
53
+ const l1 = locks.acquire( 'store', [ 'root' ], false );
54
+ const l2 = locks.acquire( 'store', [ 'root' ], true );
55
+
56
+ // on each grant, verify that the other lock is not granted at the same time
57
+ const check1 = l1.then( () => {
58
+ l1Granted = true;
59
+ expect( l2Granted ).toBe( false );
60
+ } );
61
+
62
+ const check2 = l2.then( () => {
63
+ l2Granted = true;
64
+ expect( l1Granted ).toBe( false );
65
+ } );
66
+
67
+ // unlock both
68
+ const lock1 = await l1;
69
+ locks.release( lock1 );
70
+ l1Granted = false;
71
+
72
+ const lock2 = await l2;
73
+ locks.release( lock2 );
74
+ l2Granted = false;
75
+
76
+ // ensure that both locks were granted and checked
77
+ return await Promise.all( [ check1, check2 ] );
78
+ } );
79
+
80
+ it( 'does not grant two exclusive locks to parent and child', async () => {
81
+ const locks = createLocks();
82
+
83
+ let l1Granted = false;
84
+ let l2Granted = false;
85
+
86
+ // request two locks
87
+ const l1 = locks.acquire( 'store', [ 'root' ], true );
88
+ const l2 = locks.acquire( 'store', [ 'root', 'child' ], true );
89
+
90
+ // on each grant, verify that the other lock is not granted at the same time
91
+ const check1 = l1.then( () => {
92
+ l1Granted = true;
93
+ expect( l2Granted ).toBe( false );
94
+ } );
95
+
96
+ const check2 = l2.then( () => {
97
+ l2Granted = true;
98
+ expect( l1Granted ).toBe( false );
99
+ } );
100
+
101
+ // unlock both
102
+ const lock1 = await l1;
103
+ locks.release( lock1 );
104
+ l1Granted = false;
105
+
106
+ const lock2 = await l2;
107
+ locks.release( lock2 );
108
+ l2Granted = false;
109
+
110
+ // ensure that both locks were granted and checked
111
+ return await Promise.all( [ check1, check2 ] );
112
+ } );
113
+
114
+ it( 'grants two non-exclusive locks at once', async () => {
115
+ const locks = createLocks();
116
+
117
+ const l1 = await locks.acquire( 'store', [ 'root' ], false );
118
+ const l2 = await locks.acquire( 'store', [ 'root' ], false );
119
+
120
+ expect( l1 ).not.toBeUndefined();
121
+ expect( l2 ).not.toBeUndefined();
122
+ } );
123
+
124
+ it( 'grants two exclusive locks to different branches', async () => {
125
+ const locks = createLocks();
126
+
127
+ const l1 = await locks.acquire( 'store', [ 'a' ], true );
128
+ const l2 = await locks.acquire( 'store', [ 'b' ], true );
129
+
130
+ expect( l1 ).not.toBeUndefined();
131
+ expect( l2 ).not.toBeUndefined();
132
+ } );
133
+ } );
134
+
135
+ /* eslint-enable jest/valid-expect-in-promise */
@@ -6,7 +6,7 @@ import deepFreeze from 'deep-freeze';
6
6
  /**
7
7
  * Internal dependencies
8
8
  */
9
- import { locks } from '../reducer';
9
+ import locks from '../reducer';
10
10
 
11
11
  const buildNode = ( children = {} ) => ( {
12
12
  locks: [],