@wordpress/e2e-tests 2.5.12 → 3.0.1-next.33ec3857e2.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 (32) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/config/flaky-tests-reporter.js +2 -1
  3. package/package.json +10 -9
  4. package/specs/editor/blocks/__snapshots__/heading.test.js.snap +2 -2
  5. package/specs/editor/blocks/__snapshots__/navigation.test.js.snap +27 -59
  6. package/specs/editor/blocks/columns.test.js +1 -1
  7. package/specs/editor/blocks/cover.test.js +1 -1
  8. package/specs/editor/blocks/heading.test.js +15 -3
  9. package/specs/editor/blocks/image.test.js +1 -2
  10. package/specs/editor/blocks/navigation.test.js +264 -370
  11. package/specs/editor/blocks/preformatted.test.js +2 -1
  12. package/specs/editor/blocks/site-title.test.js +2 -31
  13. package/specs/editor/fixtures/menu-items-request-fixture.json +84 -0
  14. package/specs/editor/fixtures/menu-items-response-fixture.json +240 -144
  15. package/specs/editor/plugins/custom-post-types.test.js +4 -3
  16. package/specs/editor/various/adding-patterns.test.js +1 -1
  17. package/specs/editor/various/block-grouping.test.js +2 -18
  18. package/specs/editor/various/block-hierarchy-navigation.test.js +3 -3
  19. package/specs/editor/various/change-detection.test.js +5 -0
  20. package/specs/editor/various/editor-modes.test.js +7 -0
  21. package/specs/editor/various/font-size-picker.test.js +1 -7
  22. package/specs/editor/various/inserting-blocks.test.js +6 -2
  23. package/specs/editor/various/keyboard-navigable-blocks.test.js +6 -0
  24. package/specs/editor/various/list-view.test.js +2 -2
  25. package/specs/editor/various/navigable-toolbar.test.js +2 -2
  26. package/specs/editor/various/preview.test.js +1 -1
  27. package/specs/editor/various/reusable-blocks.test.js +1 -26
  28. package/specs/editor/various/writing-flow.test.js +8 -4
  29. package/specs/site-editor/document-settings.test.js +1 -1
  30. package/specs/site-editor/multi-entity-saving.test.js +108 -55
  31. package/specs/site-editor/template-part.test.js +88 -68
  32. package/specs/widgets/customizing-widgets.test.js +4 -0
@@ -7,6 +7,7 @@ import {
7
7
  deactivatePlugin,
8
8
  publishPost,
9
9
  findSidebarPanelWithTitle,
10
+ clickBlockAppender,
10
11
  } from '@wordpress/e2e-test-utils';
11
12
 
12
13
  const openPageAttributesPanel = async () => {
@@ -40,7 +41,7 @@ describe( 'Test Custom Post Types', () => {
40
41
 
41
42
  // Create a parent post.
42
43
  await createNewPost( { postType: 'hierar-no-title' } );
43
- await page.click( '.block-editor-writing-flow' );
44
+ await clickBlockAppender();
44
45
  await page.keyboard.type( 'Parent Post' );
45
46
  await publishPost();
46
47
  // Create a post that is a child of the previously created post.
@@ -55,7 +56,7 @@ describe( 'Test Custom Post Types', () => {
55
56
  ( element ) => element.textContent
56
57
  );
57
58
  await optionToSelect.click();
58
- await page.click( '.block-editor-writing-flow' );
59
+ await clickBlockAppender();
59
60
  await page.keyboard.type( 'Child Post' );
60
61
  await publishPost();
61
62
  // Reload the child post and verify it is still correctly selected as a child post.
@@ -72,7 +73,7 @@ describe( 'Test Custom Post Types', () => {
72
73
  } );
73
74
  it( 'should create a cpt with a legacy block in its template without WSOD', async () => {
74
75
  await createNewPost( { postType: 'leg_block_in_tpl' } );
75
- await page.click( '.block-editor-writing-flow' );
76
+ await clickBlockAppender();
76
77
  await page.keyboard.type( 'Hello there' );
77
78
  await page.waitForSelector( '[data-type="core/embed"]' );
78
79
  await publishPost();
@@ -7,7 +7,7 @@ import {
7
7
  getEditedPostContent,
8
8
  } from '@wordpress/e2e-test-utils';
9
9
 
10
- /** @typedef {import('puppeteer').ElementHandle} ElementHandle */
10
+ /** @typedef {import('puppeteer-core').ElementHandle} ElementHandle */
11
11
 
12
12
  describe( 'adding patterns', () => {
13
13
  beforeEach( async () => {
@@ -13,6 +13,7 @@ import {
13
13
  getAvailableBlockTransforms,
14
14
  activatePlugin,
15
15
  deactivatePlugin,
16
+ createReusableBlock,
16
17
  } from '@wordpress/e2e-test-utils';
17
18
 
18
19
  async function insertBlocksOfSameType() {
@@ -122,24 +123,7 @@ describe( 'Block Grouping', () => {
122
123
  };
123
124
 
124
125
  const paragraphText = 'hi';
125
- const reusableBlockNameInputSelector =
126
- '.reusable-blocks-menu-items__convert-modal .components-text-control__input';
127
- await insertBlock( 'Paragraph' );
128
- await page.keyboard.type( paragraphText );
129
-
130
- await clickBlockToolbarButton( 'Options' );
131
- await clickMenuItem( 'Add to Reusable blocks' );
132
- const nameInput = await page.waitForSelector(
133
- reusableBlockNameInputSelector
134
- );
135
- await nameInput.click();
136
- await page.keyboard.type( 'Block' );
137
- await page.keyboard.press( 'Enter' );
138
-
139
- // Wait for creation to finish
140
- await page.waitForXPath(
141
- '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block created."]'
142
- );
126
+ await createReusableBlock( paragraphText, 'Block' );
143
127
  // Group
144
128
  await clickBlockToolbarButton( 'Options' );
145
129
  await clickMenuItem( 'Group' );
@@ -51,7 +51,7 @@ describe( 'Navigating the block hierarchy', () => {
51
51
  await page.click( '.edit-post-header-toolbar__list-view-toggle' );
52
52
  const columnsBlockMenuItem = (
53
53
  await page.$x(
54
- "//button[contains(@class,'block-editor-list-view-block-select-button') and contains(text(), 'Columns')]"
54
+ "//a[contains(@class,'block-editor-list-view-block-select-button') and contains(text(), 'Columns')]"
55
55
  )
56
56
  )[ 0 ];
57
57
  await columnsBlockMenuItem.click();
@@ -75,7 +75,7 @@ describe( 'Navigating the block hierarchy', () => {
75
75
  // Navigate to the last column block.
76
76
  const lastColumnsBlockMenuItem = (
77
77
  await page.$x(
78
- "//button[contains(@class,'block-editor-list-view-block-select-button') and contains(text(), 'Column')]"
78
+ "//a[contains(@class,'block-editor-list-view-block-select-button') and contains(text(), 'Column')]"
79
79
  )
80
80
  )[ 3 ];
81
81
  await lastColumnsBlockMenuItem.click();
@@ -188,7 +188,7 @@ describe( 'Navigating the block hierarchy', () => {
188
188
  await page.click( '.edit-post-header-toolbar__list-view-toggle' );
189
189
  const groupMenuItem = (
190
190
  await page.$x(
191
- "//button[contains(@class,'block-editor-list-view-block-select-button') and contains(text(), 'Group')]"
191
+ "//a[contains(@class,'block-editor-list-view-block-select-button') and contains(text(), 'Group')]"
192
192
  )
193
193
  )[ 0 ];
194
194
  await groupMenuItem.click();
@@ -10,6 +10,7 @@ import {
10
10
  saveDraft,
11
11
  openDocumentSettingsSidebar,
12
12
  isCurrentURL,
13
+ openTypographyToolsPanelMenu,
13
14
  } from '@wordpress/e2e-test-utils';
14
15
 
15
16
  describe( 'Change detection', () => {
@@ -381,6 +382,10 @@ describe( 'Change detection', () => {
381
382
 
382
383
  // Change the paragraph's `drop cap`.
383
384
  await page.click( '[data-type="core/paragraph"]' );
385
+
386
+ await openTypographyToolsPanelMenu();
387
+ await page.click( 'button[aria-label="Show Drop cap"]' );
388
+
384
389
  const [ dropCapToggle ] = await page.$x(
385
390
  "//label[contains(text(), 'Drop cap')]"
386
391
  );
@@ -10,6 +10,7 @@ import {
10
10
  switchEditorModeTo,
11
11
  pressKeyTimes,
12
12
  pressKeyWithModifier,
13
+ openTypographyToolsPanelMenu,
13
14
  } from '@wordpress/e2e-test-utils';
14
15
 
15
16
  describe( 'Editing modes (visual/HTML)', () => {
@@ -54,6 +55,9 @@ describe( 'Editing modes (visual/HTML)', () => {
54
55
 
55
56
  // The `drop cap` toggle for the paragraph block should appear, even in
56
57
  // HTML editing mode.
58
+ await openTypographyToolsPanelMenu();
59
+ await page.click( 'button[aria-label="Show Drop cap"]' );
60
+
57
61
  const dropCapToggle = await page.$x(
58
62
  "//label[contains(text(), 'Drop cap')]"
59
63
  );
@@ -74,6 +78,9 @@ describe( 'Editing modes (visual/HTML)', () => {
74
78
  expect( htmlBlockContent ).toEqual( '<p>Hello world!</p>' );
75
79
 
76
80
  // Change the `drop cap` using the sidebar.
81
+ await openTypographyToolsPanelMenu();
82
+ await page.click( 'button[aria-label="Show Drop cap"]' );
83
+
77
84
  const [ dropCapToggle ] = await page.$x(
78
85
  "//label[contains(text(), 'Drop cap')]"
79
86
  );
@@ -8,6 +8,7 @@ import {
8
8
  pressKeyWithModifier,
9
9
  pressKeyTimes,
10
10
  activateTheme,
11
+ openTypographyToolsPanelMenu,
11
12
  } from '@wordpress/e2e-test-utils';
12
13
 
13
14
  const openFontSizeSelectControl = async () => {
@@ -17,13 +18,6 @@ const openFontSizeSelectControl = async () => {
17
18
  return selectControl.click();
18
19
  };
19
20
 
20
- const openTypographyToolsPanelMenu = async () => {
21
- const toggleSelector =
22
- "//div[contains(@class, 'typography-block-support-panel')]//button[contains(@class, 'components-dropdown-menu__toggle')]";
23
- const toggle = await page.waitForXPath( toggleSelector );
24
- return toggle.click();
25
- };
26
-
27
21
  const FONT_SIZE_TOGGLE_GROUP_SELECTOR =
28
22
  "//div[contains(@class, 'components-font-size-picker__controls')]//div[contains(@class, 'components-toggle-group-control')]";
29
23
 
@@ -14,7 +14,7 @@ import {
14
14
  pressKeyWithModifier,
15
15
  } from '@wordpress/e2e-test-utils';
16
16
 
17
- /** @typedef {import('puppeteer').ElementHandle} ElementHandle */
17
+ /** @typedef {import('puppeteer-core').ElementHandle} ElementHandle */
18
18
 
19
19
  /**
20
20
  * Waits for all patterns in the inserter to have a height, which should
@@ -173,7 +173,7 @@ describe( 'Inserting blocks', () => {
173
173
  // Check for regression of https://github.com/WordPress/gutenberg/issues/9583
174
174
  it( 'should not allow transfer of focus outside of the block-insertion menu once open', async () => {
175
175
  // Enter the default block and click the inserter toggle button to the left of it.
176
- await page.keyboard.press( 'ArrowDown' );
176
+ await page.keyboard.press( 'Enter' );
177
177
  await showBlockToolbar();
178
178
  await page.click(
179
179
  '.block-editor-block-list__empty-block-inserter .block-editor-inserter__toggle'
@@ -243,6 +243,10 @@ describe( 'Inserting blocks', () => {
243
243
  );
244
244
  expect( selectedButtonBlocks.length ).toBe( 1 );
245
245
 
246
+ // The block appender is only visible when there's no selection.
247
+ await page.evaluate( () =>
248
+ window.wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock()
249
+ );
246
250
  // Specifically click the root container appender.
247
251
  await page.click(
248
252
  '.block-editor-block-list__layout.is-root-container > .block-list-appender .block-editor-inserter__toggle'
@@ -155,6 +155,12 @@ describe( 'Order of block keyboard navigation', () => {
155
155
  .focus();
156
156
  } );
157
157
 
158
+ await pressKeyWithModifier( 'shift', 'Tab' );
159
+ await expect( await getActiveLabel() ).toBe( 'Add block' );
160
+
161
+ await pressKeyWithModifier( 'shift', 'Tab' );
162
+ await expect( await getActiveLabel() ).toBe( 'Add default block' );
163
+
158
164
  await pressKeyWithModifier( 'shift', 'Tab' );
159
165
  await expect( await getActiveLabel() ).toBe(
160
166
  'Paragraph Block. Row 2. 1'
@@ -42,12 +42,12 @@ describe( 'List view', () => {
42
42
  await pressKeyWithModifier( 'access', 'o' );
43
43
 
44
44
  const paragraphBlock = await page.waitForXPath(
45
- '//button[contains(., "Paragraph")][@draggable="true"]'
45
+ '//a[contains(., "Paragraph")][@draggable="true"]'
46
46
  );
47
47
 
48
48
  // Drag above the heading block
49
49
  const headingBlock = await page.waitForXPath(
50
- '//button[contains(., "Heading")][@draggable="true"]'
50
+ '//a[contains(., "Heading")][@draggable="true"]'
51
51
  );
52
52
 
53
53
  await dragAndDrop( paragraphBlock, headingBlock, -5 );
@@ -31,8 +31,8 @@ describe.each( [
31
31
 
32
32
  it( 'navigates in and out of toolbar by keyboard (Alt+F10, Escape)', async () => {
33
33
  // Assumes new post focus starts in title. Create first new
34
- // block by ArrowDown.
35
- await page.keyboard.press( 'ArrowDown' );
34
+ // block by Enter.
35
+ await page.keyboard.press( 'Enter' );
36
36
 
37
37
  // [TEMPORARY]: A new paragraph is not technically a block yet
38
38
  // until starting to type within it.
@@ -14,7 +14,7 @@ import {
14
14
  pressKeyWithModifier,
15
15
  } from '@wordpress/e2e-test-utils';
16
16
 
17
- /** @typedef {import('puppeteer').Page} Page */
17
+ /** @typedef {import('puppeteer-core').Page} Page */
18
18
 
19
19
  /**
20
20
  * Given the Page instance for the editor, opens preview drodpdown, and
@@ -14,6 +14,7 @@ import {
14
14
  toggleGlobalBlockInserter,
15
15
  openDocumentSettingsSidebar,
16
16
  saveDraft,
17
+ createReusableBlock,
17
18
  } from '@wordpress/e2e-test-utils';
18
19
 
19
20
  const reusableBlockNameInputSelector =
@@ -44,32 +45,6 @@ const clearAllBlocks = async () => {
44
45
  } );
45
46
  };
46
47
 
47
- const createReusableBlock = async ( content, title ) => {
48
- // Insert a paragraph block
49
- await insertBlock( 'Paragraph' );
50
- await page.keyboard.type( content );
51
-
52
- await clickBlockToolbarButton( 'Options' );
53
- await clickMenuItem( 'Add to Reusable blocks' );
54
- const nameInput = await page.waitForSelector(
55
- reusableBlockNameInputSelector
56
- );
57
- await nameInput.click();
58
- await page.keyboard.type( title );
59
- await page.keyboard.press( 'Enter' );
60
-
61
- // Wait for creation to finish
62
- await page.waitForXPath(
63
- '//*[contains(@class, "components-snackbar")]/*[text()="Reusable block created."]'
64
- );
65
-
66
- // Check that we have a reusable block on the page
67
- const block = await page.waitForSelector(
68
- '.block-editor-block-list__block[data-type="core/block"]'
69
- );
70
- expect( block ).not.toBeNull();
71
- };
72
-
73
48
  describe( 'Reusable blocks', () => {
74
49
  afterAll( async () => {
75
50
  await trashAllPosts( 'wp_block' );
@@ -45,9 +45,11 @@ const addParagraphsAndColumnsDemo = async () => {
45
45
  await page.keyboard.press( 'Enter' ); // Insert paragraph.
46
46
  await page.keyboard.type( '2nd col' ); // If this text is too long, it may wrap to a new line and cause test failure. That's why we're using "2nd" instead of "Second" here.
47
47
 
48
- // Arrow down from last of layouts exits nested context to default
49
- // appender of root level.
50
- await page.keyboard.press( 'ArrowDown' );
48
+ await page.keyboard.press( 'Escape' ); // Enter navigation mode
49
+ await page.keyboard.press( 'ArrowLeft' ); // move to the column block
50
+ await page.keyboard.press( 'ArrowLeft' ); // move to the columns block
51
+ await page.keyboard.press( 'Enter' ); // Enter edit mode with the columns block selected
52
+ await page.keyboard.press( 'Enter' ); // Creates a paragraph after the columns block.
51
53
  await page.keyboard.type( 'Second paragraph' );
52
54
  };
53
55
 
@@ -289,6 +291,8 @@ describe( 'Writing Flow', () => {
289
291
  // See: https://github.com/WordPress/gutenberg/issues/9626
290
292
 
291
293
  await insertBlock( 'Shortcode' );
294
+ await insertBlock( 'Paragraph' );
295
+ await await page.click( '.wp-block-shortcode' );
292
296
 
293
297
  // Should remain in title upon ArrowRight:
294
298
  await page.keyboard.press( 'ArrowRight' );
@@ -304,7 +308,7 @@ describe( 'Writing Flow', () => {
304
308
  );
305
309
  expect( isInShortcodeBlock ).toBe( true );
306
310
 
307
- // Should navigate into blocks list upon ArrowDown:
311
+ // Should navigate to the next block.
308
312
  await page.keyboard.press( 'ArrowDown' );
309
313
  const isInParagraphBlock = await page.evaluate(
310
314
  () => !! document.activeElement.closest( '.wp-block-paragraph' )
@@ -60,7 +60,7 @@ describe( 'Document Settings', () => {
60
60
  '.edit-post-header-toolbar__list-view-toggle'
61
61
  );
62
62
  const headerTemplatePartListViewButton = await page.waitForXPath(
63
- '//button[contains(@class, "block-editor-list-view-block-select-button")][contains(., "Header")]'
63
+ '//a[contains(@class, "block-editor-list-view-block-select-button")][contains(., "Header")]'
64
64
  );
65
65
  headerTemplatePartListViewButton.click();
66
66
  await page.click(
@@ -4,11 +4,14 @@
4
4
  import {
5
5
  createNewPost,
6
6
  disablePrePublishChecks,
7
+ getOption,
7
8
  insertBlock,
8
9
  publishPost,
10
+ setOption,
9
11
  trashAllPosts,
10
12
  activateTheme,
11
13
  clickButton,
14
+ createReusableBlock,
12
15
  } from '@wordpress/e2e-test-utils';
13
16
 
14
17
  /**
@@ -21,13 +24,9 @@ describe( 'Multi-entity save flow', () => {
21
24
  const checkedBoxSelector = '.components-checkbox-control__checked';
22
25
  const checkboxInputSelector = '.components-checkbox-control__input';
23
26
  const entitiesSaveSelector = '.editor-entities-saved-states__save-button';
24
- const templatePartSelector = '*[data-type="core/template-part"]';
25
- const activatedTemplatePartSelector = `${ templatePartSelector }.block-editor-block-list__layout`;
26
27
  const savePanelSelector = '.entities-saved-states__panel';
27
28
  const closePanelButtonSelector =
28
- '.editor-post-publish-panel__header-cancel-button button';
29
- const createNewButtonSelector =
30
- '//button[contains(text(), "New template part")]';
29
+ '.editor-post-publish-panel__header-cancel-button button:not(:disabled)';
31
30
 
32
31
  // Reusable assertions across Post/Site editors.
33
32
  const assertAllBoxesChecked = async () => {
@@ -44,14 +43,26 @@ describe( 'Multi-entity save flow', () => {
44
43
  }
45
44
  };
46
45
 
46
+ let originalSiteTitle, originalBlogDescription;
47
+
47
48
  beforeAll( async () => {
48
49
  await activateTheme( 'tt1-blocks' );
49
50
  await trashAllPosts( 'wp_template' );
50
51
  await trashAllPosts( 'wp_template_part' );
52
+ await trashAllPosts( 'wp_block' );
53
+
54
+ // Get the current Site Title and Site Tagline, so that we can reset
55
+ // them back to the original values once the test suite has finished.
56
+ originalSiteTitle = await getOption( 'blogname' );
57
+ originalBlogDescription = await getOption( 'blogdescription' );
51
58
  } );
52
59
 
53
60
  afterAll( async () => {
54
61
  await activateTheme( 'twentytwentyone' );
62
+
63
+ // Reset the Site Title and Site Tagline back to their original values.
64
+ await setOption( 'blogname', originalSiteTitle );
65
+ await setOption( 'blogdescription', originalBlogDescription );
55
66
  } );
56
67
 
57
68
  describe( 'Post Editor', () => {
@@ -66,8 +77,6 @@ describe( 'Multi-entity save flow', () => {
66
77
  const saveA11ySelector =
67
78
  '.edit-post-layout__toggle-entities-saved-states-panel-button';
68
79
  const publishPanelSelector = '.editor-post-publish-panel';
69
- const confirmTitleButtonSelector =
70
- '.wp-block-template-part__placeholder-create-new__title-form .components-button.is-primary';
71
80
 
72
81
  // Reusable assertions inside Post editor.
73
82
  const assertMultiSaveEnabled = async () => {
@@ -87,6 +96,7 @@ describe( 'Multi-entity save flow', () => {
87
96
  it( 'Save flow should work as expected.', async () => {
88
97
  await createNewPost();
89
98
  // Edit the page some.
99
+ await page.waitForSelector( '.editor-post-title' );
90
100
  await page.click( '.editor-post-title' );
91
101
  await page.keyboard.type( 'Test Post...' );
92
102
  await page.keyboard.press( 'Enter' );
@@ -100,21 +110,11 @@ describe( 'Multi-entity save flow', () => {
100
110
  await assertExistance( publishPanelSelector, false );
101
111
  await assertExistance( savePanelSelector, false );
102
112
 
103
- // Add a template part and edit it.
104
- await insertBlock( 'Template Part' );
105
- const createNewButton = await page.waitForXPath(
106
- createNewButtonSelector
107
- );
108
- await createNewButton.click();
109
- const confirmTitleButton = await page.waitForSelector(
110
- confirmTitleButtonSelector
111
- );
112
- await confirmTitleButton.click();
113
-
114
- await page.waitForSelector( activatedTemplatePartSelector );
115
- await page.click( '.block-editor-button-block-appender' );
116
- await page.click( '.editor-block-list-item-paragraph' );
117
- await page.keyboard.type( 'some words...' );
113
+ // Add a reusable block and edit it.
114
+ await createReusableBlock( 'Hi!', 'Test' );
115
+ await page.waitForSelector( 'p[data-type="core/paragraph"]' );
116
+ await page.click( 'p[data-type="core/paragraph"]' );
117
+ await page.keyboard.type( 'Oh!' );
118
118
 
119
119
  // Should trigger multi-entity save button once template part edited.
120
120
  await assertMultiSaveEnabled();
@@ -145,7 +145,10 @@ describe( 'Multi-entity save flow', () => {
145
145
  await assertExistance( savePanelSelector, false );
146
146
 
147
147
  // Close publish panel.
148
- await page.click( closePanelButtonSelector );
148
+ const closePanelButton = await page.waitForSelector(
149
+ closePanelButtonSelector
150
+ );
151
+ await closePanelButton.click();
149
152
 
150
153
  // Verify saving is disabled.
151
154
  const draftSaved = await page.waitForSelector( draftSavedSelector );
@@ -154,25 +157,32 @@ describe( 'Multi-entity save flow', () => {
154
157
  await assertExistance( saveA11ySelector, false );
155
158
 
156
159
  await publishPost();
160
+ // Wait for the success notice specifically for the published post.
161
+ // `publishPost()` has a similar check but it only checks for the
162
+ // existence of any snackbars. In this case, there's another "Site updated"
163
+ // notice which will be sufficient for that and thus creating a false-positive.
164
+ await page.waitForXPath(
165
+ '//*[@id="a11y-speak-polite"][contains(text(), "Post published")]'
166
+ );
157
167
 
158
168
  // Update the post.
159
169
  await page.click( '.editor-post-title' );
160
170
  await page.keyboard.type( '...more title!' );
161
171
 
162
172
  // Verify update button is enabled.
163
- const enabledSaveButton = await page.$( enabledSavePostSelector );
173
+ const enabledSaveButton = await page.waitForSelector(
174
+ enabledSavePostSelector
175
+ );
164
176
  expect( enabledSaveButton ).not.toBeNull();
165
177
  // Verify multi-entity saving not enabled.
166
178
  await assertMultiSaveDisabled();
167
179
  await assertExistance( saveA11ySelector, false );
168
180
 
169
- // Update template part.
170
- await page.click( templatePartSelector );
171
- await page.click(
172
- `${ templatePartSelector } .wp-block[data-type="core/paragraph"]`
173
- );
174
- await page.keyboard.type( '...some more words...' );
175
- await page.keyboard.press( 'Enter' );
181
+ // Update reusable block again.
182
+ await page.click( 'p[data-type="core/paragraph"]' );
183
+ // We need to click again due to the clickthrough overlays in reusable blocks.
184
+ await page.click( 'p[data-type="core/paragraph"]' );
185
+ await page.keyboard.type( 'R!' );
176
186
 
177
187
  // Multi-entity saving should be enabled.
178
188
  await assertMultiSaveEnabled();
@@ -185,7 +195,9 @@ describe( 'Multi-entity save flow', () => {
185
195
 
186
196
  await insertBlock( 'Site Title' );
187
197
  // Ensure title is retrieved before typing.
188
- await page.waitForXPath( '//a[contains(text(), "gutenberg")]' );
198
+ await page.waitForXPath(
199
+ `//a[contains(text(), "${ originalSiteTitle }")]`
200
+ );
189
201
  const editableSiteTitleSelector =
190
202
  '.wp-block-site-title a[contenteditable="true"]';
191
203
  await page.waitForSelector( editableSiteTitleSelector );
@@ -211,23 +223,16 @@ describe( 'Multi-entity save flow', () => {
211
223
  await checkboxInputs[ 1 ].click();
212
224
  await page.click( entitiesSaveSelector );
213
225
 
226
+ // Wait for the snackbar notice that the post has been published.
227
+ await page.waitForSelector( '.components-snackbar' );
228
+
214
229
  await clickButton( 'Update…' );
215
230
  await page.waitForSelector( savePanelSelector );
231
+
232
+ await page.waitForSelector( checkboxInputSelector );
216
233
  checkboxInputs = await page.$$( checkboxInputSelector );
217
- expect( checkboxInputs ).toHaveLength( 1 );
218
234
 
219
- // Reset site entity to default value to not affect other tests.
220
- await page.evaluate( () => {
221
- wp.data
222
- .dispatch( 'core' )
223
- .editEntityRecord( 'root', 'site', undefined, {
224
- title: 'gutenberg',
225
- description: 'Just another WordPress site',
226
- } );
227
- wp.data
228
- .dispatch( 'core' )
229
- .saveEditedEntityRecord( 'root', 'site', undefined );
230
- } );
235
+ expect( checkboxInputs ).toHaveLength( 1 );
231
236
  } );
232
237
  } );
233
238
 
@@ -238,6 +243,19 @@ describe( 'Multi-entity save flow', () => {
238
243
  const disabledSaveSiteSelector = `${ saveSiteSelector }[aria-disabled=true]`;
239
244
  const saveA11ySelector = '.edit-site-editor__toggle-save-panel-button';
240
245
 
246
+ const saveAllChanges = async () => {
247
+ // Clicking button should open panel with boxes checked.
248
+ await page.click( activeSaveSiteSelector );
249
+ await page.waitForSelector( savePanelSelector );
250
+ await assertAllBoxesChecked();
251
+
252
+ // Save a11y button should not be present with save panel open.
253
+ await assertExistance( saveA11ySelector, false );
254
+
255
+ // Saving should result in items being saved.
256
+ await page.click( entitiesSaveSelector );
257
+ };
258
+
241
259
  it( 'Save flow should work as expected', async () => {
242
260
  // Navigate to site editor.
243
261
  await siteEditor.visit( {
@@ -249,7 +267,7 @@ describe( 'Multi-entity save flow', () => {
249
267
  // Select the header template part via list view.
250
268
  await page.click( '.edit-site-header-toolbar__list-view-toggle' );
251
269
  const headerTemplatePartListViewButton = await page.waitForXPath(
252
- '//button[contains(@class, "block-editor-list-view-block-select-button")][contains(., "Header")]'
270
+ '//a[contains(@class, "block-editor-list-view-block-select-button")][contains(., "Header")]'
253
271
  );
254
272
  headerTemplatePartListViewButton.click();
255
273
  await page.click( 'button[aria-label="Close list view sidebar"]' );
@@ -267,20 +285,55 @@ describe( 'Multi-entity save flow', () => {
267
285
  // Save a11y button should be present.
268
286
  await assertExistance( saveA11ySelector, true );
269
287
 
270
- // Clicking button should open panel with boxes checked.
271
- await page.click( activeSaveSiteSelector );
272
- await page.waitForSelector( savePanelSelector );
273
- await assertAllBoxesChecked();
288
+ // Save all changes.
289
+ await saveAllChanges();
274
290
 
275
- // Save a11y button should not be present with save panel open.
276
- await assertExistance( saveA11ySelector, false );
277
-
278
- // Saving should result in items being saved.
279
- await page.click( entitiesSaveSelector );
280
291
  const disabledButton = await page.waitForSelector(
281
292
  disabledSaveSiteSelector
282
293
  );
283
294
  expect( disabledButton ).not.toBeNull();
284
295
  } );
296
+
297
+ it( 'Save flow should allow re-saving after changing the same block attribute', async () => {
298
+ // Navigate to site editor.
299
+ await siteEditor.visit( {
300
+ postId: 'tt1-blocks//index',
301
+ postType: 'wp_template',
302
+ } );
303
+ await siteEditor.disableWelcomeGuide();
304
+
305
+ // Insert a paragraph at the bottom.
306
+ await insertBlock( 'Paragraph' );
307
+
308
+ // Open the block settings.
309
+ await page.click( 'button[aria-label="Settings"]' );
310
+
311
+ // Click on font size selector.
312
+ await page.click( 'button[aria-label="Font size"]' );
313
+
314
+ // Click on a different font size.
315
+ const extraSmallFontSize = await page.waitForXPath(
316
+ '//li[contains(text(), "Extra small")]'
317
+ );
318
+ await extraSmallFontSize.click();
319
+
320
+ // Save all changes.
321
+ await saveAllChanges();
322
+
323
+ // Click on font size selector again.
324
+ await page.click( 'button[aria-label="Font size"]' );
325
+
326
+ // Select another font size.
327
+ const normalFontSize = await page.waitForXPath(
328
+ '//li[contains(text(), "Normal")]'
329
+ );
330
+ await normalFontSize.click();
331
+
332
+ // Assert that the save button has been re-enabled.
333
+ const saveButton = await page.waitForSelector(
334
+ activeSaveSiteSelector
335
+ );
336
+ expect( saveButton ).not.toBeNull();
337
+ } );
285
338
  } );
286
339
  } );