@wordpress/e2e-tests 2.5.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 (32) hide show
  1. package/README.md +2 -2
  2. package/config/flaky-tests-reporter.js +94 -0
  3. package/config/setup-test-framework.js +7 -0
  4. package/jest.config.js +11 -1
  5. package/package.json +6 -5
  6. package/plugins/iframed-block/block.json +16 -0
  7. package/plugins/iframed-block/editor.css +6 -0
  8. package/plugins/iframed-block/editor.js +18 -0
  9. package/plugins/iframed-block/jquery.test.js +7 -0
  10. package/plugins/iframed-block/script.js +7 -0
  11. package/plugins/iframed-block/style.css +9 -0
  12. package/plugins/iframed-block.php +46 -0
  13. package/specs/editor/plugins/__snapshots__/iframed-block.test.js.snap +7 -0
  14. package/specs/editor/plugins/align-hook.test.js +116 -105
  15. package/specs/editor/plugins/iframed-block.test.js +58 -0
  16. package/specs/editor/various/__snapshots__/copy-cut-paste-whole-blocks.test.js.snap +28 -0
  17. package/specs/editor/various/__snapshots__/rich-text.test.js.snap +15 -3
  18. package/specs/editor/various/block-grouping.test.js +2 -2
  19. package/specs/editor/various/copy-cut-paste-whole-blocks.test.js +92 -0
  20. package/specs/editor/various/embedding.test.js +1 -1
  21. package/specs/editor/various/inserting-blocks.test.js +23 -0
  22. package/specs/editor/various/rich-text.test.js +29 -1
  23. package/specs/editor/various/writing-flow.test.js +4 -2
  24. package/specs/experiments/__snapshots__/navigation-editor.test.js.snap +27 -33
  25. package/specs/experiments/blocks/__snapshots__/navigation.test.js.snap +29 -19
  26. package/specs/experiments/blocks/navigation.test.js +93 -17
  27. package/specs/experiments/fixtures/menu-items-request-fixture.json +84 -0
  28. package/specs/experiments/navigation-editor.test.js +332 -233
  29. package/specs/experiments/template-revert.test.js +1 -1
  30. package/specs/performance/site-editor.test.js +2 -17
  31. package/specs/widgets/customizing-widgets.test.js +94 -1
  32. package/specs/widgets/editing-widgets.test.js +6 -0
@@ -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,48 +119,81 @@ 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()
157
130
  );
131
+ const safeBlocks = replaceUnstableBlockAttributes( blocks );
132
+ return page.evaluate(
133
+ ( blocksToSerialize ) => wp.blocks.serialize( blocksToSerialize ),
134
+ safeBlocks
135
+ );
136
+ }
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
+ } );
158
178
  }
159
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
178
- await setUpResponseMocking( [
179
- ...getMenuMocks( { GET: [] } ),
180
- ...getMenuItemMocks( { GET: [] } ),
181
- ] );
182
195
  await visitNavigationEditor();
183
-
184
- // Wait for the header to show that no menus are available.
185
- await page.waitForXPath( '//h3[.="Create your first menu"]', {
186
- visible: true,
187
- } );
188
-
189
- // Prepare the menu endpoint for creating a menu.
190
196
  await setUpResponseMocking( [
191
- ...getMenuMocks( {
192
- GET: [ menuPostResponse ],
193
- POST: menuPostResponse,
194
- } ),
195
- ...getMenuItemMocks( { GET: [] } ),
196
197
  ...getPagesMocks( {
197
198
  GET: [
198
199
  {
@@ -207,6 +208,11 @@ describe( 'Navigation editor', () => {
207
208
  } ),
208
209
  ] );
209
210
 
211
+ // Wait for the header to show that no menus are available.
212
+ await page.waitForXPath( '//h3[.="Create your first menu"]', {
213
+ visible: true,
214
+ } );
215
+
210
216
  await page.keyboard.type( 'Main Menu' );
211
217
  const createMenuButton = await page.waitForXPath(
212
218
  '//button[contains(., "Create menu")]'
@@ -235,37 +241,17 @@ describe( 'Navigation editor', () => {
235
241
  } );
236
242
 
237
243
  it( 'allows creation of a menu when there are existing menu items', async () => {
238
- const menuPostResponse = {
239
- id: 4,
240
- description: '',
241
- name: 'New Menu',
242
- slug: 'new-menu',
243
- meta: [],
244
- auto_add: false,
245
- };
246
-
247
- await setUpResponseMocking( [
248
- ...getMenuMocks( {
249
- GET: assignMockMenuIds( menusFixture ),
250
- POST: menuPostResponse,
251
- } ),
252
- ...getMenuItemMocks( { GET: menuItemsFixture } ),
253
- ] );
244
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
245
+ await createMenu( { name: 'Test Menu 2' }, menuItemsFixture );
254
246
  await visitNavigationEditor();
255
247
 
256
248
  // Wait for the header to show the menu name.
257
- await page.waitForXPath( '//h2[contains(., "Editing: Test Menu 1")]', {
249
+ await page.waitForXPath( '//h2[contains(., "Test Menu 1")]', {
258
250
  visible: true,
259
251
  } );
260
252
 
261
- // Open up the menu creation dialog and create a new menu.
262
- const switchMenuButton = await page.waitForXPath(
263
- '//button[.="Switch menu"]'
264
- );
265
- await switchMenuButton.click();
266
-
267
253
  const createMenuButton = await page.waitForXPath(
268
- '//button[.="Create a new menu"]'
254
+ '//button[.="New menu"]'
269
255
  );
270
256
  await createMenuButton.click();
271
257
 
@@ -274,17 +260,6 @@ describe( 'Navigation editor', () => {
274
260
  );
275
261
  await menuNameInputLabel.click();
276
262
 
277
- await setUpResponseMocking( [
278
- ...getMenuMocks( {
279
- GET: assignMockMenuIds( [
280
- ...menusFixture,
281
- { name: 'New menu', slug: 'new-menu' },
282
- ] ),
283
- POST: menuPostResponse,
284
- } ),
285
- ...getMenuItemMocks( { GET: [] } ),
286
- ] );
287
-
288
263
  await page.keyboard.type( 'New menu' );
289
264
  await page.keyboard.press( 'Enter' );
290
265
 
@@ -297,15 +272,14 @@ describe( 'Navigation editor', () => {
297
272
  expect( await getSerializedBlocks() ).toMatchSnapshot();
298
273
  } );
299
274
 
300
- it( 'displays the first menu from the REST response when at least one menu exists', async () => {
301
- await setUpResponseMocking( [
302
- ...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
303
- ...getMenuItemMocks( { GET: menuItemsFixture } ),
304
- ] );
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
+
305
279
  await visitNavigationEditor();
306
280
 
307
281
  // Wait for the header to show the menu name.
308
- await page.waitForXPath( '//h2[contains(., "Editing: Test Menu 1")]', {
282
+ await page.waitForXPath( '//h2[contains(., "Test Menu 1")]', {
309
283
  visible: true,
310
284
  } );
311
285
 
@@ -315,16 +289,48 @@ describe( 'Navigation editor', () => {
315
289
  expect( await getSerializedBlocks() ).toMatchSnapshot();
316
290
  } );
317
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
+
318
327
  it( 'shows a submenu when a link is selected and hides it when clicking the editor to deselect it', async () => {
319
- await setUpResponseMocking( [
320
- ...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
321
- ...getMenuItemMocks( { GET: menuItemsFixture } ),
322
- ] );
328
+ await createMenu( { name: 'Test Menu 1' }, menuItemsFixture );
323
329
  await visitNavigationEditor();
324
330
 
325
- // Select a link block with nested links in a submenu.
331
+ // Select a submenu block with nested links in a submenu.
326
332
  const parentLinkXPath =
327
- '//div[@aria-label="Block: Custom Link" and contains(.,"WordPress.org")]';
333
+ '//div[@aria-label="Block: Submenu" and contains(.,"WordPress.org")]';
328
334
  const linkBlock = await page.waitForXPath( parentLinkXPath );
329
335
  await linkBlock.click();
330
336
 
@@ -352,8 +358,8 @@ describe( 'Navigation editor', () => {
352
358
  } );
353
359
 
354
360
  it( 'displays suggestions when adding a link', async () => {
361
+ await createMenu( { name: 'Test Menu 1' } );
355
362
  await setUpResponseMocking( [
356
- ...getMenuMocks( { GET: assignMockMenuIds( menusFixture ) } ),
357
363
  ...getSearchMocks( { GET: searchFixture } ),
358
364
  ] );
359
365
 
@@ -374,11 +380,6 @@ describe( 'Navigation editor', () => {
374
380
  );
375
381
  await appender.click();
376
382
 
377
- const linkInserterItem = await page.waitForXPath(
378
- '//button[@role="option"]//span[.="Custom Link"]'
379
- );
380
- await linkInserterItem.click();
381
-
382
383
  await page.waitForSelector( 'input[aria-label="URL"]' );
383
384
 
384
385
  // The link suggestions should be searchable.
@@ -409,144 +410,72 @@ describe( 'Navigation editor', () => {
409
410
  const nameEditorSelector = '.edit-navigation-name-editor__text-control';
410
411
  const inputSelector = `${ nameEditorSelector } input`;
411
412
 
412
- beforeEach( async () => {
413
- const menuPostResponse = {
414
- id: 4,
415
- description: '',
416
- name: initialMenuName,
417
- slug: 'main-menu',
418
- meta: [],
419
- auto_add: false,
420
- };
421
-
422
- await setUpResponseMocking( [
423
- ...getMenuMocks( {
424
- GET: [ menuPostResponse ],
425
- POST: menuPostResponse,
426
- } ),
427
- ...getMenuItemMocks( { GET: [] } ),
428
- ] );
429
-
413
+ it( 'saves menu name changes', async () => {
414
+ await createMenu( { name: initialMenuName } );
430
415
  await visitNavigationEditor();
431
416
 
432
- // Wait for the navigation setting sidebar.
433
- await page.waitForSelector( '.edit-navigation-sidebar' );
434
- } );
435
-
436
- afterEach( async () => {
437
- await setUpResponseMocking( [] );
438
- } );
439
-
440
- it( 'is displayed in inspector additions', async () => {
441
- const nameControl = await page.$( nameEditorSelector );
442
- expect( nameControl ).toBeDefined();
443
- } );
444
-
445
- it( 'saves menu name upon clicking save button', async () => {
446
- const newName = 'newName';
447
- const menuPostResponse = {
448
- id: 4,
449
- description: '',
450
- name: newName,
451
- slug: 'main-menu',
452
- meta: [],
453
- auto_add: false,
454
- };
455
-
456
- await setUpResponseMocking( [
457
- ...getMenuMocks( {
458
- GET: [ menuPostResponse ],
459
- POST: menuPostResponse,
460
- } ),
461
- ...getMenuItemMocks( { GET: [] } ),
462
- ] );
463
-
464
- // Ensure there is focus.
465
- 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 );
466
421
  await pressKeyTimes( 'Backspace', initialMenuName.length );
467
422
  await page.keyboard.type( newName );
468
-
469
- const saveButton = await page.$(
470
- '.edit-navigation-toolbar__save-button'
471
- );
472
- await saveButton.click();
423
+ await page.click( '.edit-navigation-toolbar__save-button' );
473
424
  await page.waitForSelector( '.components-snackbar' );
425
+ await page.reload();
426
+
427
+ // Expect the header to have the new name.
474
428
  const headerSubtitle = await page.waitForSelector(
475
- '.edit-navigation-header__subtitle'
429
+ '.edit-navigation-menu-actions__subtitle'
476
430
  );
477
- expect( headerSubtitle ).toBeTruthy();
478
431
  const headerSubtitleText = await headerSubtitle.evaluate(
479
432
  ( element ) => element.innerText
480
433
  );
481
- expect( headerSubtitleText ).toBe( `Editing: ${ newName }` );
434
+ expect( headerSubtitleText ).toBe( newName );
482
435
  } );
483
436
 
484
- it( 'does not save a menu name upon clicking save button when name is empty', async () => {
485
- const menuPostResponse = {
486
- id: 4,
487
- description: '',
488
- name: initialMenuName,
489
- slug: 'main-menu',
490
- meta: [],
491
- auto_add: false,
492
- };
493
-
494
- await setUpResponseMocking( [
495
- ...getMenuMocks( {
496
- GET: [ menuPostResponse ],
497
- POST: menuPostResponse,
498
- } ),
499
- ...getMenuItemMocks( { GET: [] } ),
500
- ] );
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();
501
441
 
502
- // Ensure there is focus.
503
- await page.focus( inputSelector );
442
+ // Try saving a menu with an empty name.
443
+ await page.waitForSelector( inputSelector );
444
+ await page.click( inputSelector );
504
445
  await pressKeyTimes( 'Backspace', initialMenuName.length );
505
-
506
- const saveButton = await page.$(
507
- '.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 }
508
450
  );
509
- await saveButton.click();
510
- 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.
511
463
  const headerSubtitle = await page.waitForSelector(
512
- '.edit-navigation-header__subtitle'
464
+ '.edit-navigation-menu-actions__subtitle'
513
465
  );
514
- expect( headerSubtitle ).toBeTruthy();
515
466
  const headerSubtitleText = await headerSubtitle.evaluate(
516
467
  ( element ) => element.innerText
517
468
  );
518
- expect( headerSubtitleText ).toBe(
519
- `Editing: ${ initialMenuName }`
520
- );
469
+ expect( headerSubtitleText ).toBe( initialMenuName );
521
470
  } );
522
471
  } );
523
472
 
524
473
  describe( 'Change detections', () => {
525
474
  beforeEach( async () => {
526
- const menuPostResponse = {
527
- id: 4,
528
- description: '',
529
- name: 'Main',
530
- slug: 'main-menu',
531
- meta: [],
532
- auto_add: false,
533
- };
534
-
535
- await setUpResponseMocking( [
536
- ...getMenuMocks( {
537
- GET: [ menuPostResponse ],
538
- POST: menuPostResponse,
539
- } ),
540
- ...getMenuItemMocks( { GET: [] } ),
541
- ] );
542
-
475
+ await createMenu( { name: 'Main' } );
543
476
  await visitNavigationEditor();
544
477
  } );
545
478
 
546
- afterEach( async () => {
547
- await setUpResponseMocking( [] );
548
- } );
549
-
550
479
  async function assertIsDirty( isDirty ) {
551
480
  let hadDialog = false;
552
481
 
@@ -567,10 +496,12 @@ describe( 'Navigation editor', () => {
567
496
  }
568
497
  }
569
498
 
499
+ // eslint-disable-next-line jest/no-disabled-tests
570
500
  it.skip( 'should not prompt to confirm unsaved changes for the newly selected menu', async () => {
571
501
  await assertIsDirty( false );
572
502
  } );
573
503
 
504
+ // eslint-disable-next-line jest/no-disabled-tests
574
505
  it.skip( 'should prompt to confirm unsaved changes when menu name is edited', async () => {
575
506
  await page.type(
576
507
  '.edit-navigation-name-editor__text-control input',
@@ -580,4 +511,172 @@ describe( 'Navigation editor', () => {
580
511
  await assertIsDirty( true );
581
512
  } );
582
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
+ } );
583
682
  } );