From 054fa4f65a5e53dbda67c5e5a0bf488b1a0380ec Mon Sep 17 00:00:00 2001 From: rodelgc Date: Wed, 17 May 2023 23:25:35 +0800 Subject: [PATCH] Checkout the new files --- .../create-product-attributes.spec.js | 131 ++++ .../create-variable-product.spec.js | 120 ++++ .../create-variations.spec.js | 194 ++++++ .../update-variations.spec.js | 610 ++++++++++++++++++ 4 files changed, 1055 insertions(+) create mode 100644 plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js create mode 100644 plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variable-product.spec.js create mode 100644 plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variations.spec.js create mode 100644 plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/update-variations.spec.js diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js new file mode 100644 index 00000000000..bcdef762f15 --- /dev/null +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-product-attributes.spec.js @@ -0,0 +1,131 @@ +const { test, expect } = require( '@playwright/test' ); +const { variableProducts: utils } = require( '../../../../utils' ); +const { + createVariableProduct, + showVariableProductTour, + deleteProductsAddedByTests, + productAttributes, +} = utils; + +let productId; + +test.describe( 'Add product attributes', () => { + test.use( { storageState: process.env.ADMINSTATE } ); + + test.beforeAll( async ( { browser } ) => { + productId = await createVariableProduct(); + + await showVariableProductTour( browser, false ); + } ); + + test.afterAll( async () => { + await deleteProductsAddedByTests(); + } ); + + test( 'can add custom product attributes', async ( { page } ) => { + await test.step( + `Open "Edit product" page of product id ${ productId }`, + async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId }&action=edit` + ); + } + ); + + await test.step( 'Go to the "Attributes" tab.', async () => { + const attributesTab = page + .locator( '.attribute_tab' ) + .getByRole( 'link', { name: 'Attributes' } ); + + await attributesTab.click(); + } ); + + await test.step( + `Add ${ productAttributes.length } attributes.`, + async () => { + for ( let i = 0; i < productAttributes.length; i++ ) { + const attributeName = productAttributes[ i ].name; + const attributeValues = productAttributes[ i ].options.join( + '|' + ); + + if ( i > 0 ) { + await test.step( "Click 'Add new'.", async () => { + await page + .locator( '#product_attributes .toolbar-top' ) + .getByRole( 'button', { name: 'Add new' } ) + .click(); + } ); + } + + await test.step( + `Add the attribute "${ attributeName }" with values "${ attributeValues }"`, + async () => { + await test.step( + `Type "${ attributeName }" in the "Attribute name" input field.`, + async () => { + await page + .getByPlaceholder( + 'f.e. size or color' + ) + .nth( i ) + .type( attributeName ); + } + ); + + await test.step( + `Type the attribute values "${ attributeValues }".`, + async () => { + await page + .getByPlaceholder( + 'Enter options for customers to choose from' + ) + .nth( i ) + .type( attributeValues ); + } + ); + + await test.step( + 'Click "Save attributes".', + async () => { + await page + .getByRole( 'button', { + name: 'Save attributes', + } ) + .click( { clickCount: 3 } ); + } + ); + + await test.step( + "Wait for the tour's dismissal to be saved", + async () => { + await page.waitForResponse( + ( response ) => + response + .url() + .includes( '/post.php' ) && + response.status() === 200 + ); + } + ); + + await test.step( + `Expect the attribute "${ attributeName }" to be saved`, + async () => { + const savedAttributeHeading = page.getByRole( + 'heading', + { name: attributeName } + ); + + await expect( + savedAttributeHeading + ).toBeVisible(); + } + ); + } + ); + } + } + ); + } ); +} ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variable-product.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variable-product.spec.js new file mode 100644 index 00000000000..25ecc7c40d1 --- /dev/null +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variable-product.spec.js @@ -0,0 +1,120 @@ +const { test, expect } = require( '@playwright/test' ); +const { variableProducts: utils, api } = require( '../../../../utils' ); +const { showVariableProductTour } = utils; +const productPageURL = 'wp-admin/post-new.php?post_type=product'; +const variableProductName = 'Variable Product with Three Variations'; + +let productId; + +test.describe( 'Add variable product', () => { + test.use( { storageState: process.env.ADMINSTATE } ); + + test.beforeAll( async ( { browser } ) => { + await showVariableProductTour( browser, true ); + } ); + + test.afterAll( async ( { browser } ) => { + await showVariableProductTour( browser, false ); + await api.deletePost.product( productId ); + } ); + + test( 'can create a variable product', async ( { page } ) => { + await test.step( 'Go to the "Add new product" page', async () => { + await page.goto( productPageURL ); + } ); + + await test.step( + `Type "${ variableProductName }" into the "Product name" input field.`, + async () => { + await page + .getByLabel( 'Product name' ) + .fill( variableProductName ); + } + ); + + await test.step( + 'Select the "Variable product" product type.', + async () => { + await page.selectOption( '#product-type', 'variable' ); + } + ); + + await test.step( + 'Scroll into the "Attributes" tab and click it.', + async () => { + const attributesTab = page + .locator( '.attribute_tab' ) + .getByRole( 'link', { name: 'Attributes' } ); + + await attributesTab.scrollIntoViewIfNeeded(); + + await attributesTab.click(); + } + ); + + // the tour only seems to display when not running headless, so just make sure + const tourWasDisplayed = await test.step( + 'See if the tour was displayed.', + async () => { + return await page + .locator( '.woocommerce-tour-kit-step__heading' ) + .isVisible(); + } + ); + + if ( tourWasDisplayed ) { + await test.step( 'Tour was displayed, so dismiss it.', async () => { + await page + .getByRole( 'button', { name: 'Close Tour' } ) + .click(); + } ); + + await test.step( + "Wait for the tour's dismissal to be saved", + async () => { + await page.waitForResponse( + ( response ) => + response.url().includes( '/users/' ) && + response.status() === 200 + ); + } + ); + } + + await test.step( `Expect the "Variations" tab to appear`, async () => { + const variationsTab = page.locator( 'li.variations_tab' ); + + await expect( variationsTab ).toBeVisible(); + } ); + + await test.step( 'Save draft.', async () => { + await page.locator( '#save-post' ).click(); + } ); + + await test.step( + 'Expect the "Product draft updated." notice to appear.', + async () => { + await expect( + page.getByText( 'Product draft updated.' ) + ).toBeVisible(); + } + ); + + await test.step( + 'Expect the product type to be "Variable product"', + async () => { + const selectedProductType = page.locator( + 'select#product-type [selected]' + ); + + await expect( selectedProductType ).toHaveText( + 'Variable product' + ); + } + ); + + await test.step( 'Save product ID for clean up.', async () => { + productId = page.url().match( /(?<=post=)\d+/ ); + } ); + } ); +} ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variations.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variations.spec.js new file mode 100644 index 00000000000..bb4c5d72316 --- /dev/null +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/create-variations.spec.js @@ -0,0 +1,194 @@ +const { test, expect } = require( '@playwright/test' ); +const { variableProducts: utils } = require( '../../../../utils' ); +const { + createVariableProduct, + showVariableProductTour, + deleteProductsAddedByTests, + generateVariationsFromAttributes, + productAttributes, +} = utils; + +let expectedGeneratedVariations, + productId_addManually, + productId_generateVariations, + variationsToManuallyCreate; + +test.describe( 'Add variations', () => { + test.use( { storageState: process.env.ADMINSTATE } ); + + test.beforeAll( async ( { browser } ) => { + productId_generateVariations = await createVariableProduct( + productAttributes + ); + + productId_addManually = await createVariableProduct( + productAttributes + ); + + expectedGeneratedVariations = generateVariationsFromAttributes( + productAttributes + ); + + variationsToManuallyCreate = expectedGeneratedVariations.slice( 0, 3 ); + + await showVariableProductTour( browser, false ); + } ); + + test.afterAll( async () => { + await deleteProductsAddedByTests(); + } ); + + test( 'can generate variations from product attributes', async ( { + page, + } ) => { + await test.step( + `Open "Edit product" page of product id ${ productId_generateVariations }`, + async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_generateVariations }&action=edit` + ); + } + ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.click( 'a[href="#variable_product_options"]' ); + } ); + + await test.step( + 'Click on the "Generate variations" button.', + async () => { + // event listener for handling the link_all_variations confirmation dialog + page.on( 'dialog', ( dialog ) => dialog.accept() ); + + await page.click( 'button.generate_variations' ); + } + ); + + await test.step( + `Expect the number of variations to be ${ expectedGeneratedVariations.length }`, + async () => { + const variations = page.locator( '.woocommerce_variation' ); + + await expect( variations ).toHaveCount( + expectedGeneratedVariations.length + ); + } + ); + + for ( const variation of expectedGeneratedVariations ) { + await test.step( + `Expect the variation "${ variation.join( + ', ' + ) }" to be generated.`, + async () => { + let variationRow = page.locator( + '.woocommerce_variation h3' + ); + + for ( const attributeValue of variation ) { + variationRow = variationRow.filter( { + has: page.locator( 'option[selected]', { + hasText: attributeValue, + } ), + } ); + } + + await expect( variationRow ).toBeVisible(); + } + ); + } + } ); + + test( 'can manually add a variation', async ( { page } ) => { + await test.step( + `Open "Edit product" page of product id ${ productId_addManually }`, + async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_addManually }&action=edit` + ); + } + ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.click( 'a[href="#variable_product_options"]' ); + } ); + + await test.step( + `Manually add ${ variationsToManuallyCreate.length } variations`, + async () => { + const variationRows = page.locator( + '.woocommerce_variation h3' + ); + let variationRowsCount = await variationRows.count(); + + for ( const variationToCreate of variationsToManuallyCreate ) { + await test.step( 'Click "Add manually"', async () => { + const addManuallyButton = page.getByRole( 'button', { + name: 'Add manually', + } ); + + await addManuallyButton.click(); + + await expect( variationRows ).toHaveCount( + ++variationRowsCount + ); + } ); + + for ( const attributeValue of variationToCreate ) { + const attributeName = productAttributes.find( + ( { options } ) => + options.includes( attributeValue ) + ).name; + const addAttributeMenu = variationRows + .nth( 0 ) + .locator( 'select', { + has: page.locator( 'option', { + hasText: attributeValue, + } ), + } ); + + await test.step( + `Select "${ attributeValue }" from the "${ attributeName }" attribute menu`, + async () => { + await addAttributeMenu.selectOption( + attributeValue + ); + } + ); + } + + await test.step( 'Click "Save changes"', async () => { + await page + .getByRole( 'button', { + name: 'Save changes', + } ) + .click(); + } ); + + await test.step( + `Expect the variation ${ variationToCreate.join( + ', ' + ) } to be successfully saved.`, + async () => { + let newlyAddedVariationRow; + + for ( const attributeValue of variationToCreate ) { + newlyAddedVariationRow = ( + newlyAddedVariationRow || variationRows + ).filter( { + has: page.locator( 'option[selected]', { + hasText: attributeValue, + } ), + } ); + } + + await expect( + newlyAddedVariationRow + ).toBeVisible(); + } + ); + } + } + ); + } ); +} ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/update-variations.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/update-variations.spec.js new file mode 100644 index 00000000000..7ea454e7f9d --- /dev/null +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/add-variable-product/update-variations.spec.js @@ -0,0 +1,610 @@ +const { test, expect } = require( '@playwright/test' ); +const { variableProducts: utils } = require( '../../../../utils' ); +const { + createVariableProduct, + showVariableProductTour, + deleteProductsAddedByTests, + productAttributes, + sampleVariations, + createVariations, +} = utils; +const variationOnePrice = '9.99'; +const variationTwoPrice = '11.99'; +const variationThreePrice = '20.00'; +const productWeight = '200'; +const productLength = '10'; +const productWidth = '20'; +const productHeight = '15'; +const stockAmount = '100'; +const lowStockAmount = '10'; + +let productId_indivEdit, + productId_bulkEdit, + productId_deleteAll, + productId_manageStock, + productId_variationDefaults, + productId_removeVariation, + defaultVariation, + variationIds_indivEdit; + +test.describe( 'Update variations', () => { + test.use( { storageState: process.env.ADMINSTATE } ); + + test.beforeAll( async ( { browser } ) => { + await test.step( + 'Create variable product for individual edit test', + async () => { + productId_indivEdit = await createVariableProduct( + productAttributes + ); + + variationIds_indivEdit = await createVariations( + productId_indivEdit, + sampleVariations + ); + } + ); + + await test.step( + 'Create variable product for bulk edit test', + async () => { + productId_bulkEdit = await createVariableProduct( + productAttributes + ); + + await createVariations( productId_bulkEdit, sampleVariations ); + } + ); + + await test.step( + 'Create variable product for "delete all" test', + async () => { + productId_deleteAll = await createVariableProduct( + productAttributes + ); + + await createVariations( productId_deleteAll, sampleVariations ); + } + ); + + await test.step( + 'Create variable product for "manage stock" test', + async () => { + productId_manageStock = await createVariableProduct( + productAttributes + ); + + const variation = sampleVariations.slice( -1 ); + + await createVariations( productId_manageStock, variation ); + } + ); + + await test.step( + 'Create variable product for "variation defaults" test', + async () => { + productId_variationDefaults = await createVariableProduct( + productAttributes + ); + + await createVariations( + productId_variationDefaults, + sampleVariations + ); + + defaultVariation = sampleVariations[ 1 ].attributes; + } + ); + + await test.step( + 'Create variable product with 1 variation for "remove variation" test', + async () => { + productId_removeVariation = await createVariableProduct( + productAttributes + ); + + await createVariations( + productId_removeVariation, + sampleVariations.slice( -1 ) + ); + } + ); + + await test.step( 'Hide variable product tour', async () => { + await showVariableProductTour( browser, false ); + } ); + } ); + + test.afterAll( async () => { + await deleteProductsAddedByTests(); + } ); + + test( 'can individually edit variations', async ( { page } ) => { + const variationRows = page.locator( '.woocommerce_variation' ); + const firstVariation = variationRows.filter( { + hasText: `#${ variationIds_indivEdit[ 0 ] }`, + } ); + const secondVariation = variationRows.filter( { + hasText: `#${ variationIds_indivEdit[ 1 ] }`, + } ); + const thirdVariation = variationRows.filter( { + hasText: `#${ variationIds_indivEdit[ 2 ] }`, + } ); + + await test.step( 'Go to the "Edit product" page.', async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_indivEdit }&action=edit` + ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( 'Expand all variations.', async () => { + await page + .locator( + '#variable_product_options .toolbar-top a.expand_all' + ) + .click(); + } ); + + await test.step( 'Edit the first variation.', async () => { + await test.step( 'Check the "Virtual" checkbox.', async () => { + await firstVariation + .getByRole( 'checkbox', { + name: 'Virtual', + } ) + .check(); + } ); + + await test.step( + `Set regular price to "${ variationOnePrice }".`, + async () => { + await firstVariation + .getByRole( 'textbox', { name: 'Regular price' } ) + .fill( variationOnePrice ); + } + ); + } ); + + await test.step( 'Edit the second variation.', async () => { + await test.step( 'Check the "Virtual" checkbox.', async () => { + await secondVariation + .getByRole( 'checkbox', { + name: 'Virtual', + } ) + .check(); + } ); + + await test.step( + `Set regular price to "${ variationTwoPrice }".`, + async () => { + await secondVariation + .getByRole( 'textbox', { name: 'Regular price' } ) + .fill( variationTwoPrice ); + } + ); + } ); + + await test.step( 'Edit the third variation.', async () => { + await test.step( 'Check "Manage stock?"', async () => { + await thirdVariation + .getByRole( 'checkbox', { name: 'Manage stock?' } ) + .check(); + } ); + + await test.step( + `Set regular price to "${ variationThreePrice }".`, + async () => { + await thirdVariation + .getByRole( 'textbox', { name: 'Regular price' } ) + .fill( variationThreePrice ); + } + ); + + await test.step( 'Set the weight and dimensions.', async () => { + await thirdVariation + .getByRole( 'textbox', { name: 'Weight' } ) + .type( productWeight ); + + await thirdVariation + .getByRole( 'textbox', { name: 'Length' } ) + .type( productLength ); + + await thirdVariation + .getByRole( 'textbox', { name: 'Width' } ) + .type( productWidth ); + + await thirdVariation + .getByRole( 'textbox', { name: 'Height' } ) + .type( productHeight ); + } ); + } ); + + await test.step( 'Click "Save changes".', async () => { + await page.locator( 'button.save-variation-changes' ).click(); + await page.waitForLoadState( 'networkidle' ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( 'Expand all variations.', async () => { + await page + .locator( + '#variable_product_options .toolbar-top a.expand_all' + ) + .click(); + } ); + + await test.step( + 'Expect the first variation to be virtual.', + async () => { + await expect( + firstVariation.getByRole( 'checkbox', { + name: 'Virtual', + } ) + ).toBeChecked(); + } + ); + + await test.step( + `Expect the regular price of the first variation to be "${ variationOnePrice }".`, + async () => { + await expect( + firstVariation.getByRole( 'textbox', { + name: 'Regular price', + } ) + ).toHaveValue( variationOnePrice ); + } + ); + + await test.step( + 'Expect the second variation to be virtual.', + async () => { + await expect( + secondVariation.getByRole( 'checkbox', { + name: 'Virtual', + } ) + ).toBeChecked(); + } + ); + + await test.step( + `Expect the regular price of the second variation to be "${ variationTwoPrice }".`, + async () => { + await expect( + secondVariation.getByRole( 'textbox', { + name: 'Regular price', + } ) + ).toHaveValue( variationTwoPrice ); + } + ); + + await test.step( + 'Expect the "Manage stock?" checkbox of the third variation to be checked.', + async () => { + await expect( + thirdVariation.getByRole( 'checkbox', { + name: 'Manage stock?', + } ) + ).toBeChecked(); + } + ); + + await test.step( + `Expect the regular price of the third variation to be "${ variationThreePrice }".`, + async () => { + await expect( + thirdVariation.getByRole( 'textbox', { + name: 'Regular price', + } ) + ).toHaveValue( variationThreePrice ); + } + ); + + await test.step( + 'Expect the weight and dimensions of the third variation to be correct.', + async () => { + await expect( + thirdVariation.getByRole( 'textbox', { + name: 'Weight', + } ) + ).toHaveValue( productWeight ); + + await expect( + thirdVariation.getByRole( 'textbox', { + name: 'Length', + } ) + ).toHaveValue( productLength ); + + await expect( + thirdVariation.getByRole( 'textbox', { name: 'Width' } ) + ).toHaveValue( productWidth ); + + await expect( + thirdVariation.getByRole( 'textbox', { + name: 'Height', + } ) + ).toHaveValue( productHeight ); + } + ); + } ); + + test( 'can bulk edit variations', async ( { page } ) => { + await test.step( 'Go to the "Edit product" page.', async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_bulkEdit }&action=edit` + ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( + 'Select the \'Toggle "Downloadable"\' bulk action.', + async () => { + await page + .locator( '#field_to_edit' ) + .selectOption( 'toggle_downloadable' ); + } + ); + + await test.step( 'Expand all variations.', async () => { + await page + .locator( + '#variable_product_options .toolbar-top a.expand_all' + ) + .click(); + } ); + + await test.step( + 'Expect all "Downloadable" checkboxes to be checked.', + async () => { + const checkBoxes = page.locator( + 'input[name^="variable_is_downloadable"]' + ); + const count = await checkBoxes.count(); + + for ( let i = 0; i < count; i++ ) { + await expect( checkBoxes.nth( i ) ).toBeChecked(); + } + } + ); + } ); + + test( 'can delete all variations', async ( { page } ) => { + await test.step( 'Go to the "Edit product" page.', async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_deleteAll }&action=edit` + ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( + 'Select the bulk action "Delete all variations".', + async () => { + page.on( 'dialog', ( dialog ) => dialog.accept() ); + await page + .locator( '#field_to_edit' ) + .selectOption( 'delete_all' ); + } + ); + + await test.step( + 'Expect that there are no more variations.', + async () => { + await expect( + page.locator( '.woocommerce_variation' ) + ).toHaveCount( 0 ); + } + ); + } ); + + test( 'can manage stock levels', async ( { page } ) => { + await test.step( 'Go to the "Edit product" page.', async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_manageStock }&action=edit` + ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( 'Expand all variations', async () => { + await page + .locator( + '#variable_product_options .toolbar-top a.expand_all' + ) + .click(); + } ); + + const variationContainer = page.locator( + '.woocommerce_variations .woocommerce_variation' + ); + + await test.step( 'Check the "Manage stock?" box', async () => { + await variationContainer + .locator( 'input.checkbox.variable_manage_stock' ) + .check(); + } ); + + await test.step( + `Expect the "Stock status" text box to disappear`, + async () => { + await expect( + variationContainer.locator( 'p.variable_stock_status' ) + ).not.toBeVisible(); + } + ); + + await test.step( + `Enter "${ variationOnePrice }" as the regular price`, + async () => { + await variationContainer + .getByPlaceholder( 'Variation price (required)' ) + .fill( variationOnePrice ); + } + ); + + await test.step( + `Enter "${ stockAmount }" as the stock quantity`, + async () => { + await variationContainer + .locator( 'input[name^="variable_stock"]' ) + .fill( stockAmount ); + } + ); + + await test.step( + 'Select "Allow, but notify customer" from the "Allow backorders?" menu', + async () => { + await variationContainer + .locator( 'select[name^="variable_backorders"]' ) + .selectOption( 'notify' ); + } + ); + + await test.step( + `Enter "${ lowStockAmount }" in the "Low stock threshold" input field.`, + async () => { + await variationContainer + .getByPlaceholder( 'Store-wide threshold' ) + .fill( lowStockAmount ); + } + ); + + await test.step( 'Click "Save changes"', async () => { + await page.locator( 'button.save-variation-changes' ).click(); + } ); + + await test.step( 'Expand all variations', async () => { + await page + .locator( + '#variable_product_options .toolbar-top a.expand_all' + ) + .click(); + } ); + + await test.step( + 'Expect the stock quantity to be saved correctly', + async () => { + await expect( + variationContainer.locator( + 'input[name^="variable_stock"]' + ) + ).toHaveValue( stockAmount ); + } + ); + + await test.step( + 'Expect the "Low stock threshold" value to be saved correctly', + async () => { + await expect( + variationContainer.getByPlaceholder( + 'Store-wide threshold' + ) + ).toHaveValue( lowStockAmount ); + } + ); + + await test.step( + 'Expect the "Allow backorders?" value to be saved correctly', + async () => { + await expect( + variationContainer.locator( + 'select[name^="variable_backorders"] > option[selected]' + ) + ).toHaveText( 'Allow, but notify customer' ); + } + ); + } ); + + test( 'can set variation defaults', async ( { page } ) => { + await test.step( 'Go to the "Edit product" page.', async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_variationDefaults }&action=edit` + ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( 'Select variation defaults', async () => { + for ( const attribute of defaultVariation ) { + const defaultAttributeMenu = page.locator( 'select', { + hasText: `No default ${ attribute.name }…`, + } ); + + await defaultAttributeMenu.selectOption( attribute.option ); + } + } ); + + await test.step( 'Click "Save changes"', async () => { + await page.getByRole( 'button', { name: 'Save changes' } ).click(); + await page.waitForLoadState( 'networkidle' ); + } ); + + await test.step( 'View the product from the shop', async () => { + const permalink = await page + .locator( '#sample-permalink a' ) + .getAttribute( 'href' ); + + await page.goto( permalink ); + } ); + + await test.step( + 'Expect the default attributes to be pre-selected', + async () => { + for ( const attribute of defaultVariation ) { + await test.step( + `Expect "${ attribute.option }" is selected as the default "${ attribute.name }"`, + async () => { + const defaultSelectedAttribute = page + .getByRole( 'row', { + name: attribute.name, + } ) + .locator( 'option[selected]' ); + + await expect( defaultSelectedAttribute ).toHaveText( + attribute.option + ); + } + ); + } + } + ); + } ); + + test( 'can remove a variation', async ( { page } ) => { + await test.step( 'Go to the "Edit product" page.', async () => { + await page.goto( + `/wp-admin/post.php?post=${ productId_removeVariation }&action=edit` + ); + } ); + + await test.step( 'Click on the "Variations" tab.', async () => { + await page.locator( 'a[href="#variable_product_options"]' ).click(); + } ); + + await test.step( 'Click "Remove" on a variation', async () => { + page.on( 'dialog', ( dialog ) => dialog.accept() ); + await page.locator( '.woocommerce_variation' ).hover(); + await page.locator( '.remove_variation.delete' ).click(); + } ); + + await test.step( 'Expect the variation to be removed', async () => { + await expect( + page.locator( '.woocommerce_variation' ) + ).toHaveCount( 0 ); + } ); + } ); +} );