From f4f4d798fe15b1bd5efaaeb3ffb291f12b07ff64 Mon Sep 17 00:00:00 2001 From: Adrian Moldovan <3854374+adimoldovan@users.noreply.github.com> Date: Wed, 21 Feb 2024 00:25:16 +0200 Subject: [PATCH] [e2e tests] Add tests for product inventory in product block editor (#44699) * Remove describe blocks and duplicated page fixture * Add test `can update sku` * Add changelog * Finish test 'can update sku' * Add test 'can update stock status' * Add test 'can track stock quantity' * Add test 'can limit purchases' * Remove extra whitespace * Disable product editor tour * Revert timeout update --- .../e2e-product-inventory-block-editor | 4 + plugins/woocommerce/tests/e2e-pw/fixtures.js | 10 + .../block-editor/block-editor-fixtures.js | 9 +- .../product-edit-block-editor.spec.js | 178 ++++---- .../product-images-block-editor.spec.js | 415 +++++++++--------- .../product-inventory-block-editor.spec.js | 210 +++++++++ 6 files changed, 516 insertions(+), 310 deletions(-) create mode 100644 plugins/woocommerce/changelog/e2e-product-inventory-block-editor create mode 100644 plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-inventory-block-editor.spec.js diff --git a/plugins/woocommerce/changelog/e2e-product-inventory-block-editor b/plugins/woocommerce/changelog/e2e-product-inventory-block-editor new file mode 100644 index 00000000000..6e33ca8e2a0 --- /dev/null +++ b/plugins/woocommerce/changelog/e2e-product-inventory-block-editor @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +E2E tests: add tests for product inventory in product block editor diff --git a/plugins/woocommerce/tests/e2e-pw/fixtures.js b/plugins/woocommerce/tests/e2e-pw/fixtures.js index ebd2457a42c..64d2265c338 100644 --- a/plugins/woocommerce/tests/e2e-pw/fixtures.js +++ b/plugins/woocommerce/tests/e2e-pw/fixtures.js @@ -18,5 +18,15 @@ exports.test = base.test.extend( { await use( api ); }, + wcAdminApi: async ( { baseURL }, use ) => { + const wcAdminApi = new wcApi( { + url: baseURL, + consumerKey: process.env.CONSUMER_KEY, + consumerSecret: process.env.CONSUMER_SECRET, + version: 'wc-admin', // Use wc-admin namespace + } ); + + await use( wcAdminApi ); + }, } ); exports.expect = base.expect; diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/block-editor-fixtures.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/block-editor-fixtures.js index bbdd8a7a55a..01a59b226c3 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/block-editor-fixtures.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/block-editor-fixtures.js @@ -1,7 +1,8 @@ const { test } = require( '../../../../fixtures' ); exports.test = test.extend( { - page: async ( { page, api }, use ) => { + page: async ( { page, api, wcAdminApi }, use ) => { + // Enable product block editor await api.put( 'settings/advanced/woocommerce_feature_product_block_editor_enabled', { @@ -9,8 +10,14 @@ exports.test = test.extend( { } ); + // Disable the product editor tour + await wcAdminApi.post( 'options', { + woocommerce_block_product_tour_shown: 'yes', + } ); + await use( page ); + // Disable product block editor await api.put( 'settings/advanced/woocommerce_feature_product_block_editor_enabled', { diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-edit-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-edit-block-editor.spec.js index b80bdd9e0c1..4573e0c0ef5 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-edit-block-editor.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-edit-block-editor.spec.js @@ -1,118 +1,100 @@ const { test: baseTest } = require( './block-editor-fixtures' ); const { expect } = require( '../../../../fixtures' ); -baseTest.describe( 'Products > Edit Product', () => { - const test = baseTest.extend( { - product: async ( { api }, use ) => { - let product; - await api - .post( 'products', { - id: 0, - name: `Product ${ Date.now() }`, - type: 'simple', - description: `This product is a longer 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; - } ); +const test = baseTest.extend( { + product: async ( { api }, use ) => { + let product; - await use( product ); + await api + .post( 'products', { + id: 0, + name: `Product ${ Date.now() }`, + type: 'simple', + description: `This product is a longer 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; + } ); - // Cleanup - 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( product ); - await use( page ); + // Cleanup + await api.delete( `products/${ product.id }`, { force: true } ); + }, +} ); - await api.put( - 'settings/advanced/woocommerce_feature_product_block_editor_enabled', - { - value: 'no', - } - ); - }, +test( 'can update the general information of a product', async ( { + page, + product, +} ) => { + await page.goto( `wp-admin/post.php?post=${ product.id }&action=edit` ); + + const updatedProduct = { + name: `Product ${ Date.now() }`, + description: `Updated description for the awesome product ${ Date.now() }`, + short_description: `Updated summary for the awesome product ${ Date.now() }`, + regularPrice: '100.05', + salePrice: '99.05', + }; + + const nameTextbox = page.getByLabel( 'Name' ).getByRole( 'textbox' ); + const summaryTextbox = page + .getByLabel( 'Block: Product textarea block' ) + .getByRole( 'textbox' ); + const descriptionTextbox = page + .getByLabel( 'Block: Product description' ) + .getByRole( 'textbox' ); + const listPriceTextbox = page.getByRole( 'textbox', { + name: 'List price', + } ); + const salePriceTextbox = page.getByRole( 'textbox', { + name: 'Sale price', } ); - test( 'can update the general information of a product', async ( { - page, - product, - } ) => { - await page.goto( `wp-admin/post.php?post=${ product.id }&action=edit` ); + await test.step( 'edit the product name', async () => { + await nameTextbox.fill( updatedProduct.name ); + } ); - const updatedProduct = { - name: `Product ${ Date.now() }`, - description: `Updated description for the awesome product ${ Date.now() }`, - short_description: `Updated summary for the awesome product ${ Date.now() }`, - regularPrice: '100.05', - salePrice: '99.05', - }; + await test.step( 'edit the product price', async () => { + await listPriceTextbox.fill( updatedProduct.regularPrice ); + await salePriceTextbox.fill( updatedProduct.salePrice ); + } ); - const nameTextbox = page.getByLabel( 'Name' ).getByRole( 'textbox' ); - const summaryTextbox = page - .getByLabel( 'Block: Product textarea block' ) - .getByRole( 'textbox' ); - const descriptionTextbox = page - .getByLabel( 'Block: Product description' ) - .getByRole( 'textbox' ); - const listPriceTextbox = page.getByRole( 'textbox', { - name: 'List price', - } ); - const salePriceTextbox = page.getByRole( 'textbox', { - name: 'Sale price', - } ); + await test.step( 'edit the product description and summary', async () => { + // Need to clear the textbox before filling it, otherwise the text will be appended. + await descriptionTextbox.clear(); + await descriptionTextbox.fill( updatedProduct.description ); - await test.step( 'edit the product name', async () => { - await nameTextbox.fill( updatedProduct.name ); - } ); + await summaryTextbox.clear(); + await summaryTextbox.fill( updatedProduct.short_description ); + } ); - await test.step( 'edit the product price', async () => { - await listPriceTextbox.fill( updatedProduct.regularPrice ); - await salePriceTextbox.fill( updatedProduct.salePrice ); - } ); + await test.step( 'publish the updated product', async () => { + await page.getByRole( 'button', { name: 'Update' } ).click(); - await test.step( 'edit the product description and summary', async () => { - // Need to clear the textbox before filling it, otherwise the text will be appended. - await descriptionTextbox.clear(); - await descriptionTextbox.fill( updatedProduct.description ); + await expect( page.getByLabel( 'Dismiss this notice' ) ).toContainText( + 'Product updated' + ); + } ); - await summaryTextbox.clear(); - await summaryTextbox.fill( updatedProduct.short_description ); - } ); + await test.step( 'verify the changes', async () => { + await expect.soft( nameTextbox ).toHaveValue( updatedProduct.name ); - await test.step( 'publish the updated product', async () => { - await page.getByRole( 'button', { name: 'Update' } ).click(); + await expect + .soft( summaryTextbox ) + .toHaveText( updatedProduct.short_description ); - await expect( - page.getByLabel( 'Dismiss this notice' ) - ).toContainText( 'Product updated' ); - } ); + await expect + .soft( descriptionTextbox ) + .toHaveText( updatedProduct.description ); - await test.step( 'verify the changes', async () => { - await expect.soft( nameTextbox ).toHaveValue( updatedProduct.name ); - - await expect - .soft( summaryTextbox ) - .toHaveText( updatedProduct.short_description ); - - await expect - .soft( descriptionTextbox ) - .toHaveText( updatedProduct.description ); - - await expect - .soft( listPriceTextbox ) - .toHaveValue( updatedProduct.regularPrice ); - await expect - .soft( salePriceTextbox ) - .toHaveValue( updatedProduct.salePrice ); - } ); + await expect + .soft( listPriceTextbox ) + .toHaveValue( updatedProduct.regularPrice ); + await expect + .soft( salePriceTextbox ) + .toHaveValue( updatedProduct.salePrice ); } ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js index c35a28fa5e1..5457a230330 100644 --- a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-images-block-editor.spec.js @@ -23,230 +23,223 @@ async function selectImagesInLibrary( page, imagesNames ) { return dataIds; } -baseTest.describe( 'Products > Edit Product', () => { - const test = baseTest.extend( { - product: async ( { api }, use ) => { - let 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 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 ); + 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; - } ); + // 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 ); - }, + 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` ); } ); - test( 'can add images', async ( { page, product } ) => { - const images = [ 'image-01', 'image-02' ]; + await test.step( 'add images', async () => { + await page.getByText( 'Choose an image' ).click(); + const dataIds = await selectImagesInLibrary( page, images ); - 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' ) - ).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(); - } - } ); + page + .getByLabel( 'Block: Product images' ) + .locator( `img[id="${ dataId }"]` ) + ).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(); - } ); + 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' + ); } ); - 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( 'verify product image was set', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); - 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 ); - } ); + 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 ); } ); } ); diff --git a/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-inventory-block-editor.spec.js b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-inventory-block-editor.spec.js new file mode 100644 index 00000000000..1275f39af45 --- /dev/null +++ b/plugins/woocommerce/tests/e2e-pw/tests/merchant/products/block-editor/product-inventory-block-editor.spec.js @@ -0,0 +1,210 @@ +const { test: baseTest } = require( './block-editor-fixtures' ); +const { expect } = require( '../../../../fixtures' ); + +const test = baseTest.extend( { + product: async ( { api }, use ) => { + let product; + + await api + .post( 'products', { + name: `Product ${ Date.now() }`, + type: 'simple', + regular_price: '12.99', + stock_status: 'instock', + } ) + .then( ( response ) => { + product = response.data; + } ); + + await use( product ); + + // Cleanup + await api.delete( `products/${ product.id }`, { force: true } ); + }, + page: async ( { page, product }, use ) => { + await test.step( 'go to product editor, inventory tab', async () => { + await page.goto( + `wp-admin/post.php?post=${ product.id }&action=edit` + ); + await page.getByRole( 'button', { name: 'Inventory' } ).click(); + } ); + + await use( page ); + }, +} ); + +test( 'can update sku', async ( { page, product } ) => { + const sku = `SKU_${ Date.now() }`; + + await test.step( 'update the sku value', async () => { + await page.locator( '[name="woocommerce-product-sku"]' ).fill( sku ); + } ); + + 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 the change in product editor', async () => { + await expect( + page.locator( '[name="woocommerce-product-sku"]' ) + ).toHaveValue( sku ); + } ); + + await test.step( 'verify the changes in the store frontend', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); + + await expect( page.getByText( `SKU: ${ sku }` ) ).toBeVisible(); + } ); +} ); + +test( 'can update stock status', async ( { page, product } ) => { + await test.step( 'update the sku value', async () => { + await page.getByLabel( 'Out of stock' ).check(); + } ); + + 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 the change in product editor', async () => { + await expect( page.getByLabel( 'Out of stock' ) ).toBeChecked(); + } ); + + await test.step( 'verify the changes in the store frontend', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); + + await expect( page.getByText( 'Out of stock' ) ).toBeVisible(); + } ); +} ); + +test( 'can track stock quantity', async ( { page, product } ) => { + await test.step( 'enable track stock quantity', async () => { + await page.getByLabel( 'Track stock quantity for this' ).check(); + // await closeTourModal( { page, timeout: 2000 } ); + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await page.getByLabel( "Don't allow purchases" ).check(); + } ); + + const quantity = '1'; + + await test.step( 'update available quantity', async () => { + await page.locator( '[name="stock_quantity"]' ).fill( quantity ); + } ); + + 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 the change in product editor', async () => { + await expect( page.locator( '[name="stock_quantity"]' ) ).toHaveValue( + quantity + ); + } ); + + await test.step( 'verify the changes in the store frontend', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); + + await expect( + page.getByText( `${ quantity } in stock` ) + ).toBeVisible(); + } ); + + await test.step( 'return to product editor', async () => { + await page.goto( `wp-admin/post.php?post=${ product.id }&action=edit` ); + await page.getByRole( 'button', { name: 'Inventory' } ).click(); + } ); + + await test.step( 'update available quantity', async () => { + await page.locator( '[name="stock_quantity"]' ).fill( '0' ); + } ); + + 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 the change in product editor', async () => { + await expect( page.locator( '[name="stock_quantity"]' ) ).toHaveValue( + '0' + ); + } ); + + await test.step( 'verify the changes in the store frontend', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); + + await expect( page.getByText( 'Out of stock' ) ).toBeVisible(); + } ); +} ); + +test( 'can limit purchases', async ( { page, product } ) => { + await test.step( 'ensure limit purchases is disabled', async () => { + // await closeTourModal( { page, timeout: 2000 } ); + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await expect( + page.getByLabel( 'Limit purchases to 1 item per order' ) + ).not.toBeChecked(); + } ); + + await test.step( 'add 2 items to cart', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); + + await page.getByLabel( 'Product quantity' ).fill( '2' ); + await page.getByRole( 'button', { name: 'Add to cart' } ).click(); + await expect( + page.getByText( + `2 × “${ product.name }” have been added to your cart.` + ) + ).toBeVisible(); + } ); + + await test.step( 'return to product editor', async () => { + await page.goto( `wp-admin/post.php?post=${ product.id }&action=edit` ); + await page.getByRole( 'button', { name: 'Inventory' } ).click(); + } ); + + await test.step( 'enable limit purchases', async () => { + await page.getByRole( 'button', { name: 'Advanced' } ).click(); + await page.getByLabel( 'Limit purchases to 1 item per order' ).check(); + } ); + + 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 you cannot order more than 1 item', async () => { + // Verify image in store frontend + await page.goto( product.permalink ); + + await page.getByRole( 'button', { name: 'Add to cart' } ).click(); + await page.getByRole( 'button', { name: 'Add to cart' } ).click(); + await expect( + page.getByText( + `You cannot add another "${ product.name }" to your cart.` + ) + ).toBeVisible(); + } ); +} );