@wordpress/e2e-tests 2.4.1-next.253d9b6e21.0 → 2.5.1

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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +62 -0
  3. package/config/flaky-tests-reporter.js +94 -0
  4. package/config/setup-test-framework.js +10 -0
  5. package/jest.config.js +11 -1
  6. package/package.json +8 -7
  7. package/plugins/class-test-widget.php +5 -2
  8. package/plugins/iframed-block/block.json +16 -0
  9. package/plugins/iframed-block/editor.css +6 -0
  10. package/plugins/iframed-block/editor.js +18 -0
  11. package/plugins/iframed-block/jquery.test.js +7 -0
  12. package/plugins/iframed-block/script.js +7 -0
  13. package/plugins/iframed-block/style.css +9 -0
  14. package/plugins/iframed-block.php +46 -0
  15. package/specs/editor/blocks/buttons.test.js +30 -0
  16. package/specs/editor/blocks/post-title.test.js +1 -1
  17. package/specs/editor/plugins/__snapshots__/iframed-block.test.js.snap +7 -0
  18. package/specs/editor/plugins/align-hook.test.js +116 -105
  19. package/specs/editor/plugins/annotations.test.js +3 -3
  20. package/specs/editor/plugins/iframed-block.test.js +58 -0
  21. package/specs/editor/various/__snapshots__/copy-cut-paste-whole-blocks.test.js.snap +28 -0
  22. package/specs/editor/various/__snapshots__/rich-text.test.js.snap +15 -3
  23. package/specs/editor/various/a11y.test.js +1 -1
  24. package/specs/editor/various/block-deletion.test.js +2 -2
  25. package/specs/editor/various/block-grouping.test.js +2 -2
  26. package/specs/editor/various/block-mover.test.js +1 -1
  27. package/specs/editor/various/change-detection.test.js +2 -1
  28. package/specs/editor/various/copy-cut-paste-whole-blocks.test.js +92 -0
  29. package/specs/editor/various/embedding.test.js +1 -1
  30. package/specs/editor/various/inserting-blocks.test.js +23 -0
  31. package/specs/editor/various/keyboard-navigable-blocks.test.js +2 -2
  32. package/specs/editor/various/multi-block-selection.test.js +23 -0
  33. package/specs/editor/various/new-post.test.js +6 -3
  34. package/specs/editor/various/preview.test.js +5 -1
  35. package/specs/editor/various/rich-text.test.js +29 -1
  36. package/specs/editor/various/splitting-merging.test.js +48 -2
  37. package/specs/editor/various/writing-flow.test.js +14 -13
  38. package/specs/experiments/__snapshots__/navigation-editor.test.js.snap +27 -33
  39. package/specs/experiments/blocks/__snapshots__/navigation.test.js.snap +35 -25
  40. package/specs/experiments/blocks/navigation.test.js +93 -17
  41. package/specs/experiments/fixtures/menu-items-request-fixture.json +84 -0
  42. package/specs/experiments/navigation-editor.test.js +341 -231
  43. package/specs/experiments/template-part.test.js +6 -3
  44. package/specs/experiments/template-revert.test.js +1 -1
  45. package/specs/performance/post-editor.test.js +108 -80
  46. package/specs/performance/site-editor.test.js +2 -17
  47. package/specs/widgets/customizing-widgets.test.js +118 -7
  48. package/specs/widgets/editing-widgets.test.js +52 -88
  49. package/plugins/classic-widgets.php +0 -11
@@ -1,12 +1,21 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ // eslint-disable-next-line no-restricted-imports
5
+ import { find, findAll } from 'puppeteer-testing-library';
6
+
1
7
  /**
2
8
  * WordPress dependencies
3
9
  */
4
10
  import {
5
11
  createJSONResponse,
12
+ createMenu,
13
+ deleteAllMenus,
6
14
  pressKeyTimes,
7
15
  pressKeyWithModifier,
8
16
  setUpResponseMocking,
9
17
  visitAdminPage,
18
+ __experimentalRest as rest,
10
19
  } from '@wordpress/e2e-test-utils';
11
20
  import { addQueryArgs } from '@wordpress/url';
12
21
 
@@ -14,7 +23,7 @@ import { addQueryArgs } from '@wordpress/url';
14
23
  * Internal dependencies
15
24
  */
16
25
  import { useExperimentalFeatures } from '../../experimental-features';
17
- import menuItemsFixture from './fixtures/menu-items-response-fixture.json';
26
+ import menuItemsFixture from './fixtures/menu-items-request-fixture.json';
18
27
 
19
28
  const TYPE_NAMES = {
20
29
  post: 'post',
@@ -23,21 +32,6 @@ const TYPE_NAMES = {
23
32
  category: 'category',
24
33
  };
25
34
 
26
- const menusFixture = [
27
- {
28
- name: 'Test Menu 1',
29
- slug: 'test-menu-1',
30
- },
31
- {
32
- name: 'Test Menu 2',
33
- slug: 'test-menu-2',
34
- },
35
- {
36
- name: 'Test Menu 3',
37
- slug: 'test-menu-3',
38
- },
39
- ];
40
-
41
35
  const searchFixture = [
42
36
  {
43
37
  id: 300,
@@ -69,15 +63,6 @@ const searchFixture = [
69
63
 
70
64
  // Matching against variations of the same URL encoded and non-encoded
71
65
  // produces the most reliable mocking.
72
- const REST_MENUS_ROUTES = [
73
- '/__experimental/menus',
74
- `rest_route=${ encodeURIComponent( '/__experimental/menus' ) }`,
75
- ];
76
- const REST_MENU_ITEMS_ROUTES = [
77
- '/__experimental/menu-items',
78
- `rest_route=${ encodeURIComponent( '/__experimental/menu-items' ) }`,
79
- ];
80
-
81
66
  const REST_SEARCH_ROUTES = [
82
67
  '/wp/v2/search',
83
68
  `rest_route=${ encodeURIComponent( '/wp/v2/search' ) }`,
@@ -119,23 +104,6 @@ function getEndpointMocks( matchingRoutes, responsesByMethod ) {
119
104
  }, [] );
120
105
  }
121
106
 
122
- function assignMockMenuIds( menus ) {
123
- return menus.length
124
- ? menus.map( ( menu, index ) => ( {
125
- ...menu,
126
- id: index + 1,
127
- } ) )
128
- : [];
129
- }
130
-
131
- function getMenuMocks( responsesByMethod ) {
132
- return getEndpointMocks( REST_MENUS_ROUTES, responsesByMethod );
133
- }
134
-
135
- function getMenuItemMocks( responsesByMethod ) {
136
- return getEndpointMocks( REST_MENU_ITEMS_ROUTES, responsesByMethod );
137
- }
138
-
139
107
  function getSearchMocks( responsesByMethod ) {
140
108
  return getEndpointMocks( REST_SEARCH_ROUTES, responsesByMethod );
141
109
  }
@@ -151,51 +119,100 @@ async function visitNavigationEditor() {
151
119
  await visitAdminPage( '/admin.php', query );
152
120
  }
153
121
 
122
+ /**
123
+ * Get a list of the editor's current blocks for use in a snapshot.
124
+ *
125
+ * @return {string} Block HTML.
126
+ */
154
127
  async function getSerializedBlocks() {
155
- return page.evaluate( () =>
156
- wp.blocks.serialize( wp.data.select( 'core/block-editor' ).getBlocks() )
128
+ const blocks = await page.evaluate( () =>
129
+ wp.data.select( 'core/block-editor' ).getBlocks()
130
+ );
131
+ const safeBlocks = replaceUnstableBlockAttributes( blocks );
132
+ return page.evaluate(
133
+ ( blocksToSerialize ) => wp.blocks.serialize( blocksToSerialize ),
134
+ safeBlocks
157
135
  );
158
136
  }
159
137
 
138
+ /**
139
+ * Some block attributes contain values that are not stable from test run to
140
+ * test run and can't be compared with a snapshot. These are typically the
141
+ * ids of posts or pages that are created prior to each test run. Replace those
142
+ * values with their types before serializing to a snapshot.
143
+ *
144
+ * @param {Array} blocks The array of block data.
145
+ *
146
+ * @return {Array} Updated array of block data.
147
+ */
148
+ function replaceUnstableBlockAttributes( blocks ) {
149
+ return blocks?.map( ( block ) => {
150
+ const id = block.attributes.id ? typeof block.attributes.id : undefined;
151
+ const url = block.attributes.url
152
+ ? typeof block.attributes.url
153
+ : undefined;
154
+
155
+ return {
156
+ ...block,
157
+ attributes: {
158
+ ...block.attributes,
159
+ id,
160
+ url,
161
+ },
162
+ innerBlocks: replaceUnstableBlockAttributes( block.innerBlocks ),
163
+ };
164
+ } );
165
+ }
166
+
167
+ async function deleteAllLinkedResources() {
168
+ [ '/wp/v2/posts', '/wp/v2/pages' ].forEach( async ( path ) => {
169
+ const items = await rest( { path } );
170
+
171
+ for ( const item of items ) {
172
+ await rest( {
173
+ method: 'DELETE',
174
+ path: `${ path }/${ item.id }?force=true`,
175
+ } );
176
+ }
177
+ } );
178
+ }
179
+
160
180
  describe( 'Navigation editor', () => {
161
181
  useExperimentalFeatures( [ '#gutenberg-navigation' ] );
162
182
 
183
+ beforeAll( async () => {
184
+ await deleteAllMenus();
185
+ await deleteAllLinkedResources();
186
+ } );
187
+
163
188
  afterEach( async () => {
189
+ await deleteAllMenus();
190
+ await deleteAllLinkedResources();
164
191
  await setUpResponseMocking( [] );
165
192
  } );
166
193
 
167
194
  it( 'allows creation of a menu when there are no current menu items', async () => {
168
- const menuPostResponse = {
169
- id: 4,
170
- description: '',
171
- name: 'Main Menu',
172
- slug: 'main-menu',
173
- meta: [],
174
- auto_add: false,
175
- };
176
-
177
- // Initially return nothing from the menu and menuItem endpoints
195
+ await visitNavigationEditor();
178
196
  await setUpResponseMocking( [
179
- ...getMenuMocks( { GET: [] } ),
180
- ...getMenuItemMocks( { GET: [] } ),
197
+ ...getPagesMocks( {
198
+ GET: [
199
+ {
200
+ type: 'page',
201
+ id: 1,
202
+ link: 'https://example.com/1',
203
+ title: {
204
+ rendered: 'My page',
205
+ },
206
+ },
207
+ ],
208
+ } ),
181
209
  ] );
182
- await visitNavigationEditor();
183
210
 
184
211
  // Wait for the header to show that no menus are available.
185
212
  await page.waitForXPath( '//h3[.="Create your first menu"]', {
186
213
  visible: true,
187
214
  } );
188
215
 
189
- // Prepare the menu endpoint for creating a menu.
190
- await setUpResponseMocking( [
191
- ...getMenuMocks( {
192
- GET: [ menuPostResponse ],
193
- POST: menuPostResponse,
194
- } ),
195
- ...getMenuItemMocks( { GET: [] } ),
196
- ...getPagesMocks( { GET: [ {} ] } ), // mock a single page
197
- ] );
198
-
199
216
  await page.keyboard.type( 'Main Menu' );
200
217
  const createMenuButton = await page.waitForXPath(
201
218
  '//button[contains(., "Create menu")]'
@@ -224,37 +241,17 @@ describe( 'Navigation editor', () => {
224
241
  } );
225
242
 
226
243
  it( 'allows creation of a menu when there are existing menu items', async () => {
227
- const menuPostResponse = {
228
- id: 4,
229
- description: '',
230
- name: 'New Menu',
231
- slug: 'new-menu',
232
- meta: [],
233
- auto_add: false,
234
- };
235
-
236
- await setUpResponseMocking( [
237
- ...getMenuMocks( {
238
- GET: assignMockMenuIds( menusFixture ),
239
- POST: menuPostResponse,
240
- } ),
241
- ...getMenuItemMocks( { GET: menuItemsFixture } ),
242
- ] );
244
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
245
+ await createMenu( { name: 'Test Menu 2' }, menuItemsFixture );
243
246
  await visitNavigationEditor();
244
247
 
245
248
  // Wait for the header to show the menu name.
246
- await page.waitForXPath( '//h2[contains(., "Editing: Test Menu 1")]', {
249
+ await page.waitForXPath( '//h2[contains(., "Test Menu 1")]', {
247
250
  visible: true,
248
251
  } );
249
252
 
250
- // Open up the menu creation dialog and create a new menu.
251
- const switchMenuButton = await page.waitForXPath(
252
- '//button[.="Switch menu"]'
253
- );
254
- await switchMenuButton.click();
255
-
256
253
  const createMenuButton = await page.waitForXPath(
257
- '//button[.="Create a new menu"]'
254
+ '//button[.="New menu"]'
258
255
  );
259
256
  await createMenuButton.click();
260
257
 
@@ -263,17 +260,6 @@ describe( 'Navigation editor', () => {
263
260
  );
264
261
  await menuNameInputLabel.click();
265
262
 
266
- await setUpResponseMocking( [
267
- ...getMenuMocks( {
268
- GET: assignMockMenuIds( [
269
- ...menusFixture,
270
- { name: 'New menu', slug: 'new-menu' },
271
- ] ),
272
- POST: menuPostResponse,
273
- } ),
274
- ...getMenuItemMocks( { GET: [] } ),
275
- ] );
276
-
277
263
  await page.keyboard.type( 'New menu' );
278
264
  await page.keyboard.press( 'Enter' );
279
265
 
@@ -286,15 +272,14 @@ describe( 'Navigation editor', () => {
286
272
  expect( await getSerializedBlocks() ).toMatchSnapshot();
287
273
  } );
288
274
 
289
- it( 'displays the first menu from the REST response when at least one menu exists', async () => {
290
- await setUpResponseMocking( [
291
- ...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
292
- ...getMenuItemMocks( { GET: menuItemsFixture } ),
293
- ] );
275
+ it( 'displays the first created menu when at least one menu exists', async () => {
276
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
277
+ await createMenu( { name: 'Test Menu 2' }, menuItemsFixture );
278
+
294
279
  await visitNavigationEditor();
295
280
 
296
281
  // Wait for the header to show the menu name.
297
- await page.waitForXPath( '//h2[contains(., "Editing: Test Menu 1")]', {
282
+ await page.waitForXPath( '//h2[contains(., "Test Menu 1")]', {
298
283
  visible: true,
299
284
  } );
300
285
 
@@ -304,16 +289,48 @@ describe( 'Navigation editor', () => {
304
289
  expect( await getSerializedBlocks() ).toMatchSnapshot();
305
290
  } );
306
291
 
292
+ it( 'shows the trailing block appender within the navigation block when no blocks are selected', async () => {
293
+ // The test requires the presence of existing menus.
294
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
295
+ await visitNavigationEditor();
296
+
297
+ // Wait for at least one block to be present on the page.
298
+ await page.waitForSelector( '.wp-block' );
299
+
300
+ // And for this test to be valid, no blocks should be selected, which
301
+ // should be the case when the editor loads.
302
+ const selectedBlocks = await page.$$( '.wp-block.is-selected' );
303
+ expect( selectedBlocks.length ).toBe( 0 );
304
+
305
+ // And when no blocks are selected, the trailing appender is present.
306
+ const blockListAppender = await page.waitForSelector(
307
+ '.block-list-appender button[aria-label="Add block"]'
308
+ );
309
+ expect( blockListAppender ).toBeTruthy();
310
+ } );
311
+
312
+ it( 'has a disabled undo button when an existing menu is loaded', async () => {
313
+ // The test requires the presence of existing menus.
314
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
315
+ await visitNavigationEditor();
316
+
317
+ // Wait for at least one block to be present on the page.
318
+ await page.waitForSelector( '.wp-block' );
319
+
320
+ // Check whether there's a disabled undo button.
321
+ const disabledUndoButton = await page.waitForSelector(
322
+ 'button[aria-label="Undo"][aria-disabled="true"]'
323
+ );
324
+ expect( disabledUndoButton ).toBeTruthy();
325
+ } );
326
+
307
327
  it( 'shows a submenu when a link is selected and hides it when clicking the editor to deselect it', async () => {
308
- await setUpResponseMocking( [
309
- ...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
310
- ...getMenuItemMocks( { GET: menuItemsFixture } ),
311
- ] );
328
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
312
329
  await visitNavigationEditor();
313
330
 
314
- // Select a link block with nested links in a submenu.
331
+ // Select a submenu block with nested links in a submenu.
315
332
  const parentLinkXPath =
316
- '//div[@aria-label="Block: Custom Link" and contains(.,"WordPress.org")]';
333
+ '//div[@aria-label="Block: Submenu" and contains(.,"WordPress.org")]';
317
334
  const linkBlock = await page.waitForXPath( parentLinkXPath );
318
335
  await linkBlock.click();
319
336
 
@@ -341,8 +358,8 @@ describe( 'Navigation editor', () => {
341
358
  } );
342
359
 
343
360
  it( 'displays suggestions when adding a link', async () => {
361
+ await createMenu( { name: 'Test Menu 1' } );
344
362
  await setUpResponseMocking( [
345
- ...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
346
363
  ...getSearchMocks( { GET: searchFixture } ),
347
364
  ] );
348
365
 
@@ -354,7 +371,7 @@ describe( 'Navigation editor', () => {
354
371
  );
355
372
  await navBlock.click();
356
373
  const startEmptyButton = await page.waitForXPath(
357
- '//button[.="Start empty"]'
374
+ '//button[.="Start blank"]'
358
375
  );
359
376
  await startEmptyButton.click();
360
377
 
@@ -363,11 +380,6 @@ describe( 'Navigation editor', () => {
363
380
  );
364
381
  await appender.click();
365
382
 
366
- const linkInserterItem = await page.waitForXPath(
367
- '//button[@role="option"]//span[.="Custom Link"]'
368
- );
369
- await linkInserterItem.click();
370
-
371
383
  await page.waitForSelector( 'input[aria-label="URL"]' );
372
384
 
373
385
  // The link suggestions should be searchable.
@@ -398,144 +410,72 @@ describe( 'Navigation editor', () => {
398
410
  const nameEditorSelector = '.edit-navigation-name-editor__text-control';
399
411
  const inputSelector = `${ nameEditorSelector } input`;
400
412
 
401
- beforeEach( async () => {
402
- const menuPostResponse = {
403
- id: 4,
404
- description: '',
405
- name: initialMenuName,
406
- slug: 'main-menu',
407
- meta: [],
408
- auto_add: false,
409
- };
410
-
411
- await setUpResponseMocking( [
412
- ...getMenuMocks( {
413
- GET: [ menuPostResponse ],
414
- POST: menuPostResponse,
415
- } ),
416
- ...getMenuItemMocks( { GET: [] } ),
417
- ] );
418
-
413
+ it( 'saves menu name changes', async () => {
414
+ await createMenu( { name: initialMenuName } );
419
415
  await visitNavigationEditor();
420
416
 
421
- // Wait for the navigation setting sidebar.
422
- await page.waitForSelector( '.edit-navigation-sidebar' );
423
- } );
424
-
425
- afterEach( async () => {
426
- await setUpResponseMocking( [] );
427
- } );
428
-
429
- it( 'is displayed in inspector additions', async () => {
430
- const nameControl = await page.$( nameEditorSelector );
431
- expect( nameControl ).toBeDefined();
432
- } );
433
-
434
- it( 'saves menu name upon clicking save button', async () => {
435
- const newName = 'newName';
436
- const menuPostResponse = {
437
- id: 4,
438
- description: '',
439
- name: newName,
440
- slug: 'main-menu',
441
- meta: [],
442
- auto_add: false,
443
- };
444
-
445
- await setUpResponseMocking( [
446
- ...getMenuMocks( {
447
- GET: [ menuPostResponse ],
448
- POST: menuPostResponse,
449
- } ),
450
- ...getMenuItemMocks( { GET: [] } ),
451
- ] );
452
-
453
- // Ensure there is focus.
454
- await page.focus( inputSelector );
417
+ // Rename the menu and save it.
418
+ const newName = 'New menu';
419
+ await page.waitForSelector( inputSelector );
420
+ await page.click( inputSelector );
455
421
  await pressKeyTimes( 'Backspace', initialMenuName.length );
456
422
  await page.keyboard.type( newName );
457
-
458
- const saveButton = await page.$(
459
- '.edit-navigation-toolbar__save-button'
460
- );
461
- await saveButton.click();
423
+ await page.click( '.edit-navigation-toolbar__save-button' );
462
424
  await page.waitForSelector( '.components-snackbar' );
425
+ await page.reload();
426
+
427
+ // Expect the header to have the new name.
463
428
  const headerSubtitle = await page.waitForSelector(
464
- '.edit-navigation-header__subtitle'
429
+ '.edit-navigation-menu-actions__subtitle'
465
430
  );
466
- expect( headerSubtitle ).toBeTruthy();
467
431
  const headerSubtitleText = await headerSubtitle.evaluate(
468
432
  ( element ) => element.innerText
469
433
  );
470
- expect( headerSubtitleText ).toBe( `Editing: ${ newName }` );
434
+ expect( headerSubtitleText ).toBe( newName );
471
435
  } );
472
436
 
473
- it( 'does not save a menu name upon clicking save button when name is empty', async () => {
474
- const menuPostResponse = {
475
- id: 4,
476
- description: '',
477
- name: initialMenuName,
478
- slug: 'main-menu',
479
- meta: [],
480
- auto_add: false,
481
- };
482
-
483
- await setUpResponseMocking( [
484
- ...getMenuMocks( {
485
- GET: [ menuPostResponse ],
486
- POST: menuPostResponse,
487
- } ),
488
- ...getMenuItemMocks( { GET: [] } ),
489
- ] );
437
+ // Flaky test, see https://github.com/WordPress/gutenberg/pull/34869#issuecomment-922711557.
438
+ it.skip( 'does not save a menu name upon clicking save button when name is empty', async () => {
439
+ await createMenu( { name: initialMenuName } );
440
+ await visitNavigationEditor();
490
441
 
491
- // Ensure there is focus.
492
- await page.focus( inputSelector );
442
+ // Try saving a menu with an empty name.
443
+ await page.waitForSelector( inputSelector );
444
+ await page.click( inputSelector );
493
445
  await pressKeyTimes( 'Backspace', initialMenuName.length );
494
-
495
- const saveButton = await page.$(
496
- '.edit-navigation-toolbar__save-button'
446
+ await page.click( '.edit-navigation-toolbar__save-button' );
447
+ const snackbar = await page.waitForSelector(
448
+ '.components-snackbar',
449
+ { visible: true }
497
450
  );
498
- await saveButton.click();
499
- await page.waitForSelector( '.components-snackbar' );
451
+ const snackbarText = await snackbar.evaluate(
452
+ ( element ) => element.innerText
453
+ );
454
+ expect( snackbarText ).toBe(
455
+ "Unable to save: 'A name is required for this term.'"
456
+ );
457
+ expect( console ).toHaveErrored(
458
+ 'Failed to load resource: the server responded with a status of 500 (Internal Server Error)'
459
+ );
460
+ await page.reload();
461
+
462
+ // Expect the header to have the old name.
500
463
  const headerSubtitle = await page.waitForSelector(
501
- '.edit-navigation-header__subtitle'
464
+ '.edit-navigation-menu-actions__subtitle'
502
465
  );
503
- expect( headerSubtitle ).toBeTruthy();
504
466
  const headerSubtitleText = await headerSubtitle.evaluate(
505
467
  ( element ) => element.innerText
506
468
  );
507
- expect( headerSubtitleText ).toBe(
508
- `Editing: ${ initialMenuName }`
509
- );
469
+ expect( headerSubtitleText ).toBe( initialMenuName );
510
470
  } );
511
471
  } );
512
472
 
513
473
  describe( 'Change detections', () => {
514
474
  beforeEach( async () => {
515
- const menuPostResponse = {
516
- id: 4,
517
- description: '',
518
- name: 'Main',
519
- slug: 'main-menu',
520
- meta: [],
521
- auto_add: false,
522
- };
523
-
524
- await setUpResponseMocking( [
525
- ...getMenuMocks( {
526
- GET: [ menuPostResponse ],
527
- POST: menuPostResponse,
528
- } ),
529
- ...getMenuItemMocks( { GET: [] } ),
530
- ] );
531
-
475
+ await createMenu( { name: 'Main' } );
532
476
  await visitNavigationEditor();
533
477
  } );
534
478
 
535
- afterEach( async () => {
536
- await setUpResponseMocking( [] );
537
- } );
538
-
539
479
  async function assertIsDirty( isDirty ) {
540
480
  let hadDialog = false;
541
481
 
@@ -556,10 +496,12 @@ describe( 'Navigation editor', () => {
556
496
  }
557
497
  }
558
498
 
499
+ // eslint-disable-next-line jest/no-disabled-tests
559
500
  it.skip( 'should not prompt to confirm unsaved changes for the newly selected menu', async () => {
560
501
  await assertIsDirty( false );
561
502
  } );
562
503
 
504
+ // eslint-disable-next-line jest/no-disabled-tests
563
505
  it.skip( 'should prompt to confirm unsaved changes when menu name is edited', async () => {
564
506
  await page.type(
565
507
  '.edit-navigation-name-editor__text-control input',
@@ -569,4 +511,172 @@ describe( 'Navigation editor', () => {
569
511
  await assertIsDirty( true );
570
512
  } );
571
513
  } );
514
+
515
+ describe( 'Sidebar inserter', () => {
516
+ it( 'disables inserter toggle when Navigation block is in placeholder state', async () => {
517
+ await createMenu( { name: 'Main Menu' } );
518
+ await visitNavigationEditor();
519
+
520
+ // Wait for the block to be present.
521
+ await expect( {
522
+ role: 'document',
523
+ name: 'Block: Navigation',
524
+ } ).toBeFound();
525
+
526
+ // Check for the placeholder state
527
+ await expect( {
528
+ role: 'button',
529
+ name: 'Start blank',
530
+ } ).toBeFound();
531
+
532
+ // Expect the block inserter to be disabled.
533
+ await expect( {
534
+ name: 'Toggle block inserter',
535
+ disabled: true,
536
+ role: 'button',
537
+ } ).toBeFound();
538
+ } );
539
+
540
+ it( 'enables inserter toggle when Navigation block is in editable state', async () => {
541
+ await createMenu( { name: 'Main Menu' }, menuItemsFixture );
542
+ await visitNavigationEditor();
543
+
544
+ // Wait for the block to be present.
545
+ await expect( {
546
+ role: 'document',
547
+ name: 'Block: Navigation',
548
+ } ).toBeFound();
549
+
550
+ // Expect the block inserter to be found.
551
+ await expect( {
552
+ name: 'Toggle block inserter',
553
+ role: 'button',
554
+ } ).toBeFound();
555
+
556
+ // Work around bug where `find` with `disabled=false` doesn't return anything.
557
+ const isEnabled = await page.$eval(
558
+ '[aria-label="Toggle block inserter"]',
559
+ ( element ) => ! element.disabled
560
+ );
561
+
562
+ expect( isEnabled ).toBeTruthy();
563
+ } );
564
+
565
+ it( 'toggles the inserter sidebar open and closed', async () => {
566
+ await createMenu( { name: 'Main Menu' }, menuItemsFixture );
567
+ await visitNavigationEditor();
568
+
569
+ // Wait for the block to be present.
570
+ await expect( {
571
+ role: 'document',
572
+ name: 'Block: Navigation',
573
+ } ).toBeFound();
574
+
575
+ // Expect inserter sidebar to **not** be in the DOM.
576
+ await expect( {
577
+ role: 'region',
578
+ name: 'Block library',
579
+ } ).not.toBeFound();
580
+
581
+ const inserterToggle = await find( {
582
+ name: 'Toggle block inserter',
583
+ role: 'button',
584
+ } );
585
+
586
+ await inserterToggle.click();
587
+
588
+ // Expect the inserter sidebar to be present in the DOM.
589
+ await expect( {
590
+ role: 'region',
591
+ name: 'Block library',
592
+ } ).toBeFound();
593
+
594
+ // Expect block search input to be focused.
595
+ await expect( {
596
+ role: 'searchbox',
597
+ name: 'Search for blocks and patterns',
598
+ focused: true,
599
+ } ).toBeFound();
600
+ } );
601
+
602
+ it( 'inserts items at end of Navigation block by default', async () => {
603
+ await setUpResponseMocking( [
604
+ ...getSearchMocks( { GET: searchFixture } ),
605
+ ] );
606
+ await createMenu( { name: 'Main Menu' }, menuItemsFixture );
607
+
608
+ await visitNavigationEditor();
609
+
610
+ // Wait for the block to be present.
611
+ await expect( {
612
+ role: 'document',
613
+ name: 'Block: Navigation',
614
+ } ).toBeFound();
615
+
616
+ const inserterToggle = await find( {
617
+ name: 'Toggle block inserter',
618
+ role: 'button',
619
+ } );
620
+
621
+ await inserterToggle.click();
622
+
623
+ // Expect the inserter sidebar to be present in the DOM.
624
+ await expect( {
625
+ role: 'region',
626
+ name: 'Block library',
627
+ } ).toBeFound();
628
+
629
+ // Add Custom Link item.
630
+ const customLinkOption = await find( {
631
+ name: 'Custom Link',
632
+ role: 'option',
633
+ } );
634
+
635
+ customLinkOption.click();
636
+
637
+ // Expect that inserter is auto-closed.
638
+ await expect( {
639
+ role: 'region',
640
+ name: 'Block library',
641
+ } ).not.toBeFound();
642
+
643
+ // Expect to be focused inside the Link UI search input.
644
+ await expect( {
645
+ role: 'combobox',
646
+ name: 'URL',
647
+ focused: true,
648
+ } ).toBeFound();
649
+
650
+ const [ itemToSelect ] = searchFixture;
651
+
652
+ // Add Custom Link item.
653
+ const [ firstSearchSuggestion ] = await findAll( {
654
+ role: 'option',
655
+ name: `${ itemToSelect.title } ${ itemToSelect.subtype }`,
656
+ } );
657
+
658
+ await firstSearchSuggestion.click();
659
+
660
+ // Get the title/label of the last Nav item inside the Nav block.
661
+ const lastItemAttributes = await page.evaluate( () => {
662
+ const { getBlockOrder, getBlocks } = wp.data.select(
663
+ 'core/block-editor'
664
+ );
665
+
666
+ const lockedNavigationBlock = getBlockOrder()[ 0 ];
667
+
668
+ const navItemBlocks = getBlocks( lockedNavigationBlock );
669
+
670
+ const { attributes } = navItemBlocks[
671
+ navItemBlocks.length - 1
672
+ ];
673
+
674
+ return attributes;
675
+ } );
676
+
677
+ // Check the last item is the one we just inserted
678
+ expect( lastItemAttributes.label ).toEqual( itemToSelect.title );
679
+ expect( lastItemAttributes.isTopLevelLink ).toBeTruthy();
680
+ } );
681
+ } );
572
682
  } );