From bcf9c66e9ee003acb8642e979d8f34424aed42b2 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Thu, 19 Dec 2024 19:20:59 +0100 Subject: [PATCH 1/4] Page - Quick Edit: add E2E tests --- test/e2e/specs/site-editor/page-list.spec.js | 256 +++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/test/e2e/specs/site-editor/page-list.spec.js b/test/e2e/specs/site-editor/page-list.spec.js index fa9cb86cd1d62e..9ea3d14a3f9482 100644 --- a/test/e2e/specs/site-editor/page-list.spec.js +++ b/test/e2e/specs/site-editor/page-list.spec.js @@ -2,6 +2,10 @@ * WordPress dependencies */ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); +/** + * External dependencies + */ +const path = require( 'path' ); test.describe( 'Page List', () => { test.beforeAll( async ( { requestUtils } ) => { @@ -53,4 +57,256 @@ test.describe( 'Page List', () => { page.getByRole( 'searchbox', { name: 'Search' } ) ).toHaveValue( 'Privacy' ); } ); + + test.describe( 'Quick Edit Mode', () => { + const fields = { + 'featured-image': { + edit: async ( page ) => { + const placeholder = page.getByRole( 'button', { + name: 'Choose an image…', + } ); + await placeholder.click(); + const mediaLibrary = page.getByRole( 'dialog' ); + const TEST_IMAGE_FILE_PATH = path.resolve( + __dirname, + '../../assets/10x10_e2e_test_image_z9T8jK.png' + ); + + const fileChooserPromise = + page.waitForEvent( 'filechooser' ); + await mediaLibrary.getByText( 'Select files' ).click(); + const fileChooser = await fileChooserPromise; + await fileChooser.setFiles( TEST_IMAGE_FILE_PATH ); + await mediaLibrary + .locator( '.media-frame-toolbar' ) + .waitFor( { + state: 'hidden', + } ); + + await mediaLibrary + .getByRole( 'button', { name: 'Select', exact: true } ) + .click(); + }, + initialView: async ( page ) => { + const el = page.getByText( 'Choose an image…' ); + const placeholder = page.getByRole( 'button', { + name: 'Choose an image…', + } ); + await expect( el ).toBeVisible(); + await expect( placeholder ).toBeVisible(); + }, + viewAfterEdit: async ( page ) => { + const placeholder = page.getByRole( 'button', { + name: 'Choose an image…', + } ); + await expect( placeholder ).toBeHidden(); + const img = page.locator( + '.fields-controls__featured-image-image' + ); + await expect( img ).toBeVisible(); + }, + }, + 'status-visibility': { + edit: async ( page ) => { + const statusAndVisibility = page.getByLabel( + 'Status & Visibility' + ); + await statusAndVisibility.click(); + const options = [ + 'Published', + 'Draft', + 'Pending Review', + 'Private', + ]; + + for ( const option of options ) { + await page + .getByRole( 'radio', { name: option } ) + .click(); + await expect( statusAndVisibility ).toContainText( + option + ); + + if ( option !== 'Private' ) { + await page + .getByRole( 'checkbox', { + name: 'Password protected', + } ) + .check(); + } + } + }, + initialView: async ( page ) => { + const statusAndVisibility = page.getByLabel( + 'Status & Visibility' + ); + await expect( statusAndVisibility ).toContainText( + 'Published' + ); + }, + viewAfterEdit: async ( page ) => { + const statusAndVisibility = page.getByLabel( + 'Status & Visibility' + ); + await expect( statusAndVisibility ).toContainText( + 'Private' + ); + }, + }, + author: { + initialView: async ( page ) => { + const author = page.getByLabel( 'Author' ); + await expect( author ).toContainText( 'admin' ); + }, + edit: async ( page ) => { + const author = page.getByLabel( 'Author' ); + await author.click(); + const selectElement = page.locator( + 'select:has(option[value="1"])' + ); + await selectElement.selectOption( { value: '1' } ); + }, + viewAfterEdit: async () => {}, + }, + date: { + initialView: async ( page ) => { + const dateEl = page.getByLabel( 'Edit Date' ); + const date = new Date(); + const yy = String( date.getFullYear() ); + + await expect( dateEl ).toContainText( yy ); + }, + edit: async ( page ) => { + const dateEl = page.getByLabel( 'Edit Date' ); + await dateEl.click(); + const date = new Date(); + const yy = Number( date.getFullYear() ); + const yyEl = page.locator( + `input[type="number"][value="${ yy }"]` + ); + + await yyEl.focus(); + await page.keyboard.press( 'ArrowUp' ); + }, + viewAfterEdit: async ( page ) => { + const date = new Date(); + const yy = Number( date.getFullYear() ); + const dateEl = page.getByLabel( 'Edit Date' ); + await expect( dateEl ).toContainText( String( yy + 1 ) ); + }, + }, + slug: { + initialView: async ( page ) => { + const slug = page.getByLabel( 'Edit Slug' ); + await expect( slug ).toContainText( 'privacy-policy' ); + }, + edit: async ( page ) => { + const slug = page.getByLabel( 'Edit Slug' ); + await slug.click(); + await expect( + page.getByRole( 'link', { + name: 'http://localhost:8889/?', + } ) + ).toBeVisible(); + }, + viewAfterEdit: async () => {}, + }, + parent: { + initialView: async ( page ) => { + const parent = page.getByLabel( 'Edit Parent' ); + await expect( parent ).toContainText( 'None' ); + }, + edit: async ( page ) => { + const parent = page.getByLabel( 'Edit Parent' ); + await parent.click(); + await page + .getByLabel( 'Parent', { exact: true } ) + .fill( 'Sample' ); + + await page + .getByRole( 'option', { name: 'Sample Page' } ) + .click(); + }, + viewAfterEdit: async ( page ) => { + const parent = page.getByLabel( 'Edit Parent' ); + await expect( parent ).toContainText( 'Sample Page' ); + }, + }, + // template: { + // initialView: async ( page ) => { + // const template = page.getByRole( 'button', { + // name: 'Single Entries', + // } ); + // await expect( template ).toContainText( 'Single Entries' ); + // }, + // edit: async ( page ) => { + // const template = page.getByRole( 'button', { + // name: 'Single Entries', + // } ); + // await template.click(); + // await page + // .getByRole( 'menuitem', { name: 'Swap template' } ) + // .click(); + // }, + // viewAfterEdit: async ( page ) => { + // await page.waitForTimeout( 15000 ); + // }, + // }, + discussion: { + initialView: async ( page ) => { + const discussion = page.getByLabel( 'Edit Discussion' ); + await expect( discussion ).toContainText( 'Closed' ); + }, + edit: async ( page ) => { + const discussion = page.getByLabel( 'Edit Discussion' ); + await discussion.click(); + await page + .getByLabel( 'Open', { + exact: true, + } ) + .check(); + }, + viewAfterEdit: async ( page ) => { + const discussion = page.getByLabel( 'Edit Discussion' ); + await expect( discussion ).toContainText( 'Open' ); + }, + }, + }; + + test.beforeAll( async ( { requestUtils } ) => { + await requestUtils.setGutenbergExperiments( [ + 'gutenberg-quick-edit-dataviews', + ] ); + } ); + + test.beforeEach( async ( { admin, page } ) => { + await admin.visitSiteEditor(); + await page.getByRole( 'button', { name: 'Pages' } ).click(); + await page.getByRole( 'button', { name: 'Layout' } ).click(); + await page.getByRole( 'menuitemradio', { name: 'Table' } ).click(); + const privacyPolicyCheckbox = page.getByRole( 'checkbox', { + name: 'Select Item: Privacy Policy', + } ); + + await privacyPolicyCheckbox.check(); + + await page.getByRole( 'button', { name: 'Details' } ).click(); + } ); + + Object.entries( fields ).forEach( + ( [ key, { edit, initialView, viewAfterEdit } ] ) => { + // Asserts are done in the individual functions + // eslint-disable-next-line playwright/expect-expect + test( key, async ( { page } ) => { + await initialView( page ); + await edit( page ); + await viewAfterEdit( page ); + } ); + } + ); + + test.afterAll( async ( { requestUtils } ) => { + await requestUtils.setGutenbergExperiments( [] ); + } ); + } ); } ); From 6fc8a6a959910af0df28bd673fbbd7484c94a07b Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Fri, 20 Dec 2024 14:55:31 +0100 Subject: [PATCH 2/4] improve E2E tests --- test/e2e/specs/site-editor/page-list.spec.js | 125 +++++++++++++++++-- 1 file changed, 113 insertions(+), 12 deletions(-) diff --git a/test/e2e/specs/site-editor/page-list.spec.js b/test/e2e/specs/site-editor/page-list.spec.js index 9ea3d14a3f9482..ef0a504a440b8c 100644 --- a/test/e2e/specs/site-editor/page-list.spec.js +++ b/test/e2e/specs/site-editor/page-list.spec.js @@ -7,18 +7,22 @@ const { test, expect } = require( '@wordpress/e2e-test-utils-playwright' ); */ const path = require( 'path' ); +const createPages = async ( requestUtils ) => { + await requestUtils.createPage( { + title: 'Privacy Policy', + status: 'publish', + } ); + await requestUtils.createPage( { + title: 'Sample Page', + status: 'publish', + } ); +}; + test.describe( 'Page List', () => { test.beforeAll( async ( { requestUtils } ) => { // Activate a theme with permissions to access the site editor. await requestUtils.activateTheme( 'emptytheme' ); - await requestUtils.createPage( { - title: 'Privacy Policy', - status: 'publish', - } ); - await requestUtils.createPage( { - title: 'Sample Page', - status: 'publish', - } ); + await createPages( requestUtils ); } ); test.afterAll( async ( { requestUtils } ) => { @@ -60,7 +64,7 @@ test.describe( 'Page List', () => { test.describe( 'Quick Edit Mode', () => { const fields = { - 'featured-image': { + featuredImage: { edit: async ( page ) => { const placeholder = page.getByRole( 'button', { name: 'Choose an image…', @@ -106,7 +110,7 @@ test.describe( 'Page List', () => { await expect( img ).toBeVisible(); }, }, - 'status-visibility': { + statusVisibility: { edit: async ( page ) => { const statusAndVisibility = page.getByLabel( 'Status & Visibility' @@ -232,6 +236,7 @@ test.describe( 'Page List', () => { await expect( parent ).toContainText( 'Sample Page' ); }, }, + // TODO: Re-enable this test once https://github.com/WordPress/gutenberg/issues/68173 is fixed // template: { // initialView: async ( page ) => { // const template = page.getByRole( 'button', { @@ -249,7 +254,7 @@ test.describe( 'Page List', () => { // .click(); // }, // viewAfterEdit: async ( page ) => { - // await page.waitForTimeout( 15000 ); + // // }, // }, discussion: { @@ -297,7 +302,9 @@ test.describe( 'Page List', () => { ( [ key, { edit, initialView, viewAfterEdit } ] ) => { // Asserts are done in the individual functions // eslint-disable-next-line playwright/expect-expect - test( key, async ( { page } ) => { + test( `should initialize, edit, and update ${ key } field correctly`, async ( { + page, + } ) => { await initialView( page ); await edit( page ); await viewAfterEdit( page ); @@ -305,6 +312,100 @@ test.describe( 'Page List', () => { } ); + test( 'should update the page according to the changes ', async ( { + page, + requestUtils, + } ) => { + const selectedItem = page.locator( '.is-selected' ); + const imagePlaceholder = selectedItem.locator( + '.fields-controls__featured-image-placeholder' + ); + const status = selectedItem.getByRole( 'cell', { + name: 'Published', + } ); + await expect( status ).toBeVisible(); + + const { featuredImage, statusVisibility } = fields; + await statusVisibility.edit( page ); + await featuredImage.edit( page ); + // Ensure that no dropdown is open + await page.getByRole( 'button', { name: 'Close' } ).click(); + const saveButton = page.getByLabel( 'Review 1 change…' ); + await saveButton.click(); + await page.getByRole( 'button', { name: 'Save' } ).click(); + const updatedStatus = selectedItem.getByRole( 'cell', { + name: 'Private', + } ); + await expect( imagePlaceholder ).toBeHidden(); + await expect( updatedStatus ).toBeVisible(); + + // Reset the page to its original state + await requestUtils.deleteAllPages(); + await createPages( requestUtils ); + } ); + + // TODO: Wrap up this test once https://github.com/WordPress/gutenberg/pull/67584 is merged + // test( 'should update pages according to the changes', async ( { + // page, + // } ) => { + // const samplePage = page.getByRole( 'checkbox', { + // name: 'Select Item: Sample Page', + // } ); + + // await samplePage.check(); + + // const table = page.getByRole( 'table' ); + + // const selectedItems = table.locator( '.is-selected', { + // strict: false, + // } ); + + // expect( await selectedItems.all() ).toHaveLength( 2 ); + + // const imagePlaceholders = selectedItems.locator( + // '.fields-controls__featured-image-placeholder', + // { strict: false } + // ); + + // for ( const imagePlaceholder of await imagePlaceholders.all() ) { + // await expect( imagePlaceholder ).toBeVisible(); + // } + + // const statuses = selectedItems.getByRole( 'cell', { + // name: 'Public', + // } ); + + // for ( const status of await statuses.all() ) { + // await expect( status ).toBeVisible(); + // } + + // const { featuredImage, statusVisibility } = fields; + // await statusVisibility.edit( page ); + // await featuredImage.edit( page ); + // // Ensure that no dropdown is open + // await page.getByRole( 'button', { name: 'Close' } ).click(); + // const saveButton = page.getByLabel( 'Review 1 change…' ); + // await saveButton.click(); + // await page.getByRole( 'button', { name: 'Save' } ).click(); + // const updatedStatus = selectedItems.getByRole( + // 'cell', + // { + // name: 'Private', + // }, + // { + // strict: false, + // } + // ); + + // for ( const imagePlaceholder of await imagePlaceholders.all() ) { + // await expect( imagePlaceholder ).toBeHidden(); + // } + + // for ( const status of await updatedStatus.all() ) { + // await expect( status ).toBeVisible(); + // } + // } ); + test.afterAll( async ( { requestUtils } ) => { await requestUtils.setGutenbergExperiments( [] ); } ); From a25fbae85f8fc2354f895c7d81211d028df30ee5 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Fri, 20 Dec 2024 15:11:39 +0100 Subject: [PATCH 3/4] improve test description --- test/e2e/specs/site-editor/page-list.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/specs/site-editor/page-list.spec.js b/test/e2e/specs/site-editor/page-list.spec.js index ef0a504a440b8c..9b3b7c61f4c5db 100644 --- a/test/e2e/specs/site-editor/page-list.spec.js +++ b/test/e2e/specs/site-editor/page-list.spec.js @@ -312,7 +312,7 @@ test.describe( 'Page List', () => { } ); - test( 'should update the page according to the changes ', async ( { + test( 'should save multiple field changes and update Data Views UI', async ( { page, requestUtils, } ) => { From f00aa86559555a95516fe283c977ae824a877259 Mon Sep 17 00:00:00 2001 From: Luigi Teschio Date: Fri, 20 Dec 2024 19:48:59 +0100 Subject: [PATCH 4/4] use more descriptive names --- test/e2e/specs/site-editor/page-list.spec.js | 63 ++++++++++---------- 1 file changed, 33 insertions(+), 30 deletions(-) diff --git a/test/e2e/specs/site-editor/page-list.spec.js b/test/e2e/specs/site-editor/page-list.spec.js index 9b3b7c61f4c5db..120ded6a2b6d06 100644 --- a/test/e2e/specs/site-editor/page-list.spec.js +++ b/test/e2e/specs/site-editor/page-list.spec.js @@ -65,7 +65,7 @@ test.describe( 'Page List', () => { test.describe( 'Quick Edit Mode', () => { const fields = { featuredImage: { - edit: async ( page ) => { + performEdit: async ( page ) => { const placeholder = page.getByRole( 'button', { name: 'Choose an image…', } ); @@ -91,7 +91,7 @@ test.describe( 'Page List', () => { .getByRole( 'button', { name: 'Select', exact: true } ) .click(); }, - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const el = page.getByText( 'Choose an image…' ); const placeholder = page.getByRole( 'button', { name: 'Choose an image…', @@ -99,7 +99,7 @@ test.describe( 'Page List', () => { await expect( el ).toBeVisible(); await expect( placeholder ).toBeVisible(); }, - viewAfterEdit: async ( page ) => { + assertEditedState: async ( page ) => { const placeholder = page.getByRole( 'button', { name: 'Choose an image…', } ); @@ -111,7 +111,7 @@ test.describe( 'Page List', () => { }, }, statusVisibility: { - edit: async ( page ) => { + performEdit: async ( page ) => { const statusAndVisibility = page.getByLabel( 'Status & Visibility' ); @@ -140,7 +140,7 @@ test.describe( 'Page List', () => { } } }, - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const statusAndVisibility = page.getByLabel( 'Status & Visibility' ); @@ -148,7 +148,7 @@ test.describe( 'Page List', () => { 'Published' ); }, - viewAfterEdit: async ( page ) => { + assertEditedState: async ( page ) => { const statusAndVisibility = page.getByLabel( 'Status & Visibility' ); @@ -158,11 +158,11 @@ test.describe( 'Page List', () => { }, }, author: { - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const author = page.getByLabel( 'Author' ); await expect( author ).toContainText( 'admin' ); }, - edit: async ( page ) => { + performEdit: async ( page ) => { const author = page.getByLabel( 'Author' ); await author.click(); const selectElement = page.locator( @@ -170,17 +170,17 @@ test.describe( 'Page List', () => { ); await selectElement.selectOption( { value: '1' } ); }, - viewAfterEdit: async () => {}, + assertEditedState: async () => {}, }, date: { - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const dateEl = page.getByLabel( 'Edit Date' ); const date = new Date(); const yy = String( date.getFullYear() ); await expect( dateEl ).toContainText( yy ); }, - edit: async ( page ) => { + performEdit: async ( page ) => { const dateEl = page.getByLabel( 'Edit Date' ); await dateEl.click(); const date = new Date(); @@ -192,7 +192,7 @@ test.describe( 'Page List', () => { await yyEl.focus(); await page.keyboard.press( 'ArrowUp' ); }, - viewAfterEdit: async ( page ) => { + assertEditedState: async ( page ) => { const date = new Date(); const yy = Number( date.getFullYear() ); const dateEl = page.getByLabel( 'Edit Date' ); @@ -200,11 +200,11 @@ test.describe( 'Page List', () => { }, }, slug: { - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const slug = page.getByLabel( 'Edit Slug' ); await expect( slug ).toContainText( 'privacy-policy' ); }, - edit: async ( page ) => { + performEdit: async ( page ) => { const slug = page.getByLabel( 'Edit Slug' ); await slug.click(); await expect( @@ -213,14 +213,14 @@ test.describe( 'Page List', () => { } ) ).toBeVisible(); }, - viewAfterEdit: async () => {}, + assertEditedState: async () => {}, }, parent: { - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const parent = page.getByLabel( 'Edit Parent' ); await expect( parent ).toContainText( 'None' ); }, - edit: async ( page ) => { + performEdit: async ( page ) => { const parent = page.getByLabel( 'Edit Parent' ); await parent.click(); await page @@ -231,14 +231,14 @@ test.describe( 'Page List', () => { .getByRole( 'option', { name: 'Sample Page' } ) .click(); }, - viewAfterEdit: async ( page ) => { + assertEditedState: async ( page ) => { const parent = page.getByLabel( 'Edit Parent' ); await expect( parent ).toContainText( 'Sample Page' ); }, }, - // TODO: Re-enable this test once https://github.com/WordPress/gutenberg/issues/68173 is fixed + // TODO: Wrap up this test once https://github.com/WordPress/gutenberg/issues/68173 is fixed // template: { - // initialView: async ( page ) => { + // assertInitialState: async ( page ) => { // const template = page.getByRole( 'button', { // name: 'Single Entries', // } ); @@ -253,16 +253,16 @@ test.describe( 'Page List', () => { // .getByRole( 'menuitem', { name: 'Swap template' } ) // .click(); // }, - // viewAfterEdit: async ( page ) => { + // assertEditedState: async ( page ) => { // // }, // }, discussion: { - initialView: async ( page ) => { + assertInitialState: async ( page ) => { const discussion = page.getByLabel( 'Edit Discussion' ); await expect( discussion ).toContainText( 'Closed' ); }, - edit: async ( page ) => { + performEdit: async ( page ) => { const discussion = page.getByLabel( 'Edit Discussion' ); await discussion.click(); await page @@ -271,7 +271,7 @@ test.describe( 'Page List', () => { } ) .check(); }, - viewAfterEdit: async ( page ) => { + assertEditedState: async ( page ) => { const discussion = page.getByLabel( 'Edit Discussion' ); await expect( discussion ).toContainText( 'Open' ); }, @@ -299,15 +299,18 @@ test.describe( 'Page List', () => { } ); Object.entries( fields ).forEach( - ( [ key, { edit, initialView, viewAfterEdit } ] ) => { + ( [ + key, + { performEdit, assertInitialState, assertEditedState }, + ] ) => { // Asserts are done in the individual functions // eslint-disable-next-line playwright/expect-expect test( `should initialize, edit, and update ${ key } field correctly`, async ( { page, } ) => { - await initialView( page ); - await edit( page ); - await viewAfterEdit( page ); + await assertInitialState( page ); + await performEdit( page ); + await assertEditedState( page ); } ); } ); @@ -326,8 +329,8 @@ test.describe( 'Page List', () => { await expect( status ).toBeVisible(); const { featuredImage, statusVisibility } = fields; - await statusVisibility.edit( page ); - await featuredImage.edit( page ); + await statusVisibility.performEdit( page ); + await featuredImage.performEdit( page ); // Ensure that no dropdown is open await page.getByRole( 'button', { name: 'Close' } ).click(); const saveButton = page.getByLabel( 'Review 1 change…' );