From 70737dcc1e407529c5224792b9c317ec0d23813e Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Fri, 24 Jan 2025 20:36:07 -0300 Subject: [PATCH 1/5] Move the unblock ui helper to the generic helpers file --- tests/e2e-pw/utils/helpers.ts | 6 +++++- tests/e2e-pw/utils/shopper-navigation.ts | 3 ++- tests/e2e-pw/utils/shopper.ts | 5 +---- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/e2e-pw/utils/helpers.ts b/tests/e2e-pw/utils/helpers.ts index b0cb3d0d195..dee9669be20 100644 --- a/tests/e2e-pw/utils/helpers.ts +++ b/tests/e2e-pw/utils/helpers.ts @@ -2,7 +2,7 @@ * External dependencies */ import path from 'path'; -import { test, Page, Browser, BrowserContext } from '@playwright/test'; +import { test, expect, Page, Browser, BrowserContext } from '@playwright/test'; export const merchantStorageFile = path.resolve( __dirname, @@ -99,3 +99,7 @@ export const getAnonymousShopper = async ( */ export const describeif = ( condition: boolean ) => condition ? test.describe : test.describe.skip; + +export const isUIUnblocked = async ( page: Page ) => { + await expect( page.locator( '.blockUI' ) ).toHaveCount( 0 ); +}; diff --git a/tests/e2e-pw/utils/shopper-navigation.ts b/tests/e2e-pw/utils/shopper-navigation.ts index db9a607e4c5..3bd686495fc 100644 --- a/tests/e2e-pw/utils/shopper-navigation.ts +++ b/tests/e2e-pw/utils/shopper-navigation.ts @@ -2,10 +2,11 @@ * External dependencies */ import { Page } from 'playwright/test'; + /** * Internal dependencies */ -import { isUIUnblocked } from './shopper'; +import { isUIUnblocked } from './helpers'; export const goToShop = async ( page: Page, pageNumber?: number ) => { if ( pageNumber ) { diff --git a/tests/e2e-pw/utils/shopper.ts b/tests/e2e-pw/utils/shopper.ts index ca3ceb08ede..e642608628c 100644 --- a/tests/e2e-pw/utils/shopper.ts +++ b/tests/e2e-pw/utils/shopper.ts @@ -7,10 +7,7 @@ import { Page, expect } from 'playwright/test'; */ import * as navigation from './shopper-navigation'; import { config, CustomerAddress } from '../config/default'; - -export const isUIUnblocked = async ( page: Page ) => { - await expect( page.locator( '.blockUI' ) ).toHaveCount( 0 ); -}; +import { isUIUnblocked } from './helpers'; /** * Waits for the UI to refresh after a user interaction. From b0aca884e38348ec9d1cc37ec0b750c4b7f18ffa Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Fri, 24 Jan 2025 20:37:19 -0300 Subject: [PATCH 2/5] Convert the Merchant Orders Status Change spec to Playwright --- .../merchant-orders-status-change.spec.ts | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts b/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts new file mode 100644 index 00000000000..2138b37db40 --- /dev/null +++ b/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts @@ -0,0 +1,177 @@ +/** + * External dependencies + */ +import { test, expect, Page } from '@playwright/test'; + +/** + * Internal dependencies + */ +import { getMerchant, getShopper, isUIUnblocked } from '../../utils/helpers'; +import { placeOrderWithOptions } from '../../utils/shopper'; +import * as navigation from '../../utils/merchant-navigation'; + +const orderStatusDropdownSelector = 'select[name="order_status"]'; +const cancelModalSelector = 'div.wcpay-confirmation-modal'; +const refundModalSelector = 'div.refund-confirmation-modal'; +const refundCancelSelector = + '.refund-confirmation-modal .wcpay-confirmation-modal__footer .is-secondary'; +const refundConfirmSelector = + '.refund-confirmation-modal .wcpay-confirmation-modal__footer .is-primary'; +const selectedOrderStatusSelector = '.wc-order-status > span'; +const orderPriceSelector = + '#woocommerce-order-items .total .woocommerce-Price-amount'; + +const saveOrder = async ( page: Page ) => { + await page.locator( '.save_order' ).click(); + await page.waitForLoadState( 'networkidle' ); +}; + +const verifyOrderStatus = async ( page: Page, status: string ) => { + const selectedOrderStatus = await page.$( selectedOrderStatusSelector ); + await expect( + selectedOrderStatus.evaluate( ( el ) => el.textContent ) + ).resolves.toBe( status ); +}; + +test.describe( 'Order > Status Change', () => { + let merchantPage: Page; + let shopperPage: Page; + let orderId: string; + + test.beforeAll( async ( { browser } ) => { + merchantPage = ( await getMerchant( browser ) ).merchantPage; + shopperPage = ( await getShopper( browser ) ).shopperPage; + } ); + + test.describe( 'Change Status of order to Cancelled', () => { + test.beforeAll( async () => { + orderId = await placeOrderWithOptions( shopperPage ); + await navigation.goToOrder( merchantPage, orderId ); + } ); + + test( 'Show Cancel Confirmation modal, do not change status if Do Nothing selected', async () => { + // Select cancel from the order status dropdown. + await merchantPage.selectOption( + orderStatusDropdownSelector, + 'Cancelled' + ); + + // Verify the confirmation modal shows. + await merchantPage.waitForSelector( cancelModalSelector, { + state: 'visible', + } ); + + // Click on Do Nothing. + await merchantPage + .getByRole( 'button', { name: 'Do Nothing' } ) + .click(); + + // Verify the order status is set to processing. + await verifyOrderStatus( merchantPage, 'Processing' ); + + // Click on the update order button and wait for page reload. + await saveOrder( merchantPage ); + + // Verify the order status is set to processing. + await verifyOrderStatus( merchantPage, 'Processing' ); + } ); + + test( 'When Order Status changed to Cancel, show Cancel Confirmation modal, change status to Cancel if confirmed', async () => { + // Select cancel from the order status dropdown. + await merchantPage.selectOption( + orderStatusDropdownSelector, + 'Cancelled' + ); + + // Verify the confirmation modal shows. + await merchantPage.waitForSelector( cancelModalSelector, { + state: 'visible', + } ); + + // Click on Cancel order. + await merchantPage + .getByRole( 'button', { name: 'Cancel order' } ) + .click(); + await merchantPage.waitForLoadState( 'networkidle' ); + + // Verify the order status is set to cancel. + await verifyOrderStatus( merchantPage, 'Cancelled' ); + + // Click on the update order button and wait for page reload. + await saveOrder( merchantPage ); + + // Verify the order status is set to cancelled. + await verifyOrderStatus( merchantPage, 'Cancelled' ); + } ); + } ); + + test.describe( 'Change Status of order to Refunded', () => { + test.beforeAll( async () => { + orderId = await placeOrderWithOptions( shopperPage ); + await navigation.goToOrder( merchantPage, orderId ); + } ); + + test( 'Show Refund Confirmation modal, do not change status if Cancel clicked', async () => { + // Select refunded from the order status dropdown. + await merchantPage.selectOption( + orderStatusDropdownSelector, + 'Refunded' + ); + + // Verify the confirmation modal shows. + await merchantPage.waitForSelector( refundModalSelector, { + state: 'visible', + } ); + + // Click on Cancel. + await merchantPage.locator( refundCancelSelector ).click(); + + // Verify the order status is set to processing. + await verifyOrderStatus( merchantPage, 'Processing' ); + + // Click on the update order button and wait for page reload. + await saveOrder( merchantPage ); + + // Verify the order status is set to processing. + await verifyOrderStatus( merchantPage, 'Processing' ); + } ); + + test( 'Show Refund Confirmation modal, process Refund if confirmed', async () => { + // Select refunded from the order status dropdown. + await merchantPage.selectOption( + orderStatusDropdownSelector, + 'Refunded' + ); + + // Verify the confirmation modal shows. + await merchantPage.waitForSelector( refundModalSelector, { + state: 'visible', + } ); + + // Click on Refund order. + await merchantPage.locator( refundConfirmSelector ).click(); + + // Wait for refund to be processed + await isUIUnblocked( merchantPage ); + await merchantPage.waitForLoadState( 'networkidle' ); + + // Get the order price + const priceElement = await merchantPage.$( orderPriceSelector ); + const orderAmount = await merchantPage.evaluate( + ( el ) => el.textContent, + priceElement + ); + + // Verify the refund amount is equal to the order amount. + await expect( + merchantPage.locator( '.refund > .line_cost' ) + ).toHaveText( `-${ orderAmount }` ); + + // Click on the update order button and wait for page reload. + await saveOrder( merchantPage ); + + // Verify the order status is set to refunded. + await verifyOrderStatus( merchantPage, 'Refunded' ); + } ); + } ); +} ); From 5aabedc77e61d9113a0747828bd70d1fd5513427 Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Fri, 24 Jan 2025 20:37:54 -0300 Subject: [PATCH 3/5] Remove the Merchant Orders Status Change spec --- .../merchant-orders-status-change.spec.js | 238 ------------------ 1 file changed, 238 deletions(-) delete mode 100644 tests/e2e/specs/wcpay/merchant/merchant-orders-status-change.spec.js diff --git a/tests/e2e/specs/wcpay/merchant/merchant-orders-status-change.spec.js b/tests/e2e/specs/wcpay/merchant/merchant-orders-status-change.spec.js deleted file mode 100644 index 37c93316982..00000000000 --- a/tests/e2e/specs/wcpay/merchant/merchant-orders-status-change.spec.js +++ /dev/null @@ -1,238 +0,0 @@ -/** - * External dependencies - */ -import config from 'config'; -/** - * Internal dependencies - */ -import { fillCardDetails, setupProductCheckout } from '../../../utils/payments'; - -const { merchant, shopper, uiUnblocked } = require( '@woocommerce/e2e-utils' ); - -const orderIdSelector = '.woocommerce-order-overview__order.order > strong'; -const orderStatusDropdownSelector = 'select[name="order_status"]'; -const cancelModalSelector = 'div.wcpay-confirmation-modal'; -const refundModalSelector = 'div.refund-confirmation-modal'; -const refundCancelSelector = - '.refund-confirmation-modal .wcpay-confirmation-modal__footer .is-secondary'; -const refundConfirmSelector = - '.refund-confirmation-modal .wcpay-confirmation-modal__footer .is-primary'; -const selectedOrderStatusSelector = '.wc-order-status > span'; - -let orderId; - -describe( 'Order > Status Change', () => { - describe( 'Change Status of order to Cancelled', () => { - beforeAll( async () => { - // Place an order to change its status later - await setupProductCheckout( - config.get( 'addresses.customer.billing' ) - ); - const card = config.get( 'cards.basic' ); - await fillCardDetails( page, card ); - await shopper.placeOrder(); - await expect( page ).toMatchTextContent( 'Order received' ); - - // Get the order ID so we can open it in the merchant view - const orderIdField = await page.$( orderIdSelector ); - orderId = await orderIdField.evaluate( ( el ) => el.innerText ); - - // Login and open the order - await merchant.login(); - await merchant.goToOrder( orderId ); - } ); - - afterAll( async () => { - await merchant.logout(); - } ); - - it( 'Show Cancel Confirmation modal, do not change status if Do Nothing selected', async () => { - // Select cancel from the order status dropdown. - await expect( page ).toSelect( - orderStatusDropdownSelector, - 'Cancelled' - ); - - // Verify the confirmation modal shows. - await page.waitForSelector( cancelModalSelector, { - visible: true, - } ); - - // Click on Do Nothing. - await expect( page ).toClick( 'button', { text: 'Do Nothing' } ); - - // Verify the order status is set to processing. - const selectedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - selectedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Processing' ); - - //click Update Order button and wait for page reloaded. - await expect( page ).toClick( '.save_order' ); - await page.waitForNavigation( { - waitUntil: 'networkidle0', - } ); - - // Verify the order status is set to processing. - const updatedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - updatedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Processing' ); - } ); - - it( 'When Order Status changed to Cancel, show Cancel Confirmation modal, change status to Cancel if confirmed', async () => { - // Select cancel from the order status dropdown. - await expect( page ).toSelect( - orderStatusDropdownSelector, - 'Cancelled' - ); - - // Verify the confirmation modal shows. - await page.waitForSelector( cancelModalSelector, { - visible: true, - } ); - - // Click on Cancel order. - await expect( page ).toClick( 'button', { text: 'Cancel order' } ); - - await page.waitForNavigation( { waitUntil: 'networkidle0' } ); - - // Verify the order status is set to cancel. - const selectedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - selectedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Cancelled' ); - - //click Update Order button. - await expect( page ).toClick( '.save_order' ); - await page.waitForNavigation( { - waitUntil: 'networkidle0', - } ); - - // Verify the order status is set to processing. - const updatedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - updatedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Cancelled' ); - } ); - } ); - - describe( 'Change Status of order to Refunded', () => { - beforeAll( async () => { - // Place an order to change its status later - await setupProductCheckout( - config.get( 'addresses.customer.billing' ) - ); - const card = config.get( 'cards.basic' ); - await fillCardDetails( page, card ); - await shopper.placeOrder(); - await expect( page ).toMatchTextContent( 'Order received' ); - - // Get the order ID so we can open it in the merchant view - const orderIdField = await page.$( orderIdSelector ); - orderId = await orderIdField.evaluate( ( el ) => el.innerText ); - - // Login and open the order - await merchant.login(); - await merchant.goToOrder( orderId ); - } ); - - afterAll( async () => { - await merchant.logout(); - } ); - - it( 'Show Refund Confirmation modal, do not change status if Cancel clicked', async () => { - // Select refunded from the order status dropdown. - await expect( page ).toSelect( - orderStatusDropdownSelector, - 'Refunded' - ); - - // Verify the confirmation modal shows. - await page.waitForSelector( refundModalSelector, { - visible: true, - } ); - - // Click on Cancel. - await expect( page ).toClick( refundCancelSelector ); - - // Verify the order status is set to processing. - const selectedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - selectedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Processing' ); - - //click Update Order button and wait for page reloaded. - await expect( page ).toClick( '.save_order' ); - await page.waitForNavigation( { - waitUntil: 'networkidle0', - } ); - - // Verify the order status is set to processing. - const updatedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - updatedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Processing' ); - } ); - - it( 'Show Refund Confirmation modal, process Refund if confirmed', async () => { - // Select refunded from the order status dropdown. - await expect( page ).toSelect( - orderStatusDropdownSelector, - 'Refunded' - ); - - // Verify the confirmation modal shows. - await page.waitForSelector( refundModalSelector, { - visible: true, - } ); - - // Click on Refund order. - await expect( page ).toClick( refundConfirmSelector ); - - // Wait for refund to be processed - await uiUnblocked(); - await page.waitForNavigation( { waitUntil: 'networkidle0' } ); - - // Get the order price - const priceElement = await page.$( - '#woocommerce-order-items .total .woocommerce-Price-amount' - ); - const orderAmount = await page.evaluate( - ( el ) => el.textContent, - priceElement - ); - - //Verify the refund amount is equal to the order amount. - expect( page ).toMatchElement( '.refund > .line_cost', { - text: `-${ orderAmount }`, - } ); - - //click Update Order button. - await expect( page ).toClick( '.save_order' ); - await page.waitForNavigation( { - waitUntil: 'networkidle0', - } ); - - // Verify the order status is set to refunded. - const updatedOrderStatus = await page.$( - selectedOrderStatusSelector - ); - await expect( - updatedOrderStatus.evaluate( ( el ) => el.innerText ) - ).resolves.toBe( 'Refunded' ); - } ); - } ); -} ); From 196362146f1148101d5e0512450223ae31873739 Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Fri, 24 Jan 2025 20:39:26 -0300 Subject: [PATCH 4/5] Add changelog entry --- changelog/dev-9965-order-status-change-spec | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 changelog/dev-9965-order-status-change-spec diff --git a/changelog/dev-9965-order-status-change-spec b/changelog/dev-9965-order-status-change-spec new file mode 100644 index 00000000000..c84f2787e9c --- /dev/null +++ b/changelog/dev-9965-order-status-change-spec @@ -0,0 +1,4 @@ +Significance: patch +Type: dev + +Convert the merchant orders status change spec from Puppeteer to Playwright. From 70add24e7afa9e4368f0ec210b4d9acf63c62c0f Mon Sep 17 00:00:00 2001 From: Eduardo Umpierre Date: Mon, 27 Jan 2025 20:38:15 -0300 Subject: [PATCH 5/5] Replace waitForSelector with locator.waitFor --- .../merchant-orders-status-change.spec.ts | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts b/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts index 2138b37db40..a1f2da82ccb 100644 --- a/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts +++ b/tests/e2e-pw/specs/merchant/merchant-orders-status-change.spec.ts @@ -57,9 +57,9 @@ test.describe( 'Order > Status Change', () => { ); // Verify the confirmation modal shows. - await merchantPage.waitForSelector( cancelModalSelector, { - state: 'visible', - } ); + await merchantPage + .locator( cancelModalSelector ) + .waitFor( { state: 'visible' } ); // Click on Do Nothing. await merchantPage @@ -84,9 +84,9 @@ test.describe( 'Order > Status Change', () => { ); // Verify the confirmation modal shows. - await merchantPage.waitForSelector( cancelModalSelector, { - state: 'visible', - } ); + await merchantPage + .locator( cancelModalSelector ) + .waitFor( { state: 'visible' } ); // Click on Cancel order. await merchantPage @@ -119,9 +119,9 @@ test.describe( 'Order > Status Change', () => { ); // Verify the confirmation modal shows. - await merchantPage.waitForSelector( refundModalSelector, { - state: 'visible', - } ); + await merchantPage + .locator( refundModalSelector ) + .waitFor( { state: 'visible' } ); // Click on Cancel. await merchantPage.locator( refundCancelSelector ).click(); @@ -144,9 +144,9 @@ test.describe( 'Order > Status Change', () => { ); // Verify the confirmation modal shows. - await merchantPage.waitForSelector( refundModalSelector, { - state: 'visible', - } ); + await merchantPage + .locator( refundModalSelector ) + .waitFor( { state: 'visible' } ); // Click on Refund order. await merchantPage.locator( refundConfirmSelector ).click();