@wordpress/e2e-tests 2.5.17 → 3.0.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +7 -0
  2. package/LICENSE.md +1 -1
  3. package/config/flaky-tests-reporter.js +2 -1
  4. package/package.json +12 -11
  5. package/plugins/query-block.php +2 -2
  6. package/specs/editor/blocks/__snapshots__/heading.test.js.snap +2 -2
  7. package/specs/editor/blocks/__snapshots__/navigation.test.js.snap +29 -57
  8. package/specs/editor/blocks/__snapshots__/spacer.test.js.snap +1 -1
  9. package/specs/editor/blocks/classic.test.js +5 -2
  10. package/specs/editor/blocks/columns.test.js +1 -1
  11. package/specs/editor/blocks/cover.test.js +1 -1
  12. package/specs/editor/blocks/heading.test.js +15 -3
  13. package/specs/editor/blocks/image.test.js +1 -2
  14. package/specs/editor/blocks/navigation.test.js +311 -439
  15. package/specs/editor/blocks/preformatted.test.js +2 -1
  16. package/specs/editor/blocks/site-title.test.js +2 -31
  17. package/specs/editor/fixtures/menu-items-request-fixture.json +84 -0
  18. package/specs/editor/fixtures/menu-items-response-fixture.json +240 -144
  19. package/specs/editor/plugins/block-variations.test.js +1 -1
  20. package/specs/editor/plugins/custom-post-types.test.js +4 -3
  21. package/specs/editor/various/__snapshots__/block-editor-keyboard-shortcuts.test.js.snap +38 -24
  22. package/specs/editor/various/adding-patterns.test.js +1 -1
  23. package/specs/editor/various/block-editor-keyboard-shortcuts.test.js +43 -3
  24. package/specs/editor/various/block-grouping.test.js +2 -18
  25. package/specs/editor/various/block-hierarchy-navigation.test.js +3 -3
  26. package/specs/editor/various/change-detection.test.js +5 -0
  27. package/specs/editor/various/editor-modes.test.js +7 -0
  28. package/specs/editor/various/font-size-picker.test.js +58 -18
  29. package/specs/editor/various/inserting-blocks.test.js +6 -2
  30. package/specs/editor/various/keyboard-navigable-blocks.test.js +6 -0
  31. package/specs/editor/various/list-view.test.js +2 -2
  32. package/specs/editor/various/navigable-toolbar.test.js +2 -2
  33. package/specs/editor/various/post-editor-template-mode.test.js +1 -1
  34. package/specs/editor/various/preview.test.js +67 -2
  35. package/specs/editor/various/reusable-blocks.test.js +53 -31
  36. package/specs/editor/various/undo.test.js +21 -0
  37. package/specs/editor/various/writing-flow.test.js +8 -4
  38. package/specs/performance/site-editor.test.js +1 -1
  39. package/specs/site-editor/document-settings.test.js +5 -5
  40. package/specs/site-editor/multi-entity-editing.test.js +2 -2
  41. package/specs/site-editor/multi-entity-saving.test.js +53 -63
  42. package/specs/site-editor/settings-sidebar.test.js +4 -4
  43. package/specs/site-editor/site-editor-export.test.js +1 -1
  44. package/specs/site-editor/site-editor-inserter.test.js +1 -1
  45. package/specs/site-editor/template-part.test.js +95 -79
  46. package/specs/site-editor/template-revert.test.js +13 -9
  47. package/specs/widgets/customizing-widgets.test.js +7 -3
@@ -1,3 +1,8 @@
1
+ /**
2
+ * External dependencies
3
+ */
4
+ import { uniqueId } from 'lodash';
5
+
1
6
  /**
2
7
  * WordPress dependencies
3
8
  */
@@ -6,83 +11,33 @@ import {
6
11
  clickOnMoreMenuItem,
7
12
  createJSONResponse,
8
13
  createNewPost,
9
- getEditedPostContent,
14
+ createMenu as createClassicMenu,
15
+ deleteAllMenus as deleteAllClassicMenus,
10
16
  insertBlock,
11
17
  setUpResponseMocking,
12
18
  pressKeyWithModifier,
13
19
  saveDraft,
14
20
  showBlockToolbar,
15
21
  openPreviewPage,
16
- selectBlockByClientId,
17
- getAllBlocks,
22
+ ensureSidebarOpened,
23
+ __experimentalRest as rest,
24
+ publishPost,
25
+ createUser,
26
+ loginUser,
27
+ deleteUser,
28
+ switchUserToAdmin,
18
29
  } from '@wordpress/e2e-test-utils';
30
+ import { addQueryArgs } from '@wordpress/url';
19
31
 
20
32
  /**
21
33
  * Internal dependencies
22
34
  */
23
- import menuItemsFixture from '../fixtures/menu-items-response-fixture.json';
24
-
25
- const menusFixture = [
26
- {
27
- name: 'Test Menu 1',
28
- slug: 'test-menu-1',
29
- },
30
- {
31
- name: 'Test Menu 2',
32
- slug: 'test-menu-2',
33
- },
34
- {
35
- name: 'Test Menu 3',
36
- slug: 'test-menu-3',
37
- },
38
- ];
39
-
40
- // Matching against variations of the same URL encoded and non-encoded
41
- // produces the most reliable mocking.
42
- const REST_MENUS_ROUTES = [
43
- '/wp/v2/menus',
44
- `rest_route=${ encodeURIComponent( '/wp/v2/menus' ) }`,
45
- ];
46
- const REST_MENU_ITEMS_ROUTES = [
47
- '/wp/v2/menu-items',
48
- `rest_route=${ encodeURIComponent( '/wp/v2/menu-items' ) }`,
49
- ];
50
-
51
- const REST_PAGES_ROUTES = [
52
- '/wp/v2/pages',
53
- `rest_route=${ encodeURIComponent( '/wp/v2/pages' ) }`,
54
- ];
35
+ import menuItemsFixture from '../fixtures/menu-items-request-fixture.json';
55
36
 
56
- /**
57
- * Determines if a given URL matches any of a given collection of
58
- * routes (extressed as substrings).
59
- *
60
- * @param {string} reqUrl the full URL to be tested for matches.
61
- * @param {Array} routes array of strings to match against the URL.
62
- */
63
- function matchUrlToRoute( reqUrl, routes ) {
64
- return routes.some( ( route ) => reqUrl.includes( route ) );
65
- }
66
-
67
- async function mockPagesResponse( pages ) {
68
- const mappedPages = pages.map( ( { title, slug }, index ) => ( {
69
- id: index + 1,
70
- type: 'page',
71
- link: `https://this/is/a/test/page/${ slug }`,
72
- title: {
73
- rendered: title,
74
- raw: title,
75
- },
76
- } ) );
77
-
78
- await setUpResponseMocking( [
79
- {
80
- match: ( request ) =>
81
- matchUrlToRoute( request.url(), REST_PAGES_ROUTES ),
82
- onRequestMatch: createJSONResponse( mappedPages ),
83
- },
84
- ] );
85
- }
37
+ const POSTS_ENDPOINT = '/wp/v2/posts';
38
+ const PAGES_ENDPOINT = '/wp/v2/pages';
39
+ const DRAFT_PAGES_ENDPOINT = [ PAGES_ENDPOINT, { status: 'draft' } ];
40
+ const NAVIGATION_MENUS_ENDPOINT = '/wp/v2/navigation';
86
41
 
87
42
  async function mockSearchResponse( items ) {
88
43
  const mappedItems = items.map( ( { title, slug }, index ) => ( {
@@ -103,76 +58,6 @@ async function mockSearchResponse( items ) {
103
58
  ] );
104
59
  }
105
60
 
106
- /**
107
- * Creates mocked REST API responses for calls to menus and menu-items
108
- * endpoints.
109
- * Note: this needs to be within a single call to
110
- * `setUpResponseMocking` as you can only setup response mocking once per test run.
111
- *
112
- * @param {Array} menus menus to provide as mocked responses to menus entity API requests.
113
- * @param {Array} menuItems menu items to provide as mocked responses to menu-items entity API requests.
114
- */
115
- async function mockAllMenusResponses(
116
- menus = menusFixture,
117
- menuItems = menuItemsFixture
118
- ) {
119
- const mappedMenus = menus.length
120
- ? menus.map( ( menu, index ) => ( {
121
- ...menu,
122
- id: index + 1,
123
- } ) )
124
- : [];
125
-
126
- await setUpResponseMocking( [
127
- {
128
- match: ( request ) =>
129
- matchUrlToRoute( request.url(), REST_MENUS_ROUTES ),
130
- onRequestMatch: createJSONResponse( mappedMenus ),
131
- },
132
- {
133
- match: ( request ) =>
134
- matchUrlToRoute( request.url(), REST_MENU_ITEMS_ROUTES ),
135
- onRequestMatch: createJSONResponse( menuItems ),
136
- },
137
- ] );
138
- }
139
-
140
- async function mockEmptyMenusAndPagesResponses() {
141
- const emptyResponse = [];
142
- await setUpResponseMocking( [
143
- {
144
- match: ( request ) =>
145
- matchUrlToRoute( request.url(), REST_MENUS_ROUTES ),
146
- onRequestMatch: createJSONResponse( emptyResponse ),
147
- },
148
- {
149
- match: ( request ) =>
150
- matchUrlToRoute( request.url(), REST_PAGES_ROUTES ),
151
- onRequestMatch: createJSONResponse( emptyResponse ),
152
- },
153
- ] );
154
- }
155
-
156
- async function mockCreatePageResponse( title, slug ) {
157
- const page = {
158
- id: 1,
159
- title: { raw: title, rendered: title },
160
- type: 'page',
161
- link: `https://this/is/a/test/create/page/${ slug }`,
162
- slug,
163
- };
164
-
165
- await setUpResponseMocking( [
166
- {
167
- match: ( request ) =>
168
- request.url().includes( `rest_route` ) &&
169
- request.url().includes( `pages` ) &&
170
- request.method() === 'POST',
171
- onRequestMatch: createJSONResponse( page ),
172
- },
173
- ] );
174
- }
175
-
176
61
  /**
177
62
  * Interacts with the LinkControl to perform a search and select a returned suggestion
178
63
  *
@@ -225,7 +110,7 @@ async function updateActiveNavigationLink( { url, label, type } ) {
225
110
  }
226
111
  }
227
112
 
228
- async function selectDropDownOption( optionText ) {
113
+ async function selectClassicMenu( optionText ) {
229
114
  const dropdown = await page.waitForXPath(
230
115
  "//*[contains(@class, 'wp-block-navigation-placeholder__actions__dropdown')]"
231
116
  );
@@ -242,154 +127,200 @@ const START_EMPTY_XPATH = `${ PLACEHOLDER_ACTIONS_XPATH }//button[text()='Start
242
127
  const ADD_ALL_PAGES_XPATH = `${ PLACEHOLDER_ACTIONS_XPATH }//button[text()='Add all pages']`;
243
128
  const SELECT_MENU_XPATH = `${ PLACEHOLDER_ACTIONS_XPATH }//button[text()='Select menu']`;
244
129
 
245
- async function createNavBlockWithAllPages() {
246
- const allPagesButton = await page.waitForXPath( ADD_ALL_PAGES_XPATH );
247
- await allPagesButton.click();
130
+ /**
131
+ * Delete all items for the given REST resources using the REST API.
132
+ *
133
+ * @param {*} endpoints The endpoints of the resources to delete.
134
+ */
135
+ async function deleteAll( endpoints ) {
136
+ for ( const endpoint of endpoints ) {
137
+ const defaultArgs = { per_page: -1 };
138
+ const isArrayEndpoint = Array.isArray( endpoint );
139
+ const path = isArrayEndpoint ? endpoint[ 0 ] : endpoint;
140
+ const args = isArrayEndpoint
141
+ ? { ...defaultArgs, ...endpoint[ 1 ] }
142
+ : defaultArgs;
143
+
144
+ const items = await rest( {
145
+ path: addQueryArgs( path, args ),
146
+ } );
147
+
148
+ for ( const item of items ) {
149
+ await rest( {
150
+ method: 'DELETE',
151
+ path: `${ path }/${ item.id }?force=true`,
152
+ } );
153
+ }
154
+ }
248
155
  }
249
156
 
250
- async function createEmptyNavBlock() {
251
- const startEmptyButton = await page.waitForXPath( START_EMPTY_XPATH );
252
- await startEmptyButton.click();
157
+ /**
158
+ * Create a set of pages using the REST API.
159
+ *
160
+ * @param {Array} pages An array of page objects.
161
+ */
162
+ async function createPages( pages ) {
163
+ for ( const page of pages ) {
164
+ await rest( {
165
+ method: 'POST',
166
+ path: PAGES_ENDPOINT,
167
+ data: {
168
+ status: 'publish',
169
+ ...page,
170
+ },
171
+ } );
172
+ }
253
173
  }
254
174
 
255
- async function toggleSidebar() {
256
- await page.click(
257
- '.edit-post-header__settings button[aria-label="Settings"]'
258
- );
175
+ /**
176
+ * Replace unique ids in nav block content, since these won't be consistent
177
+ * between test runs.
178
+ *
179
+ * @param {string} content HTML block content, either raw or rendered.
180
+ *
181
+ * @return {string} HTML block content with stripped ids
182
+ */
183
+ function stripPageIds( content ) {
184
+ return content
185
+ .replace( /page_id=\d+/gm, 'page_id=[number]' )
186
+ .replace( /"id":\d+/gm, '"id":[number]' );
259
187
  }
260
188
 
261
- async function turnResponsivenessOn() {
262
- const blocks = await getAllBlocks();
189
+ /**
190
+ * Check navigation block content by fetching the navigation menu.
191
+ *
192
+ * @return {string} Menu content.
193
+ */
194
+ async function getNavigationMenuRawContent() {
195
+ const menuRef = await page.evaluate( () => {
196
+ const blocks = wp.data.select( 'core/block-editor' ).getBlocks();
197
+ const navigationBlock = blocks.find(
198
+ ( block ) => block.name === 'core/navigation'
199
+ );
263
200
 
264
- await selectBlockByClientId( blocks[ 0 ].clientId );
265
- await toggleSidebar();
201
+ return navigationBlock.attributes.ref;
202
+ } );
266
203
 
267
- const [ responsivenessToggleButton ] = await page.$x(
268
- '//label[text()[contains(.,"Enable responsive menu")]]'
269
- );
204
+ if ( ! menuRef ) {
205
+ throw 'getNavigationMenuRawContent was unable to find a ref attribute on the first navigation block';
206
+ }
270
207
 
271
- await responsivenessToggleButton.click();
208
+ const response = await rest( {
209
+ method: 'GET',
210
+ path: `/wp/v2/navigation/${ menuRef }?context=edit`,
211
+ } );
272
212
 
273
- await saveDraft();
213
+ return stripPageIds( response.content.raw );
274
214
  }
275
215
 
276
- beforeEach( async () => {
277
- await createNewPost();
278
- } );
279
-
280
- afterEach( async () => {
281
- await setUpResponseMocking( [] );
282
- } );
283
-
284
216
  // Disable reason - these tests are to be re-written.
285
217
  // eslint-disable-next-line jest/no-disabled-tests
286
- describe.skip( 'Navigation', () => {
287
- describe( 'Creating from existing Pages', () => {
218
+ describe( 'Navigation', () => {
219
+ const contributorUsername = uniqueId( 'contributoruser_' );
220
+ let contributorPassword;
221
+
222
+ beforeAll( async () => {
223
+ // Creation of the contributor user **MUST** be at the top level describe block
224
+ // otherwise this test will become unstable. This action only happens once
225
+ // so there is no huge performance hit.
226
+ contributorPassword = await createUser( contributorUsername, {
227
+ role: 'contributor',
228
+ } );
229
+ } );
230
+
231
+ beforeEach( async () => {
232
+ await deleteAll( [
233
+ POSTS_ENDPOINT,
234
+ PAGES_ENDPOINT,
235
+ DRAFT_PAGES_ENDPOINT,
236
+ NAVIGATION_MENUS_ENDPOINT,
237
+ ] );
238
+ await deleteAllClassicMenus();
239
+ } );
240
+
241
+ afterEach( async () => {
242
+ await setUpResponseMocking( [] );
243
+ } );
244
+
245
+ afterAll( async () => {
246
+ await deleteAll( [
247
+ POSTS_ENDPOINT,
248
+ PAGES_ENDPOINT,
249
+ DRAFT_PAGES_ENDPOINT,
250
+ NAVIGATION_MENUS_ENDPOINT,
251
+ ] );
252
+ await deleteAllClassicMenus();
253
+
254
+ // As per the creation in the beforeAll() above, this
255
+ // action must be done at the root level describe() block.
256
+ await deleteUser( contributorUsername );
257
+ } );
258
+
259
+ describe( 'placeholder', () => {
288
260
  it( 'allows a navigation block to be created using existing pages', async () => {
289
- // Mock the response from the Pages endpoint. This is done so that the pages returned are always
290
- // consistent and to test the feature more rigorously than the single default sample page.
291
- await mockPagesResponse( [
292
- {
293
- title: 'Home',
294
- slug: 'home',
295
- },
261
+ await createPages( [
296
262
  {
297
263
  title: 'About',
298
- slug: 'about',
264
+ menu_order: 0,
299
265
  },
300
266
  {
301
267
  title: 'Contact Us',
302
- slug: 'contact',
268
+ menu_order: 1,
269
+ },
270
+ {
271
+ title: 'FAQ',
272
+ menu_order: 2,
303
273
  },
304
274
  ] );
305
275
 
306
- // Add the navigation block.
307
- await insertBlock( 'Navigation' );
308
-
309
- await createNavBlockWithAllPages();
310
-
311
- // Snapshot should contain the mocked pages.
312
- expect( await getEditedPostContent() ).toMatchSnapshot();
313
- } );
314
-
315
- it( 'does not display option to create from existing Pages if there are no Pages', async () => {
316
- // Force no Pages or Menus to be returned by API responses.
317
- await mockEmptyMenusAndPagesResponses();
276
+ await createNewPost();
318
277
 
319
278
  // Add the navigation block.
320
279
  await insertBlock( 'Navigation' );
321
-
322
- await page.waitForXPath( START_EMPTY_XPATH );
323
-
324
- const placeholderActionsLength = await page.$$eval(
325
- `.${ PLACEHOLDER_ACTIONS_CLASS } button`,
326
- ( els ) => els.length
280
+ const allPagesButton = await page.waitForXPath(
281
+ ADD_ALL_PAGES_XPATH
327
282
  );
283
+ await allPagesButton.click();
328
284
 
329
- // Should only be showing "Start empty"
330
- expect( placeholderActionsLength ).toEqual( 1 );
285
+ // Wait for the page list block to be present
286
+ await page.waitForSelector( 'ul[aria-label="Block: Page List"]' );
287
+
288
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
331
289
  } );
332
- } );
333
290
 
334
- describe( 'Creating from existing Menus', () => {
335
291
  it( 'allows a navigation block to be created from existing menus', async () => {
336
- await mockAllMenusResponses();
337
-
338
- // Add the navigation block.
339
- await insertBlock( 'Navigation' );
340
-
341
- await selectDropDownOption( 'Test Menu 2' );
342
-
343
- // Scope element selector to the Editor's "Content" region as otherwise it picks up on
344
- // block previews.
345
- const navLinkSelector =
346
- '[aria-label="Editor content"][role="region"] .wp-block-navigation-item';
347
-
348
- await page.waitForSelector( navLinkSelector );
349
-
350
- const navBlockItemsLength = await page.$$eval(
351
- navLinkSelector,
352
- ( els ) => els.length
292
+ await createClassicMenu( { name: 'Test Menu 1' } );
293
+ await createClassicMenu(
294
+ { name: 'Test Menu 2' },
295
+ menuItemsFixture
353
296
  );
354
297
 
355
- // Assert the correct number of Nav Link blocks were inserted.
356
- expect( navBlockItemsLength ).toEqual( menuItemsFixture.length );
298
+ await createNewPost();
299
+ await insertBlock( 'Navigation' );
300
+ await selectClassicMenu( 'Test Menu 2' );
357
301
 
358
- // Snapshot should contain the mocked menu items.
359
- expect( await getEditedPostContent() ).toMatchSnapshot();
302
+ // Wait for a navigation link block before making assertion.
303
+ await page.waitForSelector( '*[aria-label="Block: Custom Link"]' );
304
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
360
305
  } );
361
306
 
362
307
  it( 'creates an empty navigation block when the selected existing menu is also empty', async () => {
363
- // Force mock to return no Menus Items (empty menu)
364
- const emptyMenuItems = [];
365
- await mockAllMenusResponses( menusFixture, emptyMenuItems );
366
-
367
- // Add the navigation block.
308
+ await createClassicMenu( { name: 'Test Menu 1' } );
309
+ await createNewPost();
368
310
  await insertBlock( 'Navigation' );
311
+ await selectClassicMenu( 'Test Menu 1' );
369
312
 
370
- await selectDropDownOption( 'Test Menu 1' );
371
-
372
- // Scope element selector to the "Editor content" as otherwise it picks up on
373
- // Block Style live previews.
374
- const navBlockItemsLength = await page.$$eval(
375
- '[aria-label="Editor content"][role="region"] li[aria-label="Block: Link"]',
376
- ( els ) => els.length
313
+ // Wait for the appender so that we know the navigation menu was created.
314
+ await page.waitForSelector(
315
+ 'nav[aria-label="Block: Navigation"] button[aria-label="Add block"]'
377
316
  );
378
-
379
- // Assert an empty Nav Block is created.
380
- expect( navBlockItemsLength ).toEqual( 0 );
381
-
382
- // Snapshot should contain the mocked menu items.
383
- expect( await getEditedPostContent() ).toMatchSnapshot();
317
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
384
318
  } );
385
319
 
386
- it( 'does not display option to create from existing menus if there are no menus', async () => {
387
- // Force no Menus to be returned by API response.
388
- await mockEmptyMenusAndPagesResponses();
320
+ it( 'does not display the options to create from pages or menus if there are none', async () => {
321
+ await createNewPost();
389
322
 
390
- // Add the navigation block.
391
323
  await insertBlock( 'Navigation' );
392
-
393
324
  await page.waitForXPath( START_EMPTY_XPATH );
394
325
 
395
326
  const placeholderActionsLength = await page.$$eval(
@@ -397,21 +328,21 @@ describe.skip( 'Navigation', () => {
397
328
  ( els ) => els.length
398
329
  );
399
330
 
400
- // Should only be showing create empty menu.
331
+ // Should only be showing "Start empty".
401
332
  expect( placeholderActionsLength ).toEqual( 1 );
402
333
  } );
403
334
  } );
404
335
 
405
336
  it( 'allows an empty navigation block to be created and manually populated using a mixture of internal and external links', async () => {
406
- // Add the navigation block.
337
+ await createNewPost();
407
338
  await insertBlock( 'Navigation' );
339
+ const startEmptyButton = await page.waitForXPath( START_EMPTY_XPATH );
340
+ await startEmptyButton.click();
408
341
 
409
- // Create an empty nav block.
410
- await page.waitForSelector( '.wp-block-navigation-placeholder' );
411
-
412
- await createEmptyNavBlock();
413
-
414
- await page.click( '.wp-block-navigation .block-list-appender' );
342
+ const appender = await page.waitForSelector(
343
+ '.wp-block-navigation .block-list-appender'
344
+ );
345
+ await appender.click();
415
346
 
416
347
  // Add a link to the Link block.
417
348
  await updateActiveNavigationLink( {
@@ -420,9 +351,14 @@ describe.skip( 'Navigation', () => {
420
351
  type: 'url',
421
352
  } );
422
353
 
354
+ // Select the parent navigation block to show the appender.
423
355
  await showBlockToolbar();
356
+ await page.click( 'button[aria-label="Select Navigation"]' );
424
357
 
425
- await page.click( '.wp-block-navigation .block-list-appender' );
358
+ const appenderAgain = await page.waitForSelector(
359
+ '.wp-block-navigation .block-list-appender'
360
+ );
361
+ await appenderAgain.click();
426
362
 
427
363
  // After adding a new block, search input should be shown immediately.
428
364
  // Verify that Escape would close the popover.
@@ -442,7 +378,7 @@ describe.skip( 'Navigation', () => {
442
378
  expect( isInURLInput ).toBe( true );
443
379
  await page.keyboard.press( 'Escape' );
444
380
 
445
- //click the link placeholder
381
+ // Click the link placeholder.
446
382
  const placeholder = await page.waitForSelector(
447
383
  '.wp-block-navigation-link__placeholder'
448
384
  );
@@ -461,20 +397,22 @@ describe.skip( 'Navigation', () => {
461
397
  type: 'entity',
462
398
  } );
463
399
 
400
+ await publishPost();
401
+
464
402
  // Expect a Navigation Block with two Links in the snapshot.
465
- expect( await getEditedPostContent() ).toMatchSnapshot();
403
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
466
404
  } );
467
405
 
468
406
  it( 'encodes URL when create block if needed', async () => {
469
- // Add the navigation block.
407
+ await createNewPost();
470
408
  await insertBlock( 'Navigation' );
409
+ const startEmptyButton = await page.waitForXPath( START_EMPTY_XPATH );
410
+ await startEmptyButton.click();
471
411
 
472
- // Create an empty nav block.
473
- await page.waitForSelector( '.wp-block-navigation-placeholder' );
474
-
475
- await createEmptyNavBlock();
476
-
477
- await page.click( '.wp-block-navigation .block-list-appender' );
412
+ const appender = await page.waitForSelector(
413
+ '.wp-block-navigation .block-list-appender'
414
+ );
415
+ await appender.click();
478
416
 
479
417
  // Add a link to the Link block.
480
418
  await updateActiveNavigationLink( {
@@ -483,8 +421,12 @@ describe.skip( 'Navigation', () => {
483
421
  } );
484
422
 
485
423
  await showBlockToolbar();
424
+ await page.click( 'button[aria-label="Select Navigation"]' );
486
425
 
487
- await page.click( '.wp-block-navigation .block-list-appender' );
426
+ const appenderAgain = await page.waitForSelector(
427
+ '.wp-block-navigation .block-list-appender'
428
+ );
429
+ await appenderAgain.click();
488
430
 
489
431
  // Wait for URL input to be focused
490
432
  await page.waitForSelector(
@@ -520,92 +462,70 @@ describe.skip( 'Navigation', () => {
520
462
  type: 'entity',
521
463
  } );
522
464
 
465
+ await publishPost();
466
+
523
467
  // Expect a Navigation Block with two Links in the snapshot.
524
468
  // The 2nd link should not be double encoded.
525
- expect( await getEditedPostContent() ).toMatchSnapshot();
469
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
526
470
  } );
527
471
 
528
472
  it( 'allows pages to be created from the navigation block and their links added to menu', async () => {
529
- // Mock request for creating pages and the page search response.
530
- // We mock the page search to return no results and we use a very long
531
- // page name because if the search returns existing pages then the
532
- // "Create" suggestion might be below the scroll fold within the
533
- // `LinkControl` search suggestions UI. If this happens then it's not
534
- // possible to wait for the element to appear and the test will
535
- // erroneously fail.
536
- await mockSearchResponse( [] );
537
- await mockCreatePageResponse(
538
- 'A really long page name that will not exist',
539
- 'my-new-page'
540
- );
541
-
542
- // Add the navigation block.
473
+ await createNewPost();
543
474
  await insertBlock( 'Navigation' );
544
-
545
- // Create an empty nav block.
546
- await createEmptyNavBlock();
547
-
548
- await page.click( '.wp-block-navigation .block-list-appender' );
549
-
550
- // Wait for URL input to be focused
551
- await page.waitForSelector(
552
- 'input.block-editor-url-input__input:focus'
553
- );
554
-
555
- // After adding a new block, search input should be shown immediately.
556
- const isInURLInput = await page.evaluate(
557
- () =>
558
- !! document.activeElement.matches(
559
- 'input.block-editor-url-input__input'
560
- )
561
- );
562
- expect( isInURLInput ).toBe( true );
563
-
564
- // Insert name for the new page.
565
- await page.type(
566
- 'input[placeholder="Search or type url"]',
567
- 'A really long page name that will not exist'
475
+ const startEmptyButton = await page.waitForXPath( START_EMPTY_XPATH );
476
+ await startEmptyButton.click();
477
+ const appender = await page.waitForSelector(
478
+ '.wp-block-navigation .block-list-appender'
568
479
  );
480
+ await appender.click();
569
481
 
570
482
  // Wait for URL input to be focused
571
- await page.waitForSelector(
483
+ // Insert name for the new page.
484
+ const pageTitle = 'A really long page name that will not exist';
485
+ const input = await page.waitForSelector(
572
486
  'input.block-editor-url-input__input:focus'
573
487
  );
574
-
575
- // Wait for the create button to appear and click it.
576
- await page.waitForSelector(
488
+ await input.type( pageTitle );
489
+
490
+ // When creating a page, the URLControl makes a request to the
491
+ // url-details endpoint to fetch information about the page.
492
+ // Because the draft is inaccessible publicly, this request
493
+ // returns a 404 response. Wait for the response and expect
494
+ // the error to have occurred.
495
+ const createPageButton = await page.waitForSelector(
577
496
  '.block-editor-link-control__search-create'
578
497
  );
579
-
580
- const createPageButton = await page.$(
581
- '.block-editor-link-control__search-create'
498
+ const responsePromise = page.waitForResponse(
499
+ ( response ) =>
500
+ response.url().includes( 'url-details' ) &&
501
+ response.status() === 404
582
502
  );
503
+ const createPagePromise = createPageButton.click();
504
+ await Promise.all( [ responsePromise, createPagePromise ] );
583
505
 
584
- await createPageButton.click();
585
-
586
- const draftLink = await page.waitForSelector(
587
- '.wp-block-navigation-item__content'
506
+ // Creating a draft is async, so wait for a sign of completion. In this
507
+ // case the link that shows in the URL popover once a link is added.
508
+ await page.waitForXPath(
509
+ `//a[contains(@class, "block-editor-link-control__search-item-title") and contains(., "${ pageTitle }")]`
588
510
  );
589
- await draftLink.click();
511
+
512
+ await publishPost();
590
513
 
591
514
  // Expect a Navigation Block with a link for "A really long page name that will not exist".
592
- expect( await getEditedPostContent() ).toMatchSnapshot();
515
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
516
+ expect( console ).toHaveErroredWith(
517
+ 'Failed to load resource: the server responded with a status of 404 (Not Found)'
518
+ );
593
519
  } );
594
520
 
595
- it( 'allows navigation submenus to open on click instead of hover', async () => {
596
- await mockAllMenusResponses();
597
-
598
- // Add the navigation block.
521
+ it( 'renders buttons for the submenu opener elements when the block is set to open on click instead of hover', async () => {
522
+ await createClassicMenu( { name: 'Test Menu 2' }, menuItemsFixture );
523
+ await createNewPost();
599
524
  await insertBlock( 'Navigation' );
525
+ await selectClassicMenu( 'Test Menu 2' );
600
526
 
601
- await selectDropDownOption( 'Test Menu 2' );
602
-
603
- // const blocks = await getAllBlocks();
604
- // await selectBlockByClientId( blocks[ 0 ].clientId );
605
-
606
- await toggleSidebar();
607
-
608
- const [ openOnClickButton ] = await page.$x(
527
+ await ensureSidebarOpened();
528
+ const openOnClickButton = await page.waitForXPath(
609
529
  '//label[contains(text(),"Open on click")]'
610
530
  );
611
531
 
@@ -640,16 +560,15 @@ describe.skip( 'Navigation', () => {
640
560
  } );
641
561
 
642
562
  it( 'Shows the quick inserter when the block contains non-navigation specific blocks', async () => {
643
- // Add the navigation block.
563
+ await createNewPost();
644
564
  await insertBlock( 'Navigation' );
565
+ const startEmptyButton = await page.waitForXPath( START_EMPTY_XPATH );
566
+ await startEmptyButton.click();
645
567
 
646
- // Create an empty nav block.
647
- await page.waitForSelector( '.wp-block-navigation-placeholder' );
648
-
649
- await createEmptyNavBlock();
650
-
651
- // Add a Link block first.
652
- await page.click( '.wp-block-navigation .block-list-appender' );
568
+ const appender = await page.waitForSelector(
569
+ '.wp-block-navigation .block-list-appender'
570
+ );
571
+ await appender.click();
653
572
 
654
573
  // Add a link to the Link block.
655
574
  await updateActiveNavigationLink( {
@@ -661,24 +580,20 @@ describe.skip( 'Navigation', () => {
661
580
  // Now add a different block type.
662
581
  await insertBlock( 'Site Title' );
663
582
 
664
- // Now try inserting another Link block via the quick inserter.
665
- await page.focus( '.wp-block-navigation .block-list-appender' );
666
-
667
- await page.click( '.wp-block-navigation .block-list-appender' );
668
-
669
- const linkButton = await page.waitForSelector(
670
- '.block-editor-inserter__quick-inserter .editor-block-list-item-navigation-link'
583
+ await showBlockToolbar();
584
+ await page.click( 'button[aria-label="Select Navigation"]' );
585
+ const appenderAgain = await page.waitForSelector(
586
+ '.wp-block-navigation .block-list-appender'
671
587
  );
672
- await linkButton.click();
588
+ await appenderAgain.click();
673
589
 
674
- await updateActiveNavigationLink( {
675
- url: 'https://wordpress.org/news/',
676
- label: 'WP News',
677
- type: 'url',
678
- } );
590
+ const quickInserter = await page.waitForSelector(
591
+ '.block-editor-inserter__quick-inserter'
592
+ );
679
593
 
680
- // Expect a Navigation block with two links and a Site Title.
681
- expect( await getEditedPostContent() ).toMatchSnapshot();
594
+ // Expect the quick inserter to be truthy, which it will be because we
595
+ // waited for it. It's nice to end a test with an assertion though.
596
+ expect( quickInserter ).toBeTruthy();
682
597
  } );
683
598
 
684
599
  it( 'supports navigation blocks that have inner blocks within their markup and converts them to wp_navigation posts', async () => {
@@ -700,10 +615,10 @@ describe.skip( 'Navigation', () => {
700
615
  // The select menu button shows up when saving is complete.
701
616
  await navBlock.click();
702
617
  await page.waitForSelector( 'button[aria-label="Select Menu"]' );
703
- // await publishPost();
618
+ await publishPost();
704
619
 
705
620
  // Check that the wp_navigation post has the page list block.
706
- // expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
621
+ expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
707
622
  } );
708
623
 
709
624
  describe( 'Creating and restarting', () => {
@@ -773,7 +688,7 @@ describe.skip( 'Navigation', () => {
773
688
  expect( await page.$x( NAV_ENTITY_SELECTOR ) ).toHaveLength( 1 );
774
689
  } );
775
690
 
776
- it( 'only update a single entity currently linked with the block', async () => {
691
+ it( 'only updates a single entity currently linked with the block', async () => {
777
692
  await createNewPost();
778
693
  await insertBlock( 'Navigation' );
779
694
 
@@ -826,31 +741,16 @@ describe.skip( 'Navigation', () => {
826
741
  } );
827
742
  } );
828
743
 
829
- // The following tests are unstable, roughly around when https://github.com/WordPress/wordpress-develop/pull/1412
830
- // landed. The block manually tests well, so let's skip to unblock other PRs and immediately follow up. cc @vcanales
831
- it.skip( 'loads frontend code only if the block is present', async () => {
832
- // Mock the response from the Pages endpoint. This is done so that the pages returned are always
833
- // consistent and to test the feature more rigorously than the single default sample page.
834
- await mockPagesResponse( [
835
- {
836
- title: 'Home',
837
- slug: 'home',
838
- },
839
- {
840
- title: 'About',
841
- slug: 'about',
842
- },
843
- {
844
- title: 'Contact Us',
845
- slug: 'contact',
846
- },
847
- ] );
848
-
849
- // Create first block at the start in order to enable preview.
850
- await insertBlock( 'Navigation' );
851
- await saveDraft();
744
+ it( 'does not load the frontend script if no navigation blocks are present', async () => {
745
+ await createNewPost();
746
+ await insertBlock( 'Paragraph' );
747
+ await page.waitForSelector( 'p[data-title="Paragraph"]:focus' );
748
+ await page.keyboard.type( 'Hello' );
852
749
 
853
750
  const previewPage = await openPreviewPage();
751
+ await previewPage.bringToFront();
752
+ await previewPage.waitForNetworkIdle();
753
+
854
754
  const isScriptLoaded = await previewPage.evaluate(
855
755
  () =>
856
756
  null !==
@@ -860,83 +760,32 @@ describe.skip( 'Navigation', () => {
860
760
  );
861
761
 
862
762
  expect( isScriptLoaded ).toBe( false );
763
+ } );
863
764
 
864
- await createNavBlockWithAllPages();
765
+ it( 'loads the frontend script only once even when multiple navigation blocks are present', async () => {
766
+ await createNewPost();
767
+ await insertBlock( 'Navigation' );
865
768
  await insertBlock( 'Navigation' );
866
- await createNavBlockWithAllPages();
867
- await turnResponsivenessOn();
868
769
 
869
- await previewPage.reload( {
870
- waitFor: [ 'networkidle0', 'domcontentloaded' ],
871
- } );
770
+ const previewPage = await openPreviewPage();
771
+ await previewPage.bringToFront();
772
+ await previewPage.waitForNetworkIdle();
872
773
 
873
- /*
874
- Count instances of the tag to make sure that it's been loaded only once,
875
- regardless of the number of navigation blocks present.
876
- */
877
774
  const tagCount = await previewPage.evaluate(
878
775
  () =>
879
- Array.from(
880
- document.querySelectorAll(
881
- 'script[src*="navigation/view.min.js"]'
882
- )
776
+ document.querySelectorAll(
777
+ 'script[src*="navigation/view.min.js"]'
883
778
  ).length
884
779
  );
885
780
 
886
781
  expect( tagCount ).toBe( 1 );
887
782
  } );
888
783
 
889
- // eslint-disable-next-line jest/no-disabled-tests
890
- it.skip( 'loads frontend code only if responsiveness is turned on', async () => {
891
- await mockPagesResponse( [
892
- {
893
- title: 'Home',
894
- slug: 'home',
895
- },
896
- {
897
- title: 'About',
898
- slug: 'about',
899
- },
900
- {
901
- title: 'Contact Us',
902
- slug: 'contact',
903
- },
904
- ] );
905
-
906
- await insertBlock( 'Navigation' );
907
- await saveDraft();
908
-
909
- const previewPage = await openPreviewPage();
910
- let isScriptLoaded = await previewPage.evaluate(
911
- () =>
912
- null !==
913
- document.querySelector(
914
- 'script[src*="navigation/view.min.js"]'
915
- )
916
- );
917
-
918
- expect( isScriptLoaded ).toBe( false );
919
-
920
- await createNavBlockWithAllPages();
921
-
922
- await turnResponsivenessOn();
923
-
924
- await previewPage.reload( {
925
- waitFor: [ 'networkidle0', 'domcontentloaded' ],
784
+ describe( 'Permission based restrictions', () => {
785
+ afterEach( async () => {
786
+ await switchUserToAdmin();
926
787
  } );
927
788
 
928
- isScriptLoaded = await previewPage.evaluate(
929
- () =>
930
- null !==
931
- document.querySelector(
932
- 'script[src*="navigation/view.min.js"]'
933
- )
934
- );
935
-
936
- expect( isScriptLoaded ).toBe( true );
937
- } );
938
-
939
- describe( 'Permission based restrictions', () => {
940
789
  it( 'shows a warning if user does not have permission to edit or update navigation menus', async () => {
941
790
  await createNewPost();
942
791
  await insertBlock( 'Navigation' );
@@ -950,11 +799,11 @@ describe.skip( 'Navigation', () => {
950
799
 
951
800
  // Publishing the Post ensures the Navigation entity is saved.
952
801
  // The Post itself is irrelevant.
953
- // await publishPost();
802
+ await publishPost();
954
803
 
955
804
  // Switch to a Contributor role user - they should not have
956
- // permission to update Navigations.
957
- // await loginUser( username, contribUserPassword );
805
+ // permission to update Navigation menus.
806
+ await loginUser( contributorUsername, contributorPassword );
958
807
 
959
808
  await createNewPost();
960
809
 
@@ -975,7 +824,30 @@ describe.skip( 'Navigation', () => {
975
824
  `//*[contains(@class, 'components-snackbar__content')][ text()="You do not have permission to edit this Menu. Any changes made will not be saved." ]`
976
825
  );
977
826
 
978
- // Expect a console 403 for request to Navigation Areas for lower permisison users.
827
+ // Expect a console 403 for request to Navigation Areas for lower permission users.
828
+ // This is because reading requires the `edit_theme_options` capability
829
+ // which the Contributor level user does not have.
830
+ // See: https://github.com/WordPress/gutenberg/blob/4cedaf0c4abb0aeac4bfd4289d63e9889efe9733/lib/class-wp-rest-block-navigation-areas-controller.php#L81-L91.
831
+ // Todo: removed once Nav Areas are removed from the Gutenberg Plugin.
832
+ expect( console ).toHaveErrored();
833
+ } );
834
+
835
+ it( 'shows a warning if user does not have permission to create navigation menus', async () => {
836
+ const noticeText =
837
+ 'You do not have permission to create Navigation Menus.';
838
+ // Switch to a Contributor role user - they should not have
839
+ // permission to update Navigations.
840
+ await loginUser( contributorUsername, contributorPassword );
841
+
842
+ await createNewPost();
843
+ await insertBlock( 'Navigation' );
844
+
845
+ // Make sure the snackbar error shows up
846
+ await page.waitForXPath(
847
+ `//*[contains(@class, 'components-snackbar__content')][ text()="${ noticeText }" ]`
848
+ );
849
+
850
+ // Expect a console 403 for request to Navigation Areas for lower permission users.
979
851
  // This is because reading requires the `edit_theme_options` capability
980
852
  // which the Contributor level user does not have.
981
853
  // See: https://github.com/WordPress/gutenberg/blob/4cedaf0c4abb0aeac4bfd4289d63e9889efe9733/lib/class-wp-rest-block-navigation-areas-controller.php#L81-L91.