[e2e tests] Add tests for managing product images in block editor (#44470)

This commit is contained in:
Adrian Moldovan 2024-02-13 01:20:48 +02:00 committed by GitHub
parent bf2db602b6
commit e863c02551
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 338 additions and 179 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
E2E tests: add tests for managing product images in block editor

View File

@ -5,5 +5,6 @@ module.exports = {
'no-console': 'off', 'no-console': 'off',
'jest/no-test-callback': 'off', 'jest/no-test-callback': 'off',
'jest/no-disabled-tests': 'off', 'jest/no-disabled-tests': 'off',
'jest/valid-expect': 'off',
}, },
}; };

View File

@ -0,0 +1,22 @@
const base = require( '@playwright/test' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
exports.test = base.test.extend( {
api: async ( { baseURL }, use ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
axiosConfig: {
// allow 404s, so we can check if a resource was deleted without try/catch
validateStatus( status ) {
return ( status >= 200 && status < 300 ) || status === 404;
},
},
} );
await use( api );
},
} );
exports.expect = base.expect;

View File

@ -53,7 +53,7 @@ const config = {
screenshot: { mode: 'only-on-failure', fullPage: true }, screenshot: { mode: 'only-on-failure', fullPage: true },
stateDir: 'tests/e2e-pw/test-results/storage/', stateDir: 'tests/e2e-pw/test-results/storage/',
trace: 'retain-on-failure', trace: 'retain-on-failure',
video: 'on-first-retry', video: 'retain-on-failure',
viewport: { width: 1280, height: 720 }, viewport: { width: 1280, height: 720 },
}, },
projects: [ projects: [

View File

@ -1,9 +1,6 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest, expect } = require( '../../fixtures' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
baseTest.describe( 'Products > Add Simple Product', () => { baseTest.describe( 'Products > Add Simple Product', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const productData = { const productData = {
virtual: { virtual: {
name: `Virtual product ${ Date.now() }`, name: `Virtual product ${ Date.now() }`,
@ -40,17 +37,7 @@ baseTest.describe( 'Products > Add Simple Product', () => {
}; };
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { storageState: process.env.ADMINSTATE,
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await use( api );
},
product: async ( { api }, use ) => { product: async ( { api }, use ) => {
const product = {}; const product = {};
await use( product ); await use( product );

View File

@ -1,29 +1,8 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest, expect } = require( '../../fixtures' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
baseTest.describe( 'Products > Delete Product', () => { baseTest.describe( 'Products > Delete Product', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { storageState: process.env.ADMINSTATE,
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
axiosConfig: {
// allow 404s, so we can check if the product was deleted without try/catch
validateStatus( status ) {
return (
( status >= 200 && status < 300 ) || status === 404
);
},
},
} );
await use( api );
},
product: async ( { api }, use ) => { product: async ( { api }, use ) => {
let product = { let product = {
id: 0, id: 0,

View File

@ -1,21 +1,8 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest, expect } = require( '../../fixtures' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
baseTest.describe( 'Products > Edit Product', () => { baseTest.describe( 'Products > Edit Product', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { storageState: process.env.ADMINSTATE,
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await use( api );
},
products: async ( { api }, use ) => { products: async ( { api }, use ) => {
const products = []; const products = [];

View File

@ -1,5 +1,4 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest, expect } = require( '../../fixtures' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
async function addImageFromLibrary( page, imageName, actionButtonName ) { async function addImageFromLibrary( page, imageName, actionButtonName ) {
await page.getByRole( 'tab', { name: 'Media Library' } ).click(); await page.getByRole( 'tab', { name: 'Media Library' } ).click();
@ -13,19 +12,8 @@ async function addImageFromLibrary( page, imageName, actionButtonName ) {
} }
baseTest.describe( 'Products > Product Images', () => { baseTest.describe( 'Products > Product Images', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { storageState: process.env.ADMINSTATE,
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await use( api );
},
product: async ( { api }, use ) => { product: async ( { api }, use ) => {
let product = { let product = {
id: 0, id: 0,

View File

@ -1,20 +1,8 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest, expect } = require( '../../fixtures' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
baseTest.describe( 'Products > Related products', () => { baseTest.describe( 'Products > Related products', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { storageState: process.env.ADMINSTATE,
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await use( api );
},
products: async ( { api }, use ) => { products: async ( { api }, use ) => {
const keys = [ 'main', 'linked1', 'linked2' ]; const keys = [ 'main', 'linked1', 'linked2' ];
const products = {}; const products = {};

View File

@ -1,20 +1,8 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest, expect } = require( '../../fixtures' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
baseTest.describe( 'Product Reviews > Edit Product Review', () => { baseTest.describe( 'Product Reviews > Edit Product Review', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { storageState: process.env.ADMINSTATE,
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await use( api );
},
testData: async ( { api }, use ) => { testData: async ( { api }, use ) => {
const timestamp = Date.now().toString(); const timestamp = Date.now().toString();
let product = {}; let product = {};

View File

@ -0,0 +1,22 @@
const { test } = require( '../../../../fixtures' );
exports.test = test.extend( {
page: async ( { page, api }, use ) => {
await api.put(
'settings/advanced/woocommerce_feature_product_block_editor_enabled',
{
value: 'yes',
}
);
await use( page );
await api.put(
'settings/advanced/woocommerce_feature_product_block_editor_enabled',
{
value: 'no',
}
);
},
storageState: process.env.ADMINSTATE,
} );

View File

@ -1,16 +1,11 @@
const { test, expect } = require( '@playwright/test' ); const { test } = require( './block-editor-fixtures' );
const { expect } = require( '@playwright/test' );
const { const { clickOnTab } = require( '../../../../utils/simple-products' );
clickOnTab,
isBlockProductEditorEnabled,
toggleBlockProductEditor,
} = require( '../../../../utils/simple-products' );
const NEW_EDITOR_ADD_PRODUCT_URL = const NEW_EDITOR_ADD_PRODUCT_URL =
'wp-admin/admin.php?page=wc-admin&path=%2Fadd-product'; 'wp-admin/admin.php?page=wc-admin&path=%2Fadd-product';
let isNewProductEditorEnabled = false;
const isTrackingSupposedToBeEnabled = !! process.env.ENABLE_TRACKING; const isTrackingSupposedToBeEnabled = !! process.env.ENABLE_TRACKING;
const productData = { const productData = {
@ -24,30 +19,6 @@ test.describe.configure( { mode: 'serial' } );
test.describe( 'General tab', () => { test.describe( 'General tab', () => {
test.describe( 'Simple product form', () => { test.describe( 'Simple product form', () => {
test.use( { storageState: process.env.ADMINSTATE } );
test.beforeEach( async ( { browser } ) => {
const context = await browser.newContext();
const page = await context.newPage();
isNewProductEditorEnabled = await isBlockProductEditorEnabled(
page
);
if ( ! isNewProductEditorEnabled ) {
await toggleBlockProductEditor( 'enable', page );
}
} );
test.afterEach( async ( { browser } ) => {
const context = await browser.newContext();
const page = await context.newPage();
isNewProductEditorEnabled = await isBlockProductEditorEnabled(
page
);
if ( isNewProductEditorEnabled ) {
await toggleBlockProductEditor( 'disable', page );
}
} );
test( 'renders each block without error', async ( { page } ) => { test( 'renders each block without error', async ( { page } ) => {
await page.goto( NEW_EDITOR_ADD_PRODUCT_URL ); await page.goto( NEW_EDITOR_ADD_PRODUCT_URL );
await clickOnTab( 'General', page ); await clickOnTab( 'General', page );
@ -61,35 +32,12 @@ test.describe( 'General tab', () => {
test.describe( 'Create product', () => { test.describe( 'Create product', () => {
let productId; let productId;
test.use( { storageState: process.env.ADMINSTATE } );
test.skip( test.skip(
isTrackingSupposedToBeEnabled, isTrackingSupposedToBeEnabled,
'The block product editor is not being tested' 'The block product editor is not being tested'
); );
test.beforeEach( async ( { browser } ) => {
const context = await browser.newContext();
const page = await context.newPage();
isNewProductEditorEnabled = await isBlockProductEditorEnabled(
page
);
if ( ! isNewProductEditorEnabled ) {
await toggleBlockProductEditor( 'enable', page );
}
} );
test.afterEach( async ( { browser } ) => {
const context = await browser.newContext();
const page = await context.newPage();
isNewProductEditorEnabled = await isBlockProductEditorEnabled(
page
);
if ( isNewProductEditorEnabled ) {
await toggleBlockProductEditor( 'disable', page );
}
} );
test( 'can create a simple product', async ( { page } ) => { test( 'can create a simple product', async ( { page } ) => {
await page.goto( NEW_EDITOR_ADD_PRODUCT_URL ); await page.goto( NEW_EDITOR_ADD_PRODUCT_URL );
await clickOnTab( 'General', page ); await clickOnTab( 'General', page );
@ -172,9 +120,7 @@ test.describe( 'General tab', () => {
test( 'can a shopper add the simple product to the cart', async ( { test( 'can a shopper add the simple product to the cart', async ( {
page, page,
} ) => { } ) => {
await page.goto( `/?post_type=product&p=${ productId }`, { await page.goto( `/?post_type=product&p=${ productId }` );
waitUntil: 'networkidle',
} );
await expect( await expect(
page.getByRole( 'heading', { name: productData.name } ) page.getByRole( 'heading', { name: productData.name } )
).toBeVisible(); ).toBeVisible();
@ -203,7 +149,6 @@ test.describe( 'General tab', () => {
await page await page
.locator( `a.remove[data-product_id='${ productId }']` ) .locator( `a.remove[data-product_id='${ productId }']` )
.click(); .click();
await page.waitForLoadState( 'networkidle' );
await expect( await expect(
page.locator( `a.remove[data-product_id='${ productId }']` ) page.locator( `a.remove[data-product_id='${ productId }']` )
).toBeHidden(); ).toBeHidden();

View File

@ -1,25 +1,8 @@
const { test: baseTest, expect } = require( '@playwright/test' ); const { test: baseTest } = require( './block-editor-fixtures' );
const { const { expect } = require( '../../../../fixtures' );
toggleBlockProductEditor,
} = require( '../../../../utils/simple-products' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
baseTest.describe( 'Products > Edit Product', () => { baseTest.describe( 'Products > Edit Product', () => {
baseTest.use( { storageState: process.env.ADMINSTATE } );
const test = baseTest.extend( { const test = baseTest.extend( {
api: async ( { baseURL }, use ) => { product: async ( { api }, use ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await use( api );
},
product: async ( { page, api }, use ) => {
let product; let product;
await api await api
@ -35,15 +18,28 @@ baseTest.describe( 'Products > Edit Product', () => {
product = response.data; product = response.data;
} ); } );
await test.step( 'ensure block product editor is enabled', async () => {
await toggleBlockProductEditor( 'enable', page );
} );
await use( product ); await use( product );
// Cleanup // Cleanup
await api.delete( `products/${ product.id }`, { force: true } ); await api.delete( `products/${ product.id }`, { force: true } );
}, },
page: async ( { page, api }, use ) => {
await api.put(
'settings/advanced/woocommerce_feature_product_block_editor_enabled',
{
value: 'yes',
}
);
await use( page );
await api.put(
'settings/advanced/woocommerce_feature_product_block_editor_enabled',
{
value: 'no',
}
);
},
} ); } );
test( 'can update the general information of a product', async ( { test( 'can update the general information of a product', async ( {

View File

@ -0,0 +1,252 @@
const { test: baseTest } = require( './block-editor-fixtures' );
const { expect } = require( '../../../../fixtures' );
async function selectImagesInLibrary( page, imagesNames ) {
const dataIds = [];
await page.getByRole( 'tab', { name: 'Media Library' } ).click();
// Select the given images
for ( const imageName of imagesNames ) {
await page
.getByRole( 'searchbox', { name: 'Search' } )
.fill( imageName );
const imageLocator = page.getByLabel( imageName ).nth( 0 );
await imageLocator.click();
await expect( imageLocator ).toBeChecked();
const dataId = await imageLocator.getAttribute( 'data-id' );
dataIds.push( dataId );
}
await page.getByRole( 'button', { name: 'Select', exact: true } ).click();
return dataIds;
}
baseTest.describe( 'Products > Edit Product', () => {
const test = baseTest.extend( {
product: async ( { api }, use ) => {
let product;
await api
.post( 'products', {
name: `Product ${ Date.now() }`,
type: 'simple',
description: `This is a description of the awesome product ${ Date.now() }`,
short_description: `This product is pretty awesome ${ Date.now() }`,
regular_price: '12.99',
} )
.then( ( response ) => {
product = response.data;
} );
await use( product );
// Cleanup
await api.delete( `products/${ product.id }`, { force: true } );
},
productWithGallery: async ( { api, product }, use ) => {
let productWithGallery;
await api
.put( `products/${ product.id }`, {
images: [
{
src: 'http://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2013/06/T_2_front.jpg',
},
{
src: 'http://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2013/06/T_2_back.jpg',
},
{
src: 'http://demo.woothemes.com/woocommerce/wp-content/uploads/sites/56/2013/06/T_3_front.jpg',
},
],
} )
.then( ( response ) => {
productWithGallery = response.data;
} );
await use( productWithGallery );
},
} );
test( 'can add images', async ( { page, product } ) => {
const images = [ 'image-01', 'image-02' ];
await test.step( 'navigate to product edit page', async () => {
await page.goto(
`wp-admin/post.php?post=${ product.id }&action=edit`
);
} );
await test.step( 'add images', async () => {
await page.getByText( 'Choose an image' ).click();
const dataIds = await selectImagesInLibrary( page, images );
await expect(
page.getByLabel( 'Block: Product images' ).locator( 'img' )
).toHaveCount( images.length );
for ( const dataId of dataIds ) {
await expect(
page
.getByLabel( 'Block: Product images' )
.locator( `img[id="${ dataId }"]` )
).toBeVisible();
}
} );
await test.step( 'update the product', async () => {
await page.getByRole( 'button', { name: 'Update' } ).click();
// Verify product was updated
await expect(
page.getByLabel( 'Dismiss this notice' )
).toContainText( 'Product updated' );
} );
await test.step( 'verify product image was set', async () => {
// Verify image in store frontend
await page.goto( product.permalink );
for ( const image of images ) {
await expect( page.getByTitle( image ) ).toBeVisible();
}
} );
} );
test( 'can replace an image', async ( { page, productWithGallery } ) => {
const initialImagesCount = productWithGallery.images.length;
const newImageName = 'image-01';
const replacedImgLocator = page
.getByLabel( 'Block: Product images' )
.locator( 'img' )
.nth( 1 );
let dataIds = [];
await test.step( 'navigate to product edit page', async () => {
await page.goto(
`wp-admin/post.php?post=${ productWithGallery.id }&action=edit`
);
} );
await test.step( 'replace an image', async () => {
await replacedImgLocator.click();
await page
.getByRole( 'toolbar', { name: 'Options' } )
.getByLabel( 'Options' )
.click();
await page.getByRole( 'menuitem', { name: 'Replace' } ).click();
dataIds = await selectImagesInLibrary( page, [ newImageName ] );
expect( await replacedImgLocator.getAttribute( 'src' ) ).toContain(
newImageName
);
} );
await test.step( 'update the product', async () => {
await page.getByRole( 'button', { name: 'Update' } ).click();
// Verify product was updated
await expect(
page.getByLabel( 'Dismiss this notice' )
).toContainText( 'Product updated' );
} );
await test.step( 'verify product image was set', async () => {
await expect( replacedImgLocator ).toHaveId( dataIds[ 0 ] );
await expect(
page.getByLabel( 'Block: Product images' ).locator( 'img' )
).toHaveCount( initialImagesCount );
// Verify image in store frontend
await page.goto( productWithGallery.permalink );
await expect( page.getByTitle( newImageName ) ).toBeVisible();
} );
} );
test( 'can remove an image', async ( { page, productWithGallery } ) => {
const initialImagesCount = productWithGallery.images.length;
const removedImgLocator = page
.getByLabel( 'Block: Product images' )
.locator( 'img' )
.nth( 1 );
await test.step( 'navigate to product edit page', async () => {
await page.goto(
`wp-admin/post.php?post=${ productWithGallery.id }&action=edit`
);
} );
await test.step( 'remove an image', async () => {
await removedImgLocator.click();
await page
.getByRole( 'toolbar', { name: 'Options' } )
.getByLabel( 'Options' )
.click();
await page.getByRole( 'menuitem', { name: 'Remove' } ).click();
await expect(
page.getByLabel( 'Block: Product images' ).locator( 'img' )
).toHaveCount( initialImagesCount - 1 );
} );
await test.step( 'update the product', async () => {
await page.getByRole( 'button', { name: 'Update' } ).click();
// Verify product was updated
await expect(
page.getByLabel( 'Dismiss this notice' )
).toContainText( 'Product updated' );
} );
await test.step( 'verify product image was set', async () => {
await expect(
page.getByLabel( 'Block: Product images' ).locator( 'img' )
).toHaveCount( initialImagesCount - 1 );
// Verify image in store frontend
await page.goto( productWithGallery.permalink );
await expect(
page.locator( `#product-${ productWithGallery.id } ol img` )
).toHaveCount( initialImagesCount - 1 );
} );
} );
test( 'can set an image as cover', async ( {
page,
productWithGallery,
} ) => {
const newCoverImgLocator = page
.getByLabel( 'Block: Product images' )
.locator( 'img' )
.nth( 1 );
await test.step( 'navigate to product edit page', async () => {
await page.goto(
`wp-admin/post.php?post=${ productWithGallery.id }&action=edit`
);
} );
const newCoverImgId = await newCoverImgLocator.getAttribute( 'id' );
await test.step( 'remove an image', async () => {
await newCoverImgLocator.click();
await page.getByLabel( 'Set as cover' ).click();
await expect(
page.getByRole( 'button', { name: 'Cover' } ).locator( 'img' )
).toHaveId( newCoverImgId );
} );
await test.step( 'update the product', async () => {
await page.getByRole( 'button', { name: 'Update' } ).click();
// Verify product was updated
await expect(
page.getByLabel( 'Dismiss this notice' )
).toContainText( 'Product updated' );
} );
await test.step( 'verify product image was set', async () => {
await expect(
page.getByRole( 'button', { name: 'Cover' } ).locator( 'img' )
).toHaveId( newCoverImgId );
} );
} );
} );