Product Editor: improve E2E tests. Test the `+3 More` item label in the Organization tab (#48891)
* introduce attributes fixture * expose the tabs from the new data/ folder * introduce confirmGlobalAttributesLoaded helper * reuse confirmGlobalAttributesLoaded helper * reuse attributes fixture * check the `+3 More` item label * changelog * not only this test * rename to waitForGlobalAttributesLoaded
This commit is contained in:
parent
66ae029f70
commit
38390ab6e8
|
@ -0,0 +1,4 @@
|
|||
Significance: patch
|
||||
Type: update
|
||||
|
||||
Product Editor: improve E2E tests. Test the `+3 More` item label in the Organization tab
|
|
@ -8,6 +8,11 @@ const {
|
|||
} = require( '../../../../utils/product-block-editor' );
|
||||
|
||||
const { variableProducts: utils } = require( '../../../../utils' );
|
||||
const attributes = require( './fixtures/attributes' );
|
||||
const tabs = require( './data/tabs' );
|
||||
const {
|
||||
waitForGlobalAttributesLoaded,
|
||||
} = require( './helpers/confirm-global-attributes-loaded' );
|
||||
|
||||
const {
|
||||
createVariableProduct,
|
||||
|
@ -26,48 +31,18 @@ const productData = {
|
|||
summary: 'This is a product summary',
|
||||
};
|
||||
|
||||
const attributesData = {
|
||||
name: 'Size',
|
||||
terms: [
|
||||
{
|
||||
name: 'Large',
|
||||
slug: 'large',
|
||||
},
|
||||
{
|
||||
name: 'Extra Large',
|
||||
slug: 'extra-large',
|
||||
},
|
||||
{
|
||||
name: 'Extra Extra Large',
|
||||
slug: 'extra-extra-large',
|
||||
},
|
||||
],
|
||||
};
|
||||
const sizeAttribute = attributes.find(
|
||||
( attribute ) => attribute.name === 'Size'
|
||||
);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
name: 'General',
|
||||
noteText:
|
||||
"This product has options, such as size or color. You can manage each variation's images, downloads, and other details individually.",
|
||||
},
|
||||
{
|
||||
name: 'Inventory',
|
||||
noteText:
|
||||
"This product has options, such as size or color. You can now manage each variation's inventory and other details individually.",
|
||||
},
|
||||
{
|
||||
name: 'Shipping',
|
||||
noteText:
|
||||
"This product has options, such as size or color. You can now manage each variation's shipping settings and other details individually.",
|
||||
},
|
||||
];
|
||||
const termsLength = sizeAttribute.terms.length;
|
||||
|
||||
let productId_editVariations,
|
||||
productId_deleteVariations,
|
||||
productId_singleVariation;
|
||||
|
||||
test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
||||
test.describe( 'Create variable product', () => {
|
||||
test.describe( 'Create variable products', () => {
|
||||
test.beforeAll( async ( { browser } ) => {
|
||||
productId_editVariations = await createVariableProduct(
|
||||
productAttributes
|
||||
|
@ -132,31 +107,18 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
|
||||
await page.waitForLoadState( 'domcontentloaded' );
|
||||
|
||||
/*
|
||||
* AttributeTableRow is the row that contains
|
||||
* the attribute name and the options (terms).
|
||||
*/
|
||||
const rowSelector =
|
||||
'.woocommerce-new-attribute-modal__table-row';
|
||||
|
||||
/*
|
||||
* Check the app loads the attributes,
|
||||
* based on the Spinner visibility.
|
||||
*/
|
||||
const spinnerLocator = page.locator(
|
||||
`${ rowSelector } .components-spinner`
|
||||
);
|
||||
await spinnerLocator.waitFor( {
|
||||
state: 'visible',
|
||||
} );
|
||||
await spinnerLocator.waitFor( { state: 'hidden' } );
|
||||
await waitForGlobalAttributesLoaded( page );
|
||||
|
||||
// Attribute combobox input
|
||||
const attributeInputLocator = page.locator(
|
||||
'input[aria-describedby^="components-form-token-suggestions-howto-combobox-control"]'
|
||||
);
|
||||
|
||||
await attributeInputLocator.fill( attributesData.name );
|
||||
await attributeInputLocator.fill( sizeAttribute.name );
|
||||
|
||||
await page.locator( 'text=Create "Size"' ).click();
|
||||
|
||||
|
@ -166,7 +128,7 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
response
|
||||
.url()
|
||||
.includes(
|
||||
`wp-json/wc/v3/products/attributes?name=${ attributesData.name }&generate_slug=true`
|
||||
`wp-json/wc/v3/products/attributes?name=${ sizeAttribute.name }&generate_slug=true`
|
||||
) && response.status() === 201
|
||||
);
|
||||
|
||||
|
@ -183,7 +145,7 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
'input[id^="components-form-token-input-"]'
|
||||
);
|
||||
|
||||
for ( const term of attributesData.terms ) {
|
||||
for ( const term of sizeAttribute.terms ) {
|
||||
// Fill the input field with the option
|
||||
await FormTokenFieldInputLocator.fill( term.name );
|
||||
await FormTokenFieldInputLocator.press( 'Enter' );
|
||||
|
@ -228,6 +190,7 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
} );
|
||||
}
|
||||
|
||||
// Wait for the last term to be validated/added
|
||||
await expect( page.locator( '.is-validating' ) ).toBeHidden();
|
||||
|
||||
await page
|
||||
|
@ -241,7 +204,7 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
await test.step( 'Add prices to variations', async () => {
|
||||
await expect(
|
||||
page.getByText(
|
||||
'3 variations do not have prices. Variations that do not have prices will not be visible to customers.Set prices'
|
||||
`${ termsLength } variations do not have prices. Variations that do not have prices will not be visible to customers.Set prices`
|
||||
)
|
||||
).toBeVisible();
|
||||
|
||||
|
@ -255,10 +218,12 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
|
||||
await expect(
|
||||
page.getByLabel( 'Dismiss this notice' )
|
||||
).toContainText( '3 variations updated.' );
|
||||
).toContainText( `${ termsLength } variations updated.` );
|
||||
|
||||
await expect(
|
||||
page.getByRole( 'button', { name: 'Select all (3)' } )
|
||||
page.getByRole( 'button', {
|
||||
name: `Select all (${ termsLength })`,
|
||||
} )
|
||||
).toBeVisible();
|
||||
} );
|
||||
|
||||
|
@ -282,7 +247,7 @@ test.describe( 'Variations tab', { tag: '@gutenberg' }, () => {
|
|||
|
||||
// Verify that the first message is the expected one
|
||||
await expect( snackbarLocator.nth( 0 ) ).toHaveText(
|
||||
`${ attributesData.terms.length } variations updated.`
|
||||
`${ sizeAttribute.terms.length } variations updated.`
|
||||
);
|
||||
|
||||
// Verify that the second message is the expected one
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Tabs (groups) define the main menu (header) of the product edit page.
|
||||
* Each tab has a name and a note text that is displayed below the tab name.
|
||||
*/
|
||||
const tabs = [
|
||||
{
|
||||
name: 'General',
|
||||
noteText:
|
||||
"This product has options, such as size or color. You can manage each variation's images, downloads, and other details individually.",
|
||||
},
|
||||
{
|
||||
name: 'Inventory',
|
||||
noteText:
|
||||
"This product has options, such as size or color. You can now manage each variation's inventory and other details individually.",
|
||||
},
|
||||
{
|
||||
name: 'Shipping',
|
||||
noteText:
|
||||
"This product has options, such as size or color. You can now manage each variation's shipping settings and other details individually.",
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = tabs;
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Attributes data
|
||||
* These attributes represent the attributes
|
||||
* that the user can assign to a product.
|
||||
*/
|
||||
const attributes = [
|
||||
{
|
||||
name: 'Color',
|
||||
terms: [
|
||||
{ name: 'Red', slug: 'red' },
|
||||
{ name: 'Blue', slug: 'blue' },
|
||||
{ name: 'Green', slug: 'green' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Size',
|
||||
terms: [
|
||||
{ name: 'Small', slug: 'small' },
|
||||
{ name: 'Medium', slug: 'medium' },
|
||||
{ name: 'Large', slug: 'large' },
|
||||
{ name: 'Extra Large', slug: 'extra-large' },
|
||||
{ name: 'Extra Extra Large', slug: 'extra-extra-large' },
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'Style',
|
||||
terms: [
|
||||
{ name: 'Modern', slug: 'modern' },
|
||||
{ name: 'Classic', slug: 'classic' },
|
||||
{ name: 'Vintage', slug: 'vintage' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = attributes;
|
|
@ -0,0 +1,18 @@
|
|||
/**
|
||||
* Waits for the spinner to appear and disappear,
|
||||
* indicating that attributes have been loaded.
|
||||
*
|
||||
* This function confirms that the attributes have been
|
||||
* loaded by waiting for a spinner to appear and then disappear.
|
||||
* The spinner indicates that a loading process is occurring,
|
||||
* and its disappearance indicates that the process is complete.
|
||||
*
|
||||
* @param {Object} page - The Playwright Page object.
|
||||
*/
|
||||
export async function waitForGlobalAttributesLoaded( page ) {
|
||||
const spinnerLocator = page.locator(
|
||||
'.woocommerce-new-attribute-modal__table-row .components-spinner'
|
||||
);
|
||||
await spinnerLocator.waitFor( { state: 'visible' } );
|
||||
await spinnerLocator.waitFor( { state: 'hidden' } );
|
||||
}
|
|
@ -4,6 +4,10 @@ const {
|
|||
const { expect } = require( '../../../../fixtures/fixtures' );
|
||||
const { updateProduct } = require( '../../../../utils/product-block-editor' );
|
||||
const { clickOnTab } = require( '../../../../utils/simple-products' );
|
||||
const attributesData = require( './fixtures/attributes' );
|
||||
const {
|
||||
waitForGlobalAttributesLoaded,
|
||||
} = require( './helpers/confirm-global-attributes-loaded' );
|
||||
|
||||
async function waitForAttributeList( page ) {
|
||||
// The list child is different in case there are no results versus when there already are some attributes, so we need to wait for either one to be visible.
|
||||
|
@ -99,22 +103,6 @@ test(
|
|||
'add local attribute (with terms) to the Product',
|
||||
{ tag: '@gutenberg' },
|
||||
async ( { page, product } ) => {
|
||||
const newAttributes = [
|
||||
{
|
||||
name: 'Color',
|
||||
|
||||
terms: [ 'Red', 'Blue', 'Green' ],
|
||||
},
|
||||
{
|
||||
name: 'Size',
|
||||
terms: [ 'Small', 'Medium', 'Large' ],
|
||||
},
|
||||
{
|
||||
name: 'Style',
|
||||
terms: [ 'Modern', 'Classic', 'Vintage' ],
|
||||
},
|
||||
];
|
||||
|
||||
await test.step( 'go to product editor -> Organization tab -> Click on `Add new`', async () => {
|
||||
await page.goto(
|
||||
`wp-admin/post.php?post=${ product.id }&action=edit`
|
||||
|
@ -147,15 +135,9 @@ test(
|
|||
* First, check the app loads the attributes,
|
||||
* based on the Spinner visibility.
|
||||
*/
|
||||
const spinnerLocator = attributeRowsLocator.locator(
|
||||
'.components-spinner'
|
||||
);
|
||||
await spinnerLocator.waitFor( {
|
||||
state: 'visible',
|
||||
} );
|
||||
await spinnerLocator.waitFor( { state: 'hidden' } );
|
||||
await waitForGlobalAttributesLoaded( page );
|
||||
|
||||
for ( const term of newAttributes ) {
|
||||
for ( const attribute of attributesData ) {
|
||||
const attributeRowLocator = attributeRowsLocator.last();
|
||||
|
||||
const attributeComboboxLocator = attributeRowLocator
|
||||
|
@ -165,8 +147,10 @@ test(
|
|||
.last();
|
||||
|
||||
// Create new (local) product attribute.
|
||||
await attributeComboboxLocator.fill( term.name );
|
||||
await page.locator( `text=Create "${ term.name }"` ).click();
|
||||
await attributeComboboxLocator.fill( attribute.name );
|
||||
await page
|
||||
.locator( `text=Create "${ attribute.name }"` )
|
||||
.click();
|
||||
|
||||
const FormTokenFieldLocator = attributeRowLocator.locator(
|
||||
'td.woocommerce-new-attribute-modal__table-attribute-value-column'
|
||||
|
@ -179,8 +163,8 @@ test(
|
|||
);
|
||||
|
||||
// Add terms to the attribute.
|
||||
for ( const attributeTerm of term.terms ) {
|
||||
await FormTokenFieldInputLocator.fill( attributeTerm );
|
||||
for ( const term of attribute.terms ) {
|
||||
await FormTokenFieldInputLocator.fill( term.name );
|
||||
await FormTokenFieldInputLocator.press( 'Enter' );
|
||||
}
|
||||
|
||||
|
@ -203,25 +187,42 @@ test(
|
|||
const attributeRowsLocator =
|
||||
attributesListLocator.getByRole( 'listitem' );
|
||||
|
||||
for ( const attribute of newAttributes ) {
|
||||
for ( const attribute of attributesData ) {
|
||||
const attributeRowLocator = attributeRowsLocator.filter( {
|
||||
has: page.getByText( attribute.name, { exact: true } ),
|
||||
} );
|
||||
await expect( attributeRowLocator ).toBeVisible();
|
||||
|
||||
for ( const term of attribute.terms ) {
|
||||
// UI only shows 3 terms, so we need to check if the term is visible.
|
||||
const shownTerms = attribute.terms.slice( 0, 3 ).entries();
|
||||
|
||||
// Check if there are more terms than three.
|
||||
const moreThanThreeTerms = attribute.terms.length > 3;
|
||||
|
||||
for ( const [ index, term ] of shownTerms ) {
|
||||
/*
|
||||
* Disabling the eslint rule because the text
|
||||
* is different when there are more than three terms.
|
||||
*/
|
||||
const termLabel =
|
||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
||||
moreThanThreeTerms && index === 2
|
||||
? '+ 3 more'
|
||||
: term.name;
|
||||
// Pick the term element/locator
|
||||
const termLocator = attributeRowLocator
|
||||
.locator( `[aria-hidden="true"]` )
|
||||
.filter( {
|
||||
has: page.getByText( term ),
|
||||
has: page.getByText( termLabel, {
|
||||
exact: true,
|
||||
} ),
|
||||
} );
|
||||
|
||||
// Verify the term is visible
|
||||
await expect( termLocator ).toBeVisible();
|
||||
|
||||
// Verify the term text
|
||||
await expect( termLocator ).toContainText( term );
|
||||
await expect( termLocator ).toContainText( termLabel );
|
||||
}
|
||||
}
|
||||
} );
|
||||
|
@ -235,7 +236,7 @@ test(
|
|||
await page.goto( product.permalink );
|
||||
|
||||
// Verify attributes in store frontend
|
||||
for ( const attribute of newAttributes ) {
|
||||
for ( const attribute of attributesData ) {
|
||||
const item = page.getByRole( 'row' ).filter( {
|
||||
has: page.getByText( attribute.name, { exact: true } ),
|
||||
} );
|
||||
|
|
Loading…
Reference in New Issue