From a7cbdecbf9fd2ad587f20faebe83dd5ca96ebbcf Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Wed, 29 Nov 2023 23:24:22 +0400 Subject: [PATCH 1/2] Migrate 'editor multi entity saving' e2e tests to Playwright --- .../various/multi-entity-saving.spec.js | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 test/e2e/specs/editor/various/multi-entity-saving.spec.js diff --git a/test/e2e/specs/editor/various/multi-entity-saving.spec.js b/test/e2e/specs/editor/various/multi-entity-saving.spec.js new file mode 100644 index 0000000000000..7a7298c137c4b --- /dev/null +++ b/test/e2e/specs/editor/various/multi-entity-saving.spec.js @@ -0,0 +1,210 @@ +/** + * WordPress dependencies + */ +const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); + +test.describe( 'Editor - Multi-entity save flow', () => { + let originalSiteTitle, originalBlogDescription; + + test.beforeEach( async ( { requestUtils } ) => { + const siteSettings = await requestUtils.getSiteSettings(); + + originalSiteTitle = siteSettings.title; + originalBlogDescription = siteSettings.description; + } ); + + test.afterEach( async ( { requestUtils, editor } ) => { + await requestUtils.updateSiteSettings( { + title: originalSiteTitle, + description: originalBlogDescription, + } ); + + // Restore the Publish sidebar. + await editor.setPreferences( 'core/edit-post', { + isPublishSidebarEnabled: true, + } ); + } ); + + test( 'Save flow should work as expected', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost(); + await editor.canvas + .getByRole( 'textbox', { name: 'Add title' } ) + .fill( 'Test Post...' ); + await page.keyboard.press( 'Enter' ); + + const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); + const publishButton = topBar.getByRole( 'button', { name: 'Publish' } ); + + // Should not trigger multi-entity save button with only post edited. + await expect( publishButton ).toBeEnabled(); + await expect( publishButton ).not.toHaveClass( /has-changes-dot/ ); + + const openPublishPanel = page.getByRole( 'button', { + name: 'Open publish panel', + } ); + const openSavePanel = page.getByRole( 'button', { + name: 'Open save panel', + } ); + const publishPanel = page.getByRole( 'region', { + name: 'Editor publish', + } ); + + // Should only have publish panel a11y button active with only post edited. + await expect( openPublishPanel ).toBeVisible(); + await expect( openSavePanel ).toBeHidden(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to publish?' + ); + await expect( publishPanel ).not.toContainText( + 'Are you ready to save?' + ); + + // Add a title block and edit it. + await editor.insertBlock( { + name: 'core/site-title', + } ); + const siteTitleField = editor.canvas.getByRole( 'textbox', { + name: 'Site title text', + } ); + await siteTitleField.fill( `${ originalSiteTitle }...` ); + + // Should trigger multi-entity save button once template part edited. + await expect( publishButton ).toHaveClass( /has-changes-dot/ ); + + // Should only have save panel a11y button active after child entities edited. + await expect( openPublishPanel ).toBeHidden(); + await expect( openSavePanel ).toBeVisible(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to publish?' + ); + await expect( publishPanel ).not.toContainText( + 'Are you ready to save?' + ); + + // Opening panel has boxes checked by default. + await publishButton.click(); + await expect( publishPanel ).toContainText( 'Are you ready to save?' ); + const allCheckboxes = await publishPanel + .getByRole( 'checkbox' ) + .count(); + await expect( + publishPanel.getByRole( 'checkbox', { checked: true } ) + ).toHaveCount( allCheckboxes ); + + // Should not show other panels (or their a11y buttons) while save panel opened. + await expect( openPublishPanel ).toBeHidden(); + await expect( openSavePanel ).toBeHidden(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to publish?' + ); + + // Publish panel should open after saving. + await publishPanel.getByRole( 'button', { name: 'Save' } ).click(); + await expect( publishPanel ).toContainText( + 'Are you ready to publish?' + ); + + // No other panels (or their a11y buttons) should be present with publish panel open. + await expect( openPublishPanel ).toBeHidden(); + await expect( openSavePanel ).toBeHidden(); + await expect( publishPanel ).not.toContainText( + 'Are you ready to save?' + ); + + // Close publish panel. + await publishPanel.getByRole( 'button', { name: 'Cancel' } ).click(); + + // Verify saving is disabled. + await expect( + topBar.getByRole( 'button', { name: 'Saved' } ) + ).toBeDisabled(); + await expect( publishButton ).not.toHaveClass( /has-changes-dot/ ); + await expect( openSavePanel ).toBeHidden(); + + await editor.publishPost(); + + // Update the post. + await editor.canvas + .getByRole( 'textbox', { name: 'Add title' } ) + .fill( 'Updated post title' ); + + const updateButton = topBar.getByRole( 'button', { name: 'Update' } ); + + // Verify update button is enabled. + await expect( updateButton ).toBeEnabled(); + + // Verify multi-entity saving not enabled. + await expect( updateButton ).not.toHaveClass( /has-changes-dot/ ); + await expect( openSavePanel ).toBeHidden(); + + await siteTitleField.fill( `${ originalSiteTitle }!` ); + + // Multi-entity saving should be enabled. + await expect( updateButton ).toHaveClass( /has-changes-dot/ ); + await expect( openSavePanel ).toBeVisible(); + } ); + + test( 'Site blocks should save individually', async ( { + admin, + editor, + page, + } ) => { + await admin.createNewPost(); + await editor.setPreferences( 'core/edit-post', { + isPublishSidebarEnabled: false, + } ); + + // Add site blocks. + await editor.insertBlock( { + name: 'core/site-title', + } ); + await editor.insertBlock( { + name: 'core/site-tagline', + } ); + + const siteTitleField = editor.canvas.getByRole( 'textbox', { + name: 'Site title text', + } ); + + // Ensure title is retrieved before typing. + await expect( siteTitleField ).toHaveText( originalSiteTitle ); + + await siteTitleField.fill( `${ originalSiteTitle }...` ); + await editor.canvas + .getByRole( 'document', { + name: 'Block: Site Tagline', + } ) + .fill( 'Just another WordPress site' ); + + const topBar = page.getByRole( 'region', { name: 'Editor top bar' } ); + const publishPanel = page.getByRole( 'region', { + name: 'Editor publish', + } ); + + await topBar.getByRole( 'button', { name: 'Publish' } ).click(); + await expect( publishPanel.getByRole( 'checkbox' ) ).toHaveCount( 3 ); + + // Skip site title saving. + await publishPanel + .getByRole( 'checkbox', { + name: 'Title', + } ) + .setChecked( false ); + + await publishPanel.getByRole( 'button', { name: 'Save' } ).click(); + + // Wait for the snackbar notice that the post has been published. + await page + .getByRole( 'button', { name: 'Dismiss this notice' } ) + .filter( { hasText: 'published' } ) + .waitFor(); + + await topBar.getByRole( 'button', { name: 'Update' } ).click(); + + await expect( publishPanel.getByRole( 'checkbox' ) ).toHaveCount( 1 ); + } ); +} ); From 210b10c7c3e64929c288393cbd85b44dfa4a8443 Mon Sep 17 00:00:00 2001 From: George Mamadashvili Date: Thu, 30 Nov 2023 12:37:01 +0400 Subject: [PATCH 2/2] Remove old test file --- .../site-editor/multi-entity-saving.test.js | 239 ------------------ 1 file changed, 239 deletions(-) delete mode 100644 packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js diff --git a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js b/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js deleted file mode 100644 index fe5334d2a1276..0000000000000 --- a/packages/e2e-tests/specs/site-editor/multi-entity-saving.test.js +++ /dev/null @@ -1,239 +0,0 @@ -/** - * WordPress dependencies - */ -import { - createNewPost, - disablePrePublishChecks, - getOption, - insertBlock, - publishPost, - setOption, - trashAllPosts, - activateTheme, - clickButton, - createReusableBlock, - deleteAllTemplates, - canvas, -} from '@wordpress/e2e-test-utils'; - -describe( 'Multi-entity save flow', () => { - // Selectors - usable between Post/Site editors. - const checkedBoxSelector = '.components-checkbox-control__checked'; - const checkboxInputSelector = '.components-checkbox-control__input'; - const entitiesSaveSelector = '.editor-entities-saved-states__save-button'; - const savePanelSelector = '.entities-saved-states__panel'; - const closePanelButtonSelector = - '.editor-post-publish-panel__header-cancel-button button:not(:disabled)'; - - // Reusable assertions across Post/Site editors. - const assertAllBoxesChecked = async () => { - const checkedBoxes = await page.$$( checkedBoxSelector ); - const checkboxInputs = await page.$$( checkboxInputSelector ); - expect( checkedBoxes.length - checkboxInputs.length ).toBe( 0 ); - }; - const assertExistence = async ( selector, shouldBePresent ) => { - const element = await page.$( selector ); - if ( shouldBePresent ) { - expect( element ).not.toBeNull(); - } else { - expect( element ).toBeNull(); - } - }; - - let originalSiteTitle, originalBlogDescription; - - beforeAll( async () => { - await activateTheme( 'emptytheme' ); - await deleteAllTemplates( 'wp_template' ); - await deleteAllTemplates( 'wp_template_part' ); - await trashAllPosts( 'wp_block' ); - - // Get the current Site Title and Site Tagline, so that we can reset - // them back to the original values once the test suite has finished. - originalSiteTitle = await getOption( 'blogname' ); - originalBlogDescription = await getOption( 'blogdescription' ); - } ); - - afterAll( async () => { - await activateTheme( 'twentytwentyone' ); - - // Reset the Site Title and Site Tagline back to their original values. - await setOption( 'blogname', originalSiteTitle ); - await setOption( 'blogdescription', originalBlogDescription ); - } ); - - describe( 'Post Editor', () => { - // Selectors - Post editor specific. - const draftSavedSelector = '.editor-post-saved-state.is-saved'; - const multiSaveSelector = - '.editor-post-publish-button__button.has-changes-dot'; - const savePostSelector = '.editor-post-publish-button__button'; - const enabledSavePostSelector = `${ savePostSelector }[aria-disabled=false]`; - const publishA11ySelector = - '.edit-post-layout__toggle-publish-panel-button'; - const saveA11ySelector = - '.edit-post-layout__toggle-entities-saved-states-panel-button'; - const publishPanelSelector = '.editor-post-publish-panel'; - - // Reusable assertions inside Post editor. - const assertMultiSaveEnabled = async () => { - const multiSaveButton = - await page.waitForSelector( multiSaveSelector ); - expect( multiSaveButton ).not.toBeNull(); - }; - const assertMultiSaveDisabled = async () => { - const multiSaveButton = await page.waitForSelector( - multiSaveSelector, - { hidden: true } - ); - expect( multiSaveButton ).toBeNull(); - }; - - it( 'Save flow should work as expected.', async () => { - await createNewPost(); - // Edit the page some. - await canvas().waitForSelector( '.editor-post-title' ); - await canvas().click( '.editor-post-title' ); - await page.keyboard.type( 'Test Post...' ); - await page.keyboard.press( 'Enter' ); - - // Should not trigger multi-entity save button with only post edited. - await assertMultiSaveDisabled(); - - // Should only have publish panel a11y button active with only post edited. - await assertExistence( publishA11ySelector, true ); - await assertExistence( saveA11ySelector, false ); - await assertExistence( publishPanelSelector, false ); - await assertExistence( savePanelSelector, false ); - - // Add a reusable block and edit it. - await createReusableBlock( 'Hi!', 'Test' ); - await canvas().waitForSelector( 'p[data-type="core/paragraph"]' ); - await canvas().click( 'p[data-type="core/paragraph"]' ); - await page.keyboard.type( 'Oh!' ); - - // Should trigger multi-entity save button once template part edited. - await assertMultiSaveEnabled(); - - // Should only have save panel a11y button active after child entities edited. - await assertExistence( publishA11ySelector, false ); - await assertExistence( saveA11ySelector, true ); - await assertExistence( publishPanelSelector, false ); - await assertExistence( savePanelSelector, false ); - - // Opening panel has boxes checked by default. - await page.click( savePostSelector ); - await page.waitForSelector( savePanelSelector ); - await assertAllBoxesChecked(); - - // Should not show other panels (or their a11y buttons) while save panel opened. - await assertExistence( publishA11ySelector, false ); - await assertExistence( saveA11ySelector, false ); - await assertExistence( publishPanelSelector, false ); - - // Publish panel should open after saving. - await page.click( entitiesSaveSelector ); - await page.waitForSelector( publishPanelSelector ); - - // No other panels (or their a11y buttons) should be present with publish panel open. - await assertExistence( publishA11ySelector, false ); - await assertExistence( saveA11ySelector, false ); - await assertExistence( savePanelSelector, false ); - - // Close publish panel. - const closePanelButton = await page.waitForSelector( - closePanelButtonSelector - ); - await closePanelButton.click(); - - // Verify saving is disabled. - const draftSaved = await page.waitForSelector( draftSavedSelector ); - expect( draftSaved ).not.toBeNull(); - await assertMultiSaveDisabled(); - await assertExistence( saveA11ySelector, false ); - - await publishPost(); - // Wait for the success notice specifically for the published post. - // `publishPost()` has a similar check but it only checks for the - // existence of any snackbars. In this case, there's another "Site updated" - // notice which will be sufficient for that and thus creating a false-positive. - await page.waitForXPath( - '//*[@id="a11y-speak-polite"][contains(text(), "Post published")]' - ); - - // Unselect the blocks to avoid clicking the block toolbar. - await page.evaluate( () => { - wp.data.dispatch( 'core/block-editor' ).clearSelectedBlock(); - } ); - - // Update the post. - await canvas().click( '.editor-post-title' ); - await page.keyboard.type( '...more title!' ); - - // Verify update button is enabled. - const enabledSaveButton = await page.waitForSelector( - enabledSavePostSelector - ); - expect( enabledSaveButton ).not.toBeNull(); - // Verify multi-entity saving not enabled. - await assertMultiSaveDisabled(); - await assertExistence( saveA11ySelector, false ); - - // Update reusable block again. - await canvas().click( 'p[data-type="core/paragraph"]' ); - // We need to click again due to the clickthrough overlays in reusable blocks. - await canvas().click( 'p[data-type="core/paragraph"]' ); - await page.keyboard.type( 'R!' ); - - // Multi-entity saving should be enabled. - await assertMultiSaveEnabled(); - await assertExistence( saveA11ySelector, true ); - } ); - - it( 'Site blocks should save individually', async () => { - await createNewPost(); - await disablePrePublishChecks(); - - await insertBlock( 'Site Title' ); - // Ensure title is retrieved before typing. - await page.waitForXPath( - `//a[contains(text(), "${ originalSiteTitle }")]` - ); - const editableSiteTitleSelector = - '.wp-block-site-title a[contenteditable="true"]'; - await canvas().waitForSelector( editableSiteTitleSelector ); - await canvas().focus( editableSiteTitleSelector ); - await page.keyboard.type( '...' ); - - await insertBlock( 'Site Tagline' ); - // Wait for the placeholder. - await canvas().waitForXPath( - '//span[@data-rich-text-placeholder="Write site tagline…"]' - ); - const editableSiteTagLineSelector = - '.wp-block-site-tagline[contenteditable="true"]'; - await canvas().waitForSelector( editableSiteTagLineSelector ); - await canvas().focus( editableSiteTagLineSelector ); - await page.keyboard.type( 'Just another WordPress site' ); - - await clickButton( 'Publish' ); - await page.waitForSelector( savePanelSelector ); - let checkboxInputs = await page.$$( checkboxInputSelector ); - expect( checkboxInputs ).toHaveLength( 3 ); - - await checkboxInputs[ 1 ].click(); - await page.click( entitiesSaveSelector ); - - // Wait for the snackbar notice that the post has been published. - await page.waitForSelector( '.components-snackbar' ); - - await clickButton( 'Update…' ); - await page.waitForSelector( savePanelSelector ); - - await page.waitForSelector( checkboxInputSelector ); - checkboxInputs = await page.$$( checkboxInputSelector ); - - expect( checkboxInputs ).toHaveLength( 1 ); - } ); - } ); -} );