@wordpress/e2e-tests 6.4.0 → 6.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 (39) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/jest.config.js +4 -0
  3. package/package.json +7 -7
  4. package/specs/editor/blocks/pullquote.test.js +3 -3
  5. package/specs/editor/plugins/__snapshots__/align-hook.test.js.snap +11 -11
  6. package/specs/editor/plugins/__snapshots__/container-blocks.test.js.snap +14 -14
  7. package/specs/editor/plugins/__snapshots__/cpt-locking.test.js.snap +29 -29
  8. package/specs/editor/plugins/__snapshots__/iframed-inline-styles.test.js.snap +1 -1
  9. package/specs/editor/plugins/__snapshots__/iframed-masonry-block.test.js.snap +1 -1
  10. package/specs/editor/plugins/__snapshots__/inner-blocks-render-appender.test.js.snap +5 -5
  11. package/specs/editor/plugins/__snapshots__/plugins-api.test.js.snap +2 -2
  12. package/specs/editor/various/__snapshots__/adding-patterns.test.js.snap +4 -4
  13. package/specs/editor/various/__snapshots__/block-grouping.test.js.snap +21 -21
  14. package/specs/editor/various/__snapshots__/block-hierarchy-navigation.test.js.snap +12 -12
  15. package/specs/editor/various/__snapshots__/embedding.test.js.snap +19 -19
  16. package/specs/editor/various/__snapshots__/inserting-blocks.test.js.snap +8 -8
  17. package/specs/editor/various/__snapshots__/keep-styles-on-block-transforms.test.js.snap +11 -11
  18. package/specs/editor/various/__snapshots__/links.test.js.snap +9 -9
  19. package/specs/editor/various/__snapshots__/multi-block-selection.test.js.snap +17 -17
  20. package/specs/editor/various/__snapshots__/rich-text.test.js.snap +5 -5
  21. package/specs/editor/various/block-grouping.test.js +3 -3
  22. package/specs/editor/various/format-library/__snapshots__/text-color.test.js.snap +1 -1
  23. package/specs/editor/various/links.test.js +7 -7
  24. package/specs/editor/various/multi-block-selection.test.js +2 -2
  25. package/specs/performance/front-end-block-theme.test.js +13 -24
  26. package/specs/performance/front-end-classic-theme.test.js +13 -24
  27. package/specs/performance/post-editor.test.js +14 -12
  28. package/specs/performance/site-editor.test.js +1 -2
  29. package/specs/performance/utils.js +12 -1
  30. package/specs/site-editor/multi-entity-saving.test.js +0 -2
  31. package/specs/site-editor/settings-sidebar.test.js +0 -1
  32. package/specs/widgets/editing-widgets.test.js +20 -15
  33. package/specs/editor/blocks/__snapshots__/navigation.test.js.snap +0 -47
  34. package/specs/editor/blocks/navigation.test.js +0 -1723
  35. package/specs/editor/various/__snapshots__/block-deletion.test.js.snap +0 -111
  36. package/specs/editor/various/__snapshots__/list-view.test.js.snap +0 -15
  37. package/specs/editor/various/block-deletion.test.js +0 -209
  38. package/specs/editor/various/list-view.test.js +0 -414
  39. package/specs/editor/various/switch-to-draft.test.js +0 -254
@@ -1,1723 +0,0 @@
1
- /**
2
- * WordPress dependencies
3
- */
4
- import {
5
- clickButton,
6
- clickOnMoreMenuItem,
7
- createJSONResponse,
8
- createNewPost,
9
- createMenu as createClassicMenu,
10
- deleteAllMenus as deleteAllClassicMenus,
11
- insertBlock,
12
- setUpResponseMocking,
13
- pressKeyWithModifier,
14
- saveDraft,
15
- showBlockToolbar,
16
- openPreviewPage,
17
- ensureSidebarOpened,
18
- __experimentalRest as rest,
19
- __experimentalBatch as batch,
20
- publishPost,
21
- createUser,
22
- loginUser,
23
- deleteUser,
24
- switchUserToAdmin,
25
- clickBlockToolbarButton,
26
- openListView,
27
- getListViewBlocks,
28
- } from '@wordpress/e2e-test-utils';
29
- import { addQueryArgs } from '@wordpress/url';
30
-
31
- /**
32
- * Internal dependencies
33
- */
34
- import menuItemsFixture from '../fixtures/menu-items-request-fixture.json';
35
-
36
- const POSTS_ENDPOINT = '/wp/v2/posts';
37
- const PAGES_ENDPOINT = '/wp/v2/pages';
38
- const DRAFT_PAGES_ENDPOINT = [ PAGES_ENDPOINT, { status: 'draft' } ];
39
- const NAVIGATION_MENUS_ENDPOINT = '/wp/v2/navigation';
40
- // todo: consolidate with logic found in navigation-editor tests
41
- // https://github.com/WordPress/gutenberg/blob/trunk/packages/e2e-tests/specs/experiments/navigation-editor.test.js#L71
42
- const REST_PAGES_ROUTES = [
43
- '/wp/v2/pages',
44
- `rest_route=${ encodeURIComponent( '/wp/v2/pages' ) }`,
45
- ];
46
- let uniqueId = 0;
47
-
48
- /**
49
- * Determines if a given URL matches any of a given collection of
50
- * routes (expressed as substrings).
51
- *
52
- * @param {string} reqUrl the full URL to be tested for matches.
53
- * @param {Array} routes array of strings to match against the URL.
54
- */
55
- function matchUrlToRoute( reqUrl, routes ) {
56
- return routes.some( ( route ) => reqUrl.includes( route ) );
57
- }
58
-
59
- function getEndpointMocks( matchingRoutes, responsesByMethod ) {
60
- return [ 'GET', 'POST', 'DELETE', 'PUT' ].reduce( ( mocks, restMethod ) => {
61
- if ( responsesByMethod[ restMethod ] ) {
62
- return [
63
- ...mocks,
64
- {
65
- match: ( request ) =>
66
- matchUrlToRoute( request.url(), matchingRoutes ) &&
67
- request.method() === restMethod,
68
- onRequestMatch: createJSONResponse(
69
- responsesByMethod[ restMethod ]
70
- ),
71
- },
72
- ];
73
- }
74
-
75
- return mocks;
76
- }, [] );
77
- }
78
-
79
- function getPagesMocks( responsesByMethod ) {
80
- return getEndpointMocks( REST_PAGES_ROUTES, responsesByMethod );
81
- }
82
-
83
- async function mockSearchResponse( items ) {
84
- const mappedItems = items.map( ( { title, slug }, index ) => ( {
85
- id: index + 1,
86
- subtype: 'page',
87
- title,
88
- type: 'post',
89
- url: `https://this/is/a/test/search/${ slug }`,
90
- } ) );
91
- await setUpResponseMocking( [
92
- {
93
- match: ( request ) =>
94
- request.url().includes( `rest_route` ) &&
95
- request.url().includes( `search` ),
96
- onRequestMatch: createJSONResponse( mappedItems ),
97
- },
98
- ...getPagesMocks( {
99
- GET: [
100
- {
101
- type: 'page',
102
- id: 1,
103
- link: 'https://example.com/1',
104
- title: {
105
- rendered: 'My page',
106
- },
107
- },
108
- ],
109
- } ),
110
- ] );
111
- }
112
-
113
- async function forceSelectNavigationBlock() {
114
- const navBlock = await waitForBlock( 'Navigation' );
115
-
116
- if ( ! navBlock ) {
117
- return;
118
- }
119
-
120
- await page.evaluate( () => {
121
- const blocks = wp.data.select( 'core/block-editor' ).getBlocks();
122
- const navigationBlock = blocks.find(
123
- ( block ) => block.name === 'core/navigation'
124
- );
125
-
126
- if ( ! navigationBlock ) {
127
- return;
128
- }
129
-
130
- return wp.data
131
- .dispatch( 'core/block-editor' )
132
- .selectBlock( navigationBlock?.clientId, 0 );
133
- } );
134
- }
135
-
136
- /**
137
- * Interacts with the LinkControl to perform a search and select a returned suggestion
138
- *
139
- * @param {Object} link link object to be tested
140
- * @param {string} link.url What will be typed in the search input
141
- * @param {string} link.label What the resulting label will be in the creating Link Block after the block is created.
142
- * @param {string} link.type What kind of suggestion should be clicked, ie. 'url', 'create', or 'entity'
143
- */
144
- async function updateActiveNavigationLink( { url, label, type } ) {
145
- const typeClasses = {
146
- create: 'block-editor-link-control__search-create',
147
- entity: 'is-entity',
148
- url: 'is-url',
149
- };
150
-
151
- if ( url ) {
152
- const input = await page.waitForSelector(
153
- 'input[placeholder="Search or type url"]'
154
- );
155
- await input.type( url );
156
-
157
- const suggestionPath = `//button[contains(@class, 'block-editor-link-control__search-item') and contains(@class, '${ typeClasses[ type ] }') and contains(., "${ url }")]`;
158
-
159
- // Wait for the autocomplete suggestion item to appear.
160
- await page.waitForXPath( suggestionPath );
161
- // Set the suggestion.
162
- const suggestion = await page.waitForXPath( suggestionPath );
163
-
164
- // Select it (so we're clicking the right one, even if it's further down the list).
165
- await suggestion.click();
166
- }
167
-
168
- if ( label ) {
169
- // Wait for rich text editor input to be focused before we start typing the label.
170
- await page.waitForSelector( ':focus.rich-text' );
171
-
172
- // With https://github.com/WordPress/gutenberg/pull/19686, we're auto-selecting the label if the label is URL-ish.
173
- // In this case, it means we have to select and delete the label if it's _not_ the url.
174
- if ( label !== url ) {
175
- // Ideally this would be `await pressKeyWithModifier( 'primary', 'a' )`
176
- // to select all text like other tests do.
177
- // Unfortunately, these tests don't seem to pass on Travis CI when
178
- // using that approach, while using `Home` and `End` they do pass.
179
- await page.keyboard.press( 'Home' );
180
- await pressKeyWithModifier( 'shift', 'End' );
181
- await page.keyboard.press( 'Backspace' );
182
- }
183
-
184
- await page.keyboard.type( label );
185
- }
186
- }
187
-
188
- async function selectClassicMenu( optionText ) {
189
- const navigationSelector = await page.waitForXPath(
190
- "//button[text()='Select Menu']"
191
- );
192
- await navigationSelector.click();
193
-
194
- const theOption = await page.waitForXPath(
195
- '//button[contains(., "' + optionText + '")]'
196
- );
197
- await theOption.click();
198
-
199
- await page.waitForResponse(
200
- ( response ) =>
201
- response.url().includes( 'menu-items' ) && response.status() === 200
202
- );
203
- }
204
-
205
- /**
206
- * Delete all items for the given REST resources using the REST API.
207
- *
208
- * @param {*} endpoints The endpoints of the resources to delete.
209
- */
210
- async function deleteAll( endpoints ) {
211
- for ( const endpoint of endpoints ) {
212
- const defaultArgs = { per_page: -1 };
213
- const isArrayEndpoint = Array.isArray( endpoint );
214
- const path = isArrayEndpoint ? endpoint[ 0 ] : endpoint;
215
- const args = isArrayEndpoint
216
- ? { ...defaultArgs, ...endpoint[ 1 ] }
217
- : defaultArgs;
218
-
219
- const items = await rest( {
220
- path: addQueryArgs( path, args ),
221
- } );
222
-
223
- for ( const item of items ) {
224
- await rest( {
225
- method: 'DELETE',
226
- path: `${ path }/${ item.id }?force=true`,
227
- } );
228
- }
229
- }
230
- }
231
-
232
- /**
233
- * Replace unique ids in nav block content, since these won't be consistent
234
- * between test runs.
235
- *
236
- * @param {string} content HTML block content, either raw or rendered.
237
- *
238
- * @return {string} HTML block content with stripped ids
239
- */
240
- function stripPageIds( content ) {
241
- return content
242
- .replace( /page_id=\d+/gm, 'page_id=[number]' )
243
- .replace( /"id":\d+/gm, '"id":[number]' );
244
- }
245
-
246
- /**
247
- * Check navigation block content by fetching the navigation menu.
248
- *
249
- * @return {string} Menu content.
250
- */
251
- async function getNavigationMenuRawContent() {
252
- const menuRef = await page.evaluate( () => {
253
- const blocks = wp.data.select( 'core/block-editor' ).getBlocks();
254
- const navigationBlock = blocks.find(
255
- ( block ) => block.name === 'core/navigation'
256
- );
257
-
258
- return navigationBlock.attributes.ref;
259
- } );
260
-
261
- if ( ! menuRef ) {
262
- throw 'getNavigationMenuRawContent was unable to find a ref attribute on the first navigation block';
263
- }
264
-
265
- const response = await rest( {
266
- method: 'GET',
267
- path: `/wp/v2/navigation/${ menuRef }?context=edit`,
268
- } );
269
-
270
- return stripPageIds( response.content.raw );
271
- }
272
-
273
- async function waitForBlock( blockName ) {
274
- const blockSelector = `[aria-label="Editor content"][role="region"] [aria-label="Block: ${ blockName }"]`;
275
-
276
- // Wait for a Submenu block before making assertion.
277
- return page.waitForSelector( blockSelector );
278
- }
279
-
280
- // Disable reason - these tests are to be re-written.
281
- // Skipped temporarily due to issues with GH actions: https://wordpress.slack.com/archives/C02QB2JS7/p1661331673166269.
282
- // eslint-disable-next-line jest/no-disabled-tests
283
- describe.skip( 'Navigation', () => {
284
- const contributorUsername = `contributoruser_${ ++uniqueId }`;
285
- let contributorPassword;
286
-
287
- beforeAll( async () => {
288
- // Creation of the contributor user **MUST** be at the top level describe block
289
- // otherwise this test will become unstable. This action only happens once
290
- // so there is no huge performance hit.
291
- contributorPassword = await createUser( contributorUsername, {
292
- role: 'contributor',
293
- } );
294
- } );
295
-
296
- beforeEach( async () => {
297
- await deleteAll( [
298
- POSTS_ENDPOINT,
299
- PAGES_ENDPOINT,
300
- DRAFT_PAGES_ENDPOINT,
301
- NAVIGATION_MENUS_ENDPOINT,
302
- ] );
303
- await deleteAllClassicMenus();
304
- } );
305
-
306
- afterEach( async () => {
307
- await setUpResponseMocking( [] );
308
- } );
309
-
310
- afterAll( async () => {
311
- await deleteAll( [
312
- POSTS_ENDPOINT,
313
- PAGES_ENDPOINT,
314
- DRAFT_PAGES_ENDPOINT,
315
- NAVIGATION_MENUS_ENDPOINT,
316
- ] );
317
- await deleteAllClassicMenus();
318
-
319
- // As per the creation in the beforeAll() above, this
320
- // action must be done at the root level describe() block.
321
- await deleteUser( contributorUsername );
322
- } );
323
-
324
- describe( 'loading states', () => {
325
- it( 'does not show a loading indicator if there is no ref to a Navigation post and Nav Menus have loaded', async () => {
326
- await createNewPost();
327
-
328
- // Insert an empty block to trigger resolution of Nav Menu items.
329
- await insertBlock( 'Navigation' );
330
- await waitForBlock( 'Navigation' );
331
-
332
- await page.waitForXPath( "//button[text()='Select Menu']" );
333
-
334
- // Now we have Nav Menu items resolved. Continue to assert.
335
- await clickOnMoreMenuItem( 'Code editor' );
336
-
337
- const codeEditorInput = await page.waitForSelector(
338
- '.editor-post-text-editor'
339
- );
340
-
341
- // Simulate block behaviour when loading a page containing an unconfigured Nav block
342
- // that is not selected.
343
- await codeEditorInput.click();
344
- const markup = '<!-- wp:navigation /-->';
345
- await page.keyboard.type( markup );
346
- await clickButton( 'Exit code editor' );
347
-
348
- // Wait for block to render...
349
- const navBlock = await waitForBlock( 'Navigation' );
350
-
351
- // Test specifically for the primary loading indicator because a spinner also exists
352
- // in the hidden Placeholder component when it is loading.
353
- const loadingSpinner = await navBlock.$(
354
- '.wp-block-navigation__loading-indicator.components-spinner'
355
- );
356
-
357
- // We should not see the loading state if the block has not been configured and is empty.
358
- expect( loadingSpinner ).toBeNull();
359
- } );
360
-
361
- // Skip reason: This test is quite flaky recently.
362
- // See https://github.com/WordPress/gutenberg/issues/39231.
363
- // eslint-disable-next-line jest/no-disabled-tests
364
- it.skip( 'shows a loading indicator whilst ref resolves to Navigation post items', async () => {
365
- const testNavId = 1;
366
-
367
- let resolveNavigationRequest;
368
-
369
- // Mock the request for the single Navigation post in order to fully
370
- // control the resolution of the request. This will enable the ability
371
- // to assert on how the UI responds during the API resolution without
372
- // relying on variable factors such as network conditions.
373
- await setUpResponseMocking( [
374
- {
375
- match: ( request ) => {
376
- return decodeURIComponent( request.url() ).includes(
377
- `navigation/`
378
- );
379
- },
380
- onRequestMatch: ( request ) => {
381
- // The Promise simulates a REST API request whose resolultion
382
- // the test has full control over.
383
- return new Promise( ( resolve ) => {
384
- // Assign the resolution function to the var in the
385
- // upper scope to afford control over resolution.
386
- resolveNavigationRequest = resolve;
387
-
388
- // Call request.continue() is required to fully resolve the mock.
389
- } ).then( () => request.continue() );
390
- },
391
- },
392
- ] );
393
- /*
394
- Expected mock function not to be called but it was called with: ["POST", "http://localhost:8889/wp-admin/admin-ajax.php", "http://localhost:8889/wp-admin/admin-ajax.php"],["GET", "http://localhost:8889/wp-admin/post-new.php", "http://localhost:8889/wp-admin/post-new.php"],["GET", "http://localhost:8889/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css?ver=4.2.16", "http://localhost:8889/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css?ver=4.2.16"],["GET", "http://localhost:8889/wp-includes/js/mediaelement/wp-mediaelement.min.css?ver=6.1-alpha-53506", "http://localhost:8889/wp-includes/js/mediaelement/wp-mediaelement.min.css?ver=6.1-alpha-53506"],["GET", "http://localhost:8889/wp-includes/js/imgareaselect/imgareaselect.css?ver=0.9.8", "http://localhost:8889/wp-includes/js/imgareaselect/imgareaselect.css?ver=0.9.8"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/components/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/components/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-editor/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-editor/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/nux/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/nux/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/reusable-blocks/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/reusable-blocks/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/editor/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/editor/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/reset.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/reset.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/edit-post/classic.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/edit-post/classic.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/editor.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/editor.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/edit-post/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/edit-post/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-directory/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-directory/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/format-library/style.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/format-library/style.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/themes/twentytwentyone/assets/css/custom-color-overrides.css?ver=1.6", "http://localhost:8889/wp-content/themes/twentytwentyone/assets/css/custom-color-overrides.css?ver=1.6"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/theme.css?ver=1655290402", "http://localhost:8889/wp-content/plugins/gutenberg/build/block-library/theme.css?ver=1655290402"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/blob/index.min.js?ver=bccaf46e493181a8db9a", "http://localhost:8889/wp-content/plugins/gutenberg/build/blob/index.min.js?ver=bccaf46e493181a8db9a"],["GET", "http://localhost:8889/wp-content/plugins/gutenberg/build/autop/index.min.js?ver=b1a2f86387be4fa46f89", "http://loca
395
- */
396
- await createNewPost();
397
- await clickOnMoreMenuItem( 'Code editor' );
398
- const codeEditorInput = await page.waitForSelector(
399
- '.editor-post-text-editor'
400
- );
401
- await codeEditorInput.click();
402
-
403
- // The ID used in this `ref` is that which we mock in the request
404
- // above to ensure we can control the resolution of the Navigation post.
405
- const markup = `<!-- wp:navigation {"ref":${ testNavId }} /-->`;
406
- await page.keyboard.type( markup );
407
- await clickButton( 'Exit code editor' );
408
-
409
- const navBlock = await waitForBlock( 'Navigation' );
410
-
411
- // Check for the spinner to be present whilst loading.
412
- await navBlock.waitForSelector( '.components-spinner' );
413
-
414
- // Resolve the controlled mocked API request.
415
- if ( typeof resolveNavigationRequest === 'function' ) {
416
- resolveNavigationRequest();
417
- }
418
- } );
419
-
420
- it( 'shows a loading indicator whilst empty Navigation menu is being created', async () => {
421
- const testNavId = 1;
422
-
423
- let resolveNavigationRequest;
424
-
425
- // Mock the request for the single Navigation post in order to fully
426
- // control the resolution of the request. This will enable the ability
427
- // to assert on how the UI responds during the API resolution without
428
- // relying on variable factors such as network conditions.
429
- await setUpResponseMocking( [
430
- {
431
- match: ( request ) =>
432
- request.url().includes( `rest_route` ) &&
433
- request.url().includes( `navigation` ) &&
434
- request.url().includes( `${ testNavId }?` ),
435
- onRequestMatch: ( request ) => {
436
- // The Promise simulates a REST API request whose resolultion
437
- // the test has full control over.
438
- return new Promise( ( resolve ) => {
439
- // Assign the resolution function to the var in the
440
- // upper scope to afford control over resolution.
441
- resolveNavigationRequest = resolve;
442
-
443
- // Call request.continue() is required to fully resolve the mock.
444
- } ).then( () => request.continue() );
445
- },
446
- },
447
- ] );
448
-
449
- await createNewPost();
450
- await insertBlock( 'Navigation' );
451
-
452
- const navBlock = await waitForBlock( 'Navigation' );
453
-
454
- // Create empty Navigation block with no items
455
- const navigationSelector = await page.waitForXPath(
456
- "//button[text()='Select Menu']"
457
- );
458
- await navigationSelector.click();
459
-
460
- const createNewMenuButton = await page.waitForXPath(
461
- '//button[contains(., "Create new menu")]'
462
- );
463
- await createNewMenuButton.click();
464
-
465
- // Check for the spinner to be present whilst loading.
466
- await navBlock.waitForSelector( '.components-spinner' );
467
-
468
- // Resolve the controlled mocked API request.
469
- if ( typeof resolveNavigationRequest === 'function' ) {
470
- resolveNavigationRequest();
471
- }
472
- } );
473
- } );
474
-
475
- describe( 'Placeholder', () => {
476
- describe( 'fallback states', () => {
477
- it( 'shows page list on insertion of block', async () => {
478
- await createNewPost();
479
- await insertBlock( 'Navigation' );
480
- await waitForBlock( 'Page List' );
481
- } );
482
-
483
- it( 'shows placeholder preview when block with no menu items is not selected', async () => {
484
- await createNewPost();
485
- await insertBlock( 'Navigation' );
486
-
487
- const navigationSelector = await page.waitForXPath(
488
- "//button[text()='Select Menu']"
489
- );
490
- await navigationSelector.click();
491
-
492
- const createNewMenuButton = await page.waitForXPath(
493
- '//button[contains(., "Create new menu")]'
494
- );
495
- await createNewMenuButton.click();
496
-
497
- // Wait for Navigation creation to complete.
498
- await page.waitForXPath(
499
- '//*[contains(@class, "components-snackbar")]/*[text()="Navigation Menu successfully created."]'
500
- );
501
-
502
- // Wait for block to resolve
503
- let navBlock = await waitForBlock( 'Navigation' );
504
-
505
- // Deselect the Nav block by inserting a new block at the root level
506
- // outside of the Nav block.
507
- await insertBlock( 'Paragraph' );
508
-
509
- // Acquire fresh reference to block
510
- navBlock = await waitForBlock( 'Navigation' );
511
-
512
- // Check Placeholder Preview is visible.
513
- await navBlock.waitForSelector(
514
- '.wp-block-navigation-placeholder__preview',
515
- { visible: true }
516
- );
517
-
518
- // Check the block's appender is not visible.
519
- const blockAppender = await navBlock.$(
520
- '.block-list-appender'
521
- );
522
-
523
- expect( blockAppender ).toBeNull();
524
- } );
525
- } );
526
-
527
- describe( 'menu selector actions', () => {
528
- it( 'allows a navigation block to be created from existing menus', async () => {
529
- await createClassicMenu( { name: 'Test Menu 1' } );
530
- await createClassicMenu(
531
- { name: 'Test Menu 2' },
532
- menuItemsFixture
533
- );
534
-
535
- await createNewPost();
536
- await insertBlock( 'Navigation' );
537
- await selectClassicMenu( 'Test Menu 2' );
538
-
539
- // Wait for a navigation link block before making assertion.
540
- await page.waitForSelector(
541
- '*[aria-label="Block: Custom Link"]'
542
- );
543
- expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
544
- } );
545
-
546
- it( 'creates an empty navigation block when the selected existing menu is also empty', async () => {
547
- await createClassicMenu( { name: 'Test Menu 1' } );
548
- await createNewPost();
549
- await insertBlock( 'Navigation' );
550
- await selectClassicMenu( 'Test Menu 1' );
551
-
552
- await page.waitForNetworkIdle();
553
-
554
- // Wait for the appender so that we know the navigation menu was created.
555
- await page.waitForSelector(
556
- 'nav[aria-label="Block: Navigation"] button[aria-label="Add block"]'
557
- );
558
- expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
559
- } );
560
-
561
- it( 'does not display the options to create from existing menus if there are no existing menus', async () => {
562
- await createNewPost();
563
-
564
- await insertBlock( 'Navigation' );
565
-
566
- const navigationSelector = await page.waitForXPath(
567
- "//button[text()='Select Menu']"
568
- );
569
- await navigationSelector.click();
570
-
571
- await page.waitForXPath(
572
- '//button[contains(., "Create new menu")]'
573
- );
574
-
575
- await page.waitForSelector( '.components-menu-group' );
576
-
577
- const placeholderActionsLength = await page.$$eval(
578
- '.components-menu-group',
579
- ( els ) => els.length
580
- );
581
-
582
- // Should only be showing "Create new menu".
583
- expect( placeholderActionsLength ).toEqual( 1 );
584
- } );
585
- } );
586
- } );
587
-
588
- it( 'allows an empty navigation block to be created and manually populated using a mixture of internal and external links', async () => {
589
- await createNewPost();
590
- await insertBlock( 'Navigation' );
591
-
592
- await showBlockToolbar();
593
-
594
- const navigationSelector = await page.waitForXPath(
595
- "//button[text()='Select Menu']"
596
- );
597
- await navigationSelector.click();
598
-
599
- const createNewMenuButton = await page.waitForXPath(
600
- '//button[contains(., "Create new menu")]'
601
- );
602
- await createNewMenuButton.click();
603
-
604
- await page.waitForNetworkIdle();
605
-
606
- // Await "success" notice.
607
- await page.waitForXPath(
608
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
609
- );
610
-
611
- const appender = await page.waitForSelector(
612
- '.wp-block-navigation .block-editor-button-block-appender'
613
- );
614
- await appender.click();
615
-
616
- // Add a link to the Link block.
617
- await updateActiveNavigationLink( {
618
- url: 'https://wordpress.org',
619
- label: 'WP',
620
- type: 'url',
621
- } );
622
-
623
- // Select the parent navigation block to show the appender.
624
- await showBlockToolbar();
625
- await page.click( 'button[aria-label="Select Navigation"]' );
626
-
627
- const appenderAgain = await page.waitForSelector(
628
- '.wp-block-navigation .block-list-appender'
629
- );
630
- await appenderAgain.click();
631
-
632
- // After adding a new block, search input should be shown immediately.
633
- // Verify that Escape would close the popover.
634
- // Regression: https://github.com/WordPress/gutenberg/pull/19885
635
- // Wait for URL input to be focused.
636
- await page.waitForSelector(
637
- 'input.block-editor-url-input__input:focus'
638
- );
639
-
640
- // After adding a new block, search input should be shown immediately.
641
- const isInURLInput = await page.evaluate(
642
- () =>
643
- !! document.activeElement.matches(
644
- 'input.block-editor-url-input__input'
645
- )
646
- );
647
- expect( isInURLInput ).toBe( true );
648
- await page.keyboard.press( 'Escape' );
649
-
650
- // Click the link placeholder.
651
- const placeholder = await page.waitForSelector(
652
- '.wp-block-navigation-link__placeholder'
653
- );
654
- await placeholder.click();
655
-
656
- // For the second nav link block use an existing internal page.
657
- // Mock the api response so that it's consistent.
658
- await mockSearchResponse( [
659
- { title: 'Get in Touch', slug: 'get-in-touch' },
660
- ] );
661
-
662
- // Add a link to the default Link block.
663
- await updateActiveNavigationLink( {
664
- url: 'Get in Touch',
665
- label: 'Contact',
666
- type: 'entity',
667
- } );
668
-
669
- await publishPost();
670
-
671
- // Expect a Navigation Block with two Links in the snapshot.
672
- expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
673
- } );
674
-
675
- it( 'encodes URL when create block if needed', async () => {
676
- await createNewPost();
677
- await insertBlock( 'Navigation' );
678
- const navigationSelector = await page.waitForXPath(
679
- "//button[text()='Select Menu']"
680
- );
681
- await navigationSelector.click();
682
-
683
- const createNewMenuButton = await page.waitForXPath(
684
- '//button[contains(., "Create new menu")]'
685
- );
686
- await createNewMenuButton.click();
687
-
688
- await page.waitForNetworkIdle();
689
-
690
- // Await "success" notice.
691
- await page.waitForXPath(
692
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
693
- );
694
-
695
- const appender = await page.waitForSelector(
696
- '.wp-block-navigation .block-editor-button-block-appender'
697
- );
698
- await appender.click();
699
-
700
- // Add a link to the Link block.
701
- await updateActiveNavigationLink( {
702
- url: 'https://wordpress.org/шеллы',
703
- type: 'url',
704
- } );
705
-
706
- await showBlockToolbar();
707
- await page.click( 'button[aria-label="Select Navigation"]' );
708
-
709
- const appenderAgain = await page.waitForSelector(
710
- '.wp-block-navigation .block-list-appender'
711
- );
712
- await appenderAgain.click();
713
-
714
- // Wait for URL input to be focused.
715
- await page.waitForSelector(
716
- 'input.block-editor-url-input__input:focus'
717
- );
718
-
719
- // After adding a new block, search input should be shown immediately.
720
- const isInURLInput = await page.evaluate(
721
- () =>
722
- !! document.activeElement.matches(
723
- 'input.block-editor-url-input__input'
724
- )
725
- );
726
- expect( isInURLInput ).toBe( true );
727
- await page.keyboard.press( 'Escape' );
728
-
729
- // Click the link placeholder.
730
- const placeholder = await page.waitForSelector(
731
- '.wp-block-navigation-link__placeholder'
732
- );
733
- await placeholder.click();
734
-
735
- // Mocked response for internal page.
736
- // We are encoding the slug/url in order
737
- // that we can assert it is not double encoded by the block.
738
- await mockSearchResponse( [
739
- { title: 'お問い合わせ', slug: encodeURI( 'お問い合わせ' ) },
740
- ] );
741
-
742
- // Select the mocked internal page above.
743
- await updateActiveNavigationLink( {
744
- url: 'お問い合わせ',
745
- type: 'entity',
746
- } );
747
-
748
- await publishPost();
749
-
750
- // Expect a Navigation Block with two Links in the snapshot.
751
- // The 2nd link should not be double encoded.
752
- expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
753
- } );
754
-
755
- it( 'allows pages to be created from the navigation block and their links added to menu', async () => {
756
- await createNewPost();
757
- await insertBlock( 'Navigation' );
758
-
759
- const navigationSelector = await page.waitForXPath(
760
- "//button[text()='Select Menu']"
761
- );
762
- await navigationSelector.click();
763
-
764
- const createNewMenuButton = await page.waitForXPath(
765
- '//button[contains(., "Create new menu")]'
766
- );
767
- await createNewMenuButton.click();
768
-
769
- await page.waitForNetworkIdle();
770
-
771
- // Await "success" notice.
772
- await page.waitForXPath(
773
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
774
- );
775
-
776
- const appender = await page.waitForSelector(
777
- '.wp-block-navigation .block-editor-button-block-appender'
778
- );
779
- await appender.click();
780
-
781
- // Wait for URL input to be focused
782
- // Insert name for the new page.
783
- const pageTitle = 'A really long page name that will not exist';
784
- const input = await page.waitForSelector(
785
- 'input.block-editor-url-input__input:focus'
786
- );
787
- await input.type( pageTitle );
788
-
789
- // When creating a page, the URLControl makes a request to the
790
- // url-details endpoint to fetch information about the page.
791
- // Because the draft is inaccessible publicly, this request
792
- // returns a 404 response. Wait for the response and expect
793
- // the error to have occurred.
794
- const createPageButton = await page.waitForSelector(
795
- '.block-editor-link-control__search-create'
796
- );
797
- const responsePromise = page.waitForResponse(
798
- ( response ) =>
799
- response.url().includes( 'url-details' ) &&
800
- response.status() === 404
801
- );
802
- const createPagePromise = createPageButton.click();
803
- await Promise.all( [ responsePromise, createPagePromise ] );
804
-
805
- // Creating a draft is async, so wait for a sign of completion. In this
806
- // case the link that shows in the URL popover once a link is added.
807
- await page.waitForXPath(
808
- `//a[contains(@class, "block-editor-link-control__search-item-title") and contains(., "${ pageTitle }")]`
809
- );
810
-
811
- await publishPost();
812
-
813
- // Expect a Navigation Block with a link for "A really long page name that will not exist".
814
- expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
815
- expect( console ).toHaveErroredWith(
816
- 'Failed to load resource: the server responded with a status of 404 (Not Found)'
817
- );
818
- } );
819
-
820
- it( 'correctly decodes special characters in the created Page title for display', async () => {
821
- await createNewPost();
822
- await insertBlock( 'Navigation' );
823
-
824
- const navigationSelector = await page.waitForXPath(
825
- "//button[text()='Select Menu']"
826
- );
827
- await navigationSelector.click();
828
-
829
- const createNewMenuButton = await page.waitForXPath(
830
- '//button[contains(., "Create new menu")]'
831
- );
832
- await createNewMenuButton.click();
833
-
834
- await page.waitForNetworkIdle();
835
-
836
- // Await "success" notice.
837
- await page.waitForXPath(
838
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
839
- );
840
-
841
- const appender = await page.waitForSelector(
842
- '.wp-block-navigation .block-editor-button-block-appender'
843
- );
844
- await appender.click();
845
-
846
- // Wait for URL input to be focused
847
- // Insert name for the new page.
848
- const pageTitle = 'This & That & Some < other > chars';
849
- const input = await page.waitForSelector(
850
- 'input.block-editor-url-input__input:focus'
851
- );
852
- await input.type( pageTitle );
853
-
854
- // When creating a page, the URLControl makes a request to the
855
- // url-details endpoint to fetch information about the page.
856
- // Because the draft is inaccessible publicly, this request
857
- // returns a 404 response. Wait for the response and expect
858
- // the error to have occurred.
859
- const createPageButton = await page.waitForSelector(
860
- '.block-editor-link-control__search-create'
861
- );
862
- const responsePromise = page.waitForResponse(
863
- ( response ) =>
864
- response.url().includes( 'url-details' ) &&
865
- response.status() === 404
866
- );
867
- const createPagePromise = createPageButton.click();
868
- await Promise.all( [ responsePromise, createPagePromise ] );
869
-
870
- await waitForBlock( 'Navigation' );
871
-
872
- const innerLinkBlock = await waitForBlock( 'Custom Link' );
873
-
874
- const linkText = await innerLinkBlock.$eval(
875
- '[aria-label="Navigation link text"]',
876
- ( element ) => {
877
- return element.innerText;
878
- }
879
- );
880
-
881
- expect( linkText ).toContain( pageTitle );
882
-
883
- expect( console ).toHaveErroredWith(
884
- 'Failed to load resource: the server responded with a status of 404 (Not Found)'
885
- );
886
- } );
887
-
888
- it( 'renders buttons for the submenu opener elements when the block is set to open on click instead of hover', async () => {
889
- await createClassicMenu( { name: 'Test Menu 2' }, menuItemsFixture );
890
- await createNewPost();
891
- await insertBlock( 'Navigation' );
892
- await selectClassicMenu( 'Test Menu 2' );
893
-
894
- await ensureSidebarOpened();
895
- const openOnClickButton = await page.waitForXPath(
896
- '//label[contains(text(),"Open on click")]'
897
- );
898
-
899
- await openOnClickButton.click();
900
-
901
- await saveDraft();
902
-
903
- // Scope element selector to the Editor's "Content" region as otherwise it picks up on
904
- // block previews.
905
- const navSubmenuSelector =
906
- '[aria-label="Editor content"][role="region"] [aria-label="Block: Submenu"]';
907
-
908
- await page.waitForSelector( navSubmenuSelector );
909
-
910
- const navSubmenusLength = await page.$$eval(
911
- navSubmenuSelector,
912
- ( els ) => els.length
913
- );
914
-
915
- const navButtonTogglesSelector =
916
- '[aria-label="Editor content"][role="region"] [aria-label="Block: Submenu"] button.wp-block-navigation-item__content';
917
-
918
- await page.waitForSelector( navButtonTogglesSelector );
919
-
920
- const navButtonTogglesLength = await page.$$eval(
921
- navButtonTogglesSelector,
922
- ( els ) => els.length
923
- );
924
-
925
- // Assert the correct number of button toggles are present.
926
- expect( navSubmenusLength ).toEqual( navButtonTogglesLength );
927
- } );
928
-
929
- it( 'Shows the quick inserter when the block contains non-navigation specific blocks', async () => {
930
- await createNewPost();
931
- await insertBlock( 'Navigation' );
932
-
933
- const navigationSelector = await page.waitForXPath(
934
- "//button[text()='Select Menu']"
935
- );
936
- await navigationSelector.click();
937
-
938
- const createNewMenuButton = await page.waitForXPath(
939
- '//button[contains(., "Create new menu")]'
940
- );
941
- await createNewMenuButton.click();
942
-
943
- await page.waitForNetworkIdle();
944
-
945
- // Await "success" notice.
946
- await page.waitForXPath(
947
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
948
- );
949
-
950
- const appender = await page.waitForSelector(
951
- '.wp-block-navigation .block-editor-button-block-appender'
952
- );
953
- await appender.click();
954
-
955
- // Add a link to the Link block.
956
- await updateActiveNavigationLink( {
957
- url: 'https://wordpress.org',
958
- label: 'WP',
959
- type: 'url',
960
- } );
961
-
962
- // Now add a different block type.
963
- await insertBlock( 'Site Title' );
964
-
965
- await showBlockToolbar();
966
- await page.click( 'button[aria-label="Select Navigation"]' );
967
- const appenderAgain = await page.waitForSelector(
968
- '.wp-block-navigation .block-list-appender'
969
- );
970
- await appenderAgain.click();
971
-
972
- const quickInserter = await page.waitForSelector(
973
- '.block-editor-inserter__quick-inserter'
974
- );
975
-
976
- // Expect the quick inserter to be truthy, which it will be because we
977
- // waited for it. It's nice to end a test with an assertion though.
978
- expect( quickInserter ).toBeTruthy();
979
- } );
980
-
981
- describe( 'Creating and restarting', () => {
982
- const NAV_ENTITY_SELECTOR =
983
- '//div[@class="entities-saved-states__panel"]//label//strong[contains(text(), "Navigation")]';
984
-
985
- it( 'respects the nesting level', async () => {
986
- await createNewPost();
987
-
988
- await insertBlock( 'Navigation' );
989
-
990
- const navBlock = await waitForBlock( 'Navigation' );
991
-
992
- const navigationSelector = await page.waitForXPath(
993
- "//button[text()='Select Menu']"
994
- );
995
- await navigationSelector.click();
996
-
997
- const createNewMenuButton = await page.waitForXPath(
998
- '//button[contains(., "Create new menu")]'
999
- );
1000
- await createNewMenuButton.click();
1001
-
1002
- await page.waitForNetworkIdle();
1003
-
1004
- // Await "success" notice.
1005
- await page.waitForXPath(
1006
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1007
- );
1008
-
1009
- const appender = await page.waitForSelector(
1010
- '.wp-block-navigation .block-editor-button-block-appender'
1011
- );
1012
- await appender.click();
1013
-
1014
- await clickOnMoreMenuItem( 'Code editor' );
1015
- const codeEditorInput = await page.waitForSelector(
1016
- '.editor-post-text-editor'
1017
- );
1018
-
1019
- let code = await codeEditorInput.evaluate( ( el ) => el.value );
1020
- code = code.replace( '} /-->', ',"maxNestingLevel":0} /-->' );
1021
- await codeEditorInput.evaluate(
1022
- ( el, newCode ) => ( el.value = newCode ),
1023
- code
1024
- );
1025
- await clickButton( 'Exit code editor' );
1026
-
1027
- const blockAppender = navBlock.$( '.block-list-appender' );
1028
-
1029
- expect( blockAppender ).not.toBeNull();
1030
-
1031
- // Check the Submenu block is no longer present.
1032
- const navSubmenuSelector =
1033
- '[aria-label="Editor content"][role="region"] [aria-label="Block: Submenu"]';
1034
- const submenuBlock = await page.$( navSubmenuSelector );
1035
-
1036
- expect( submenuBlock ).toBeFalsy();
1037
- } );
1038
-
1039
- it( 'retains initial uncontrolled inner blocks whilst there are no modifications to those blocks', async () => {
1040
- await createNewPost();
1041
- await clickOnMoreMenuItem( 'Code editor' );
1042
- const codeEditorInput = await page.waitForSelector(
1043
- '.editor-post-text-editor'
1044
- );
1045
- await codeEditorInput.click();
1046
-
1047
- const markup =
1048
- '<!-- wp:navigation --><!-- wp:page-list /--><!-- /wp:navigation -->';
1049
- await page.keyboard.type( markup );
1050
- await clickButton( 'Exit code editor' );
1051
-
1052
- const navBlock = await waitForBlock( 'Navigation' );
1053
-
1054
- // Select the block
1055
- await navBlock.click();
1056
-
1057
- const hasUncontrolledInnerBlocks = await page.evaluate( () => {
1058
- const blocks = wp.data
1059
- .select( 'core/block-editor' )
1060
- .getBlocks();
1061
- return !! blocks[ 0 ]?.innerBlocks?.length;
1062
- } );
1063
-
1064
- expect( hasUncontrolledInnerBlocks ).toBe( true );
1065
- } );
1066
-
1067
- it( 'converts uncontrolled inner blocks to an entity when modifications are made to the blocks', async () => {
1068
- await rest( {
1069
- method: 'POST',
1070
- path: `/wp/v2/pages/`,
1071
- data: {
1072
- status: 'publish',
1073
- title: 'A Test Page',
1074
- content: 'Hello world',
1075
- },
1076
- } );
1077
-
1078
- // Insert 'old-school' inner blocks via the code editor.
1079
- await createNewPost();
1080
- await clickOnMoreMenuItem( 'Code editor' );
1081
- const codeEditorInput = await page.waitForSelector(
1082
- '.editor-post-text-editor'
1083
- );
1084
- await codeEditorInput.click();
1085
- const markup =
1086
- '<!-- wp:navigation --><!-- wp:page-list /--><!-- /wp:navigation -->';
1087
- await page.keyboard.type( markup );
1088
-
1089
- await clickButton( 'Exit code editor' );
1090
-
1091
- const navBlock = await waitForBlock( 'Navigation' );
1092
-
1093
- await navBlock.click();
1094
-
1095
- // Wait for the Page List to have resolved and render as a `<ul>`.
1096
- await page.waitForSelector(
1097
- `[aria-label="Editor content"][role="region"] ul[aria-label="Block: Page List"]`
1098
- );
1099
-
1100
- // Select the Page List block.
1101
- await openListView();
1102
-
1103
- const navExpander = await page.waitForXPath(
1104
- `//a[.//span[text()='Navigation']]/span[contains(@class, 'block-editor-list-view__expander')]`
1105
- );
1106
-
1107
- await navExpander.click();
1108
-
1109
- const pageListBlock = (
1110
- await getListViewBlocks( 'Page List' )
1111
- )[ 0 ];
1112
-
1113
- await pageListBlock.click();
1114
-
1115
- // Modify the uncontrolled inner blocks by converting Page List.
1116
- await clickBlockToolbarButton( 'Edit' );
1117
-
1118
- // Must wait for button to be enabled.
1119
- const convertButton = await page.waitForXPath(
1120
- `//button[not(@disabled) and text()="Convert"]`
1121
- );
1122
-
1123
- await convertButton.click();
1124
-
1125
- // Wait for new Nav Menu entity to be created as a result of the modification to inner blocks.
1126
- await page.waitForXPath(
1127
- `//*[contains(@class, 'components-snackbar__content')][ text()="New Navigation Menu created." ]`
1128
- );
1129
-
1130
- await publishPost();
1131
-
1132
- // Check that the wp_navigation post exists and has the page list block.
1133
- expect( await getNavigationMenuRawContent() ).toMatchSnapshot();
1134
- } );
1135
-
1136
- it( 'only updates a single entity currently linked with the block', async () => {
1137
- await createNewPost();
1138
- await insertBlock( 'Navigation' );
1139
-
1140
- const navigationSelector = await page.waitForXPath(
1141
- "//button[text()='Select Menu']"
1142
- );
1143
- await navigationSelector.click();
1144
-
1145
- const createNewMenuButton = await page.waitForXPath(
1146
- '//button[contains(., "Create new menu")]'
1147
- );
1148
- await createNewMenuButton.click();
1149
-
1150
- await page.waitForNetworkIdle();
1151
-
1152
- // Await "success" notice.
1153
- await page.waitForXPath(
1154
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1155
- );
1156
-
1157
- const appender = await page.waitForSelector(
1158
- '.wp-block-navigation .block-editor-button-block-appender'
1159
- );
1160
- await appender.click();
1161
-
1162
- // Confirm that the menu entity was updated.
1163
- const publishPanelButton = await page.waitForSelector(
1164
- '.editor-post-publish-panel__toggle:not([aria-disabled="true"])'
1165
- );
1166
- await publishPanelButton.click();
1167
-
1168
- await page.waitForXPath( NAV_ENTITY_SELECTOR );
1169
- expect( await page.$x( NAV_ENTITY_SELECTOR ) ).toHaveLength( 1 );
1170
-
1171
- // Publish the post.
1172
- const entitySaveButton = await page.waitForSelector(
1173
- '.editor-entities-saved-states__save-button'
1174
- );
1175
- await entitySaveButton.click();
1176
- const publishButton = await page.waitForSelector(
1177
- '.editor-post-publish-button:not([aria-disabled="true"])'
1178
- );
1179
- await publishButton.click();
1180
-
1181
- // A success notice should show up.
1182
- await page.waitForXPath(
1183
- `//*[contains(@class, 'components-snackbar__content')][ text()="Post published." ]`
1184
- );
1185
-
1186
- // Now try inserting another Link block via the quick inserter.
1187
- // await page.click( 'nav[aria-label="Block: Navigation"]' );
1188
- await forceSelectNavigationBlock();
1189
-
1190
- const newNavigationSelector = await page.waitForXPath(
1191
- "//button[text()='Select Menu']"
1192
- );
1193
- await newNavigationSelector.click();
1194
-
1195
- const newCreateNewMenuButton = await page.waitForXPath(
1196
- '//button[contains(., "Create new menu")]'
1197
- );
1198
- await newCreateNewMenuButton.click();
1199
-
1200
- await page.waitForNetworkIdle();
1201
-
1202
- // Await "success" notice.
1203
- await page.waitForXPath(
1204
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1205
- );
1206
-
1207
- const newAppender = await page.waitForSelector(
1208
- '.wp-block-navigation .block-editor-button-block-appender'
1209
- );
1210
- await newAppender.click();
1211
-
1212
- // Confirm that only the last menu entity was updated.
1213
- const publishPanelButton2 = await page.waitForSelector(
1214
- '.editor-post-publish-button__button:not([aria-disabled="true"])'
1215
- );
1216
- await publishPanelButton2.click();
1217
-
1218
- await page.waitForXPath( NAV_ENTITY_SELECTOR );
1219
- expect( await page.$x( NAV_ENTITY_SELECTOR ) ).toHaveLength( 1 );
1220
- } );
1221
- } );
1222
-
1223
- it( 'applies accessible label to block element', async () => {
1224
- await createNewPost();
1225
- await insertBlock( 'Navigation' );
1226
-
1227
- const navigationSelector = await page.waitForXPath(
1228
- "//button[text()='Select Menu']"
1229
- );
1230
- await navigationSelector.click();
1231
-
1232
- const createNewMenuButton = await page.waitForXPath(
1233
- '//button[contains(., "Create new menu")]'
1234
- );
1235
- await createNewMenuButton.click();
1236
-
1237
- await page.waitForNetworkIdle();
1238
-
1239
- // Await "success" notice.
1240
- await page.waitForXPath(
1241
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1242
- );
1243
-
1244
- const appender = await page.waitForSelector(
1245
- '.wp-block-navigation .block-editor-button-block-appender'
1246
- );
1247
- await appender.click();
1248
-
1249
- // Add a link to the Link block.
1250
- await updateActiveNavigationLink( {
1251
- url: 'https://wordpress.org',
1252
- label: 'WP',
1253
- type: 'url',
1254
- } );
1255
-
1256
- const previewPage = await openPreviewPage();
1257
- await previewPage.bringToFront();
1258
- await previewPage.waitForNetworkIdle();
1259
-
1260
- const isAccessibleLabelPresent = await previewPage.$(
1261
- 'nav[aria-label="Navigation"]'
1262
- );
1263
-
1264
- expect( isAccessibleLabelPresent ).toBeTruthy();
1265
- } );
1266
-
1267
- it( 'does not load the frontend script if no navigation blocks are present', async () => {
1268
- await createNewPost();
1269
- await insertBlock( 'Paragraph' );
1270
- await page.waitForSelector( 'p[data-title="Paragraph"]:focus' );
1271
- await page.keyboard.type( 'Hello' );
1272
-
1273
- const previewPage = await openPreviewPage();
1274
- await previewPage.bringToFront();
1275
- await previewPage.waitForNetworkIdle();
1276
-
1277
- const isScriptLoaded = await previewPage.evaluate(
1278
- () =>
1279
- null !==
1280
- document.querySelector(
1281
- 'script[src*="navigation/view.min.js"]'
1282
- )
1283
- );
1284
-
1285
- expect( isScriptLoaded ).toBe( false );
1286
- } );
1287
-
1288
- it( 'loads the frontend script only once even when multiple navigation blocks are present', async () => {
1289
- await createNewPost();
1290
- await insertBlock( 'Navigation' );
1291
-
1292
- const navigationSelector = await page.waitForXPath(
1293
- "//button[text()='Select Menu']"
1294
- );
1295
- await navigationSelector.click();
1296
-
1297
- const createNewMenuButton = await page.waitForXPath(
1298
- '//button[contains(., "Create new menu")]'
1299
- );
1300
- await createNewMenuButton.click();
1301
-
1302
- await page.waitForNetworkIdle();
1303
-
1304
- // Await "success" notice.
1305
- await page.waitForXPath(
1306
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1307
- );
1308
-
1309
- await insertBlock( 'Navigation' );
1310
-
1311
- const newNavigationSelector = await page.waitForXPath(
1312
- "//button[text()='Select Menu']"
1313
- );
1314
- await newNavigationSelector.click();
1315
-
1316
- const newCreateNewMenuButton = await page.waitForXPath(
1317
- '//button[contains(., "Create new menu")]'
1318
- );
1319
- await newCreateNewMenuButton.click();
1320
-
1321
- await page.waitForNetworkIdle();
1322
-
1323
- // Await "success" notice.
1324
- await page.waitForXPath(
1325
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1326
- );
1327
-
1328
- const previewPage = await openPreviewPage();
1329
- await previewPage.bringToFront();
1330
- await previewPage.waitForNetworkIdle();
1331
-
1332
- const tagCount = await previewPage.evaluate(
1333
- () =>
1334
- document.querySelectorAll(
1335
- 'script[src*="navigation/view.min.js"]'
1336
- ).length
1337
- );
1338
-
1339
- expect( tagCount ).toBe( 1 );
1340
- } );
1341
-
1342
- describe( 'Submenus', () => {
1343
- it( 'shows button which converts submenu to link when submenu is not-populated (empty)', async () => {
1344
- const navSubmenuSelector = `[aria-label="Editor content"][role="region"] [aria-label="Block: Submenu"]`;
1345
-
1346
- await createNewPost();
1347
- await insertBlock( 'Navigation' );
1348
-
1349
- const navigationSelector = await page.waitForXPath(
1350
- "//button[text()='Select Menu']"
1351
- );
1352
- await navigationSelector.click();
1353
-
1354
- const createNewMenuButton = await page.waitForXPath(
1355
- '//button[contains(., "Create new menu")]'
1356
- );
1357
- await createNewMenuButton.click();
1358
-
1359
- await page.waitForNetworkIdle();
1360
-
1361
- // Await "success" notice.
1362
- await page.waitForXPath(
1363
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1364
- );
1365
-
1366
- const appender = await page.waitForSelector(
1367
- '.wp-block-navigation .block-editor-button-block-appender'
1368
- );
1369
- await appender.click();
1370
-
1371
- await clickBlockToolbarButton( 'Add submenu' );
1372
-
1373
- await waitForBlock( 'Submenu' );
1374
-
1375
- // Revert the Submenu back to a Navigation Link block.
1376
- await clickBlockToolbarButton( 'Convert to Link' );
1377
-
1378
- // Check the Submenu block is no longer present.
1379
- const submenuBlock = await page.$( navSubmenuSelector );
1380
-
1381
- expect( submenuBlock ).toBeFalsy();
1382
- } );
1383
-
1384
- it( 'shows button to convert submenu to link in disabled state when submenu is populated', async () => {
1385
- await createNewPost();
1386
- await insertBlock( 'Navigation' );
1387
-
1388
- const navigationSelector = await page.waitForXPath(
1389
- "//button[text()='Select Menu']"
1390
- );
1391
- await navigationSelector.click();
1392
-
1393
- const createNewMenuButton = await page.waitForXPath(
1394
- '//button[contains(., "Create new menu")]'
1395
- );
1396
- await createNewMenuButton.click();
1397
-
1398
- await page.waitForNetworkIdle();
1399
-
1400
- // Await "success" notice.
1401
- await page.waitForXPath(
1402
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1403
- );
1404
-
1405
- const appender = await page.waitForSelector(
1406
- '.wp-block-navigation .block-editor-button-block-appender'
1407
- );
1408
- await appender.click();
1409
-
1410
- await updateActiveNavigationLink( {
1411
- url: 'https://make.wordpress.org/core/',
1412
- label: 'Menu item #1',
1413
- type: 'url',
1414
- } );
1415
-
1416
- await clickBlockToolbarButton( 'Add submenu' );
1417
-
1418
- await waitForBlock( 'Submenu' );
1419
-
1420
- // Add a Link block first.
1421
- const SubAppender = await page.waitForSelector(
1422
- '[aria-label="Block: Submenu"] [aria-label="Add block"]'
1423
- );
1424
-
1425
- await SubAppender.click();
1426
-
1427
- await updateActiveNavigationLink( {
1428
- url: 'https://make.wordpress.org/core/',
1429
- label: 'Submenu item #1',
1430
- type: 'url',
1431
- } );
1432
-
1433
- await clickBlockToolbarButton( 'Select Submenu' );
1434
-
1435
- // Check button exists but is in disabled state.
1436
- const disabledConvertToLinkButton = await page.$$eval(
1437
- '[aria-label="Block tools"] [aria-label="Convert to Link"][disabled]',
1438
- ( els ) => els.length
1439
- );
1440
-
1441
- expect( disabledConvertToLinkButton ).toEqual( 1 );
1442
- } );
1443
-
1444
- it( 'shows button to convert submenu to link when submenu is populated with a single incomplete link item', async () => {
1445
- // For context on why this test is required please see:
1446
- // https://github.com/WordPress/gutenberg/pull/38203#issuecomment-1027672948.
1447
-
1448
- await createNewPost();
1449
- await insertBlock( 'Navigation' );
1450
-
1451
- await clickBlockToolbarButton( 'Select Menu' );
1452
-
1453
- const createNewMenuButton = await page.waitForXPath(
1454
- '//button[contains(., "Create new menu")]'
1455
- );
1456
- await createNewMenuButton.click();
1457
-
1458
- await page.waitForNetworkIdle();
1459
-
1460
- // Await "success" notice.
1461
- await page.waitForXPath(
1462
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1463
- );
1464
-
1465
- const appender = await page.waitForSelector(
1466
- '.wp-block-navigation .block-editor-button-block-appender'
1467
- );
1468
- await appender.click();
1469
-
1470
- await updateActiveNavigationLink( {
1471
- url: 'https://make.wordpress.org/core/',
1472
- label: 'Menu item #1',
1473
- type: 'url',
1474
- } );
1475
-
1476
- await clickBlockToolbarButton( 'Add submenu' );
1477
-
1478
- await waitForBlock( 'Submenu' );
1479
-
1480
- // Add a Link block first.
1481
- const subAppender = await page.waitForSelector(
1482
- '[aria-label="Block: Submenu"] [aria-label="Add block"]'
1483
- );
1484
-
1485
- await subAppender.click();
1486
-
1487
- // Here we intentionally do not populate the inserted Navigation Link block.
1488
- // Rather we immediaely click away leaving the link in a state where it has
1489
- // no URL of label and can be considered unpopulated.
1490
- await clickBlockToolbarButton( 'Select Submenu' );
1491
-
1492
- // Check for non-disabled Convert to Link button.
1493
- const convertToLinkButton = await page.$(
1494
- '[aria-label="Block tools"] [aria-label="Convert to Link"]:not([disabled])'
1495
- );
1496
-
1497
- expect( convertToLinkButton ).toBeTruthy();
1498
- } );
1499
- } );
1500
-
1501
- describe( 'Permission based restrictions', () => {
1502
- afterEach( async () => {
1503
- await switchUserToAdmin();
1504
- } );
1505
-
1506
- it.skip( 'shows a warning if user does not have permission to create navigation menus', async () => {
1507
- const noticeText =
1508
- 'You do not have permission to create Navigation Menus.';
1509
-
1510
- // Switch to a Contributor role user - they should not have
1511
- // permission to update Navigations.
1512
- await loginUser( contributorUsername, contributorPassword );
1513
-
1514
- await createNewPost();
1515
- await insertBlock( 'Navigation' );
1516
-
1517
- // Make sure the snackbar error shows up.
1518
- await page.waitForXPath(
1519
- `//*[contains(@class, 'components-snackbar__content')][ text()="${ noticeText }" ]`
1520
- );
1521
-
1522
- // Expect a console 403 for requests to:
1523
- // * /wp/v2/settings?_locale=user
1524
- // * /wp/v2/templates?context=edit&post_type=post&per_page=100&_locale=user
1525
- expect( console ).toHaveErrored();
1526
- } );
1527
- } );
1528
-
1529
- describe( 'Initial block insertion state', () => {
1530
- async function createNavigationMenu( menu = {} ) {
1531
- return rest( {
1532
- method: 'POST',
1533
- path: '/wp/v2/navigation',
1534
- data: {
1535
- status: 'publish',
1536
- ...menu,
1537
- },
1538
- } );
1539
- }
1540
-
1541
- afterEach( async () => {
1542
- const navMenusEndpoint = '/wp/v2/navigation';
1543
- const allNavMenus = await rest( { path: navMenusEndpoint } );
1544
-
1545
- if ( ! allNavMenus?.length ) {
1546
- return;
1547
- }
1548
-
1549
- return batch(
1550
- allNavMenus.map( ( menu ) => ( {
1551
- method: 'DELETE',
1552
- path: `${ navMenusEndpoint }/${ menu.id }?force=true`,
1553
- } ) )
1554
- );
1555
- } );
1556
-
1557
- it( 'automatically uses the first Navigation Menu if only one is available', async () => {
1558
- await createNavigationMenu( {
1559
- title: 'Example Navigation',
1560
- content:
1561
- '<!-- wp:navigation-link {"label":"WordPress","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1562
- } );
1563
-
1564
- await createNewPost();
1565
-
1566
- await insertBlock( 'Navigation' );
1567
-
1568
- await waitForBlock( 'Navigation' );
1569
-
1570
- const innerLinkBlock = await waitForBlock( 'Custom Link' );
1571
-
1572
- const linkText = await innerLinkBlock.$eval(
1573
- '[aria-label="Navigation link text"]',
1574
- ( element ) => {
1575
- return element.innerText;
1576
- }
1577
- );
1578
-
1579
- expect( linkText ).toBe( 'WordPress' );
1580
- } );
1581
-
1582
- it( 'does not automatically use the first Navigation Menu if uncontrolled inner blocks are present', async () => {
1583
- const pageTitle = 'A Test Page';
1584
-
1585
- await createNavigationMenu( {
1586
- title: 'Example Navigation',
1587
- content:
1588
- '<!-- wp:navigation-link {"label":"First Nav Menu Item","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1589
- } );
1590
-
1591
- await rest( {
1592
- method: 'POST',
1593
- path: `/wp/v2/pages/`,
1594
- data: {
1595
- status: 'publish',
1596
- title: pageTitle,
1597
- content: 'Hello world',
1598
- },
1599
- } );
1600
-
1601
- await createNewPost();
1602
-
1603
- await clickOnMoreMenuItem( 'Code editor' );
1604
-
1605
- const codeEditorInput = await page.waitForSelector(
1606
- '.editor-post-text-editor'
1607
- );
1608
- await codeEditorInput.click();
1609
-
1610
- const markup =
1611
- '<!-- wp:navigation --><!-- wp:page-list /--><!-- /wp:navigation -->';
1612
- await page.keyboard.type( markup );
1613
- await clickButton( 'Exit code editor' );
1614
-
1615
- await waitForBlock( 'Navigation' );
1616
-
1617
- const hasUncontrolledInnerBlocks = await page.evaluate( () => {
1618
- const blocks = wp.data
1619
- .select( 'core/block-editor' )
1620
- .getBlocks();
1621
- return !! blocks[ 0 ]?.innerBlocks?.length;
1622
- } );
1623
-
1624
- expect( hasUncontrolledInnerBlocks ).toBe( true );
1625
- } );
1626
-
1627
- it( 'automatically uses most recent Navigation Menu if more than one exists', async () => {
1628
- await createNavigationMenu( {
1629
- title: 'Example Navigation',
1630
- content:
1631
- '<!-- wp:navigation-link {"label":"WordPress","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1632
- } );
1633
-
1634
- await createNavigationMenu( {
1635
- title: 'Second Example Navigation',
1636
- content:
1637
- '<!-- wp:navigation-link {"label":"WordPress","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1638
- } );
1639
-
1640
- await createNewPost();
1641
-
1642
- await insertBlock( 'Navigation' );
1643
-
1644
- await waitForBlock( 'Navigation' );
1645
-
1646
- const navigationSelector = await page.waitForXPath(
1647
- "//button[text()='Select Menu']"
1648
- );
1649
- await navigationSelector.click();
1650
-
1651
- await page.waitForXPath(
1652
- '//button[@aria-checked="true"][contains(., "Second Example Navigation")]'
1653
- );
1654
- } );
1655
-
1656
- it( 'allows users to manually create new empty menu when block has automatically selected the first available Navigation Menu', async () => {
1657
- await createNavigationMenu( {
1658
- title: 'Example Navigation',
1659
- content:
1660
- '<!-- wp:navigation-link {"label":"WordPress","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1661
- } );
1662
-
1663
- await createNewPost();
1664
-
1665
- await insertBlock( 'Navigation' );
1666
-
1667
- const navigationSelector = await page.waitForXPath(
1668
- "//button[text()='Select Menu']"
1669
- );
1670
- await navigationSelector.click();
1671
-
1672
- const createNewMenuButton = await page.waitForXPath(
1673
- '//button[contains(., "Create new menu")]'
1674
- );
1675
- await createNewMenuButton.click();
1676
-
1677
- await page.waitForNetworkIdle();
1678
-
1679
- // Await "success" notice.
1680
- await page.waitForXPath(
1681
- '//div[@class="components-snackbar__content"][contains(text(), "Navigation Menu successfully created.")]'
1682
- );
1683
- } );
1684
-
1685
- it( 'should always focus select menu button after item selection', async () => {
1686
- // Create some navigation menus to work with.
1687
- await createNavigationMenu( {
1688
- title: 'First navigation',
1689
- content:
1690
- '<!-- wp:navigation-link {"label":"WordPress Example Navigation","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1691
- } );
1692
- await createNavigationMenu( {
1693
- title: 'Second Navigation',
1694
- content:
1695
- '<!-- wp:navigation-link {"label":"WordPress Second Example Navigation","type":"custom","url":"http://www.wordpress.org/","kind":"custom","isTopLevelLink":true} /-->',
1696
- } );
1697
-
1698
- // Create new post.
1699
- await createNewPost();
1700
-
1701
- // Insert new block and wait for the insert to complete.
1702
- await insertBlock( 'Navigation' );
1703
- await waitForBlock( 'Navigation' );
1704
-
1705
- const navigationSelector = await page.waitForXPath(
1706
- "//button[text()='Select Menu']"
1707
- );
1708
- await navigationSelector.click();
1709
-
1710
- const theOption = await page.waitForXPath(
1711
- "//button[@aria-checked='false'][contains(., 'First navigation')]"
1712
- );
1713
- await theOption.click();
1714
-
1715
- // Once the options are closed, does select menu button receive focus?
1716
- const selectMenuDropdown2 = await page.$(
1717
- '[aria-label="Select Menu"]'
1718
- );
1719
-
1720
- await expect( selectMenuDropdown2 ).toHaveFocus();
1721
- } );
1722
- } );
1723
- } );