diff --git a/.gitignore b/.gitignore index a4e4804293b..6b905dd43dd 100644 --- a/.gitignore +++ b/.gitignore @@ -47,6 +47,7 @@ tests/cli/vendor **/e2e/artifacts/ /artifacts/ /playwright-report/ +**/e2e/.auth # Logs /logs diff --git a/tests/e2e-jest/specs/shopper/cart-checkout/checkout.test.js b/tests/e2e-jest/specs/shopper/cart-checkout/checkout.test.js index daec97e8a3b..a715417b335 100644 --- a/tests/e2e-jest/specs/shopper/cart-checkout/checkout.test.js +++ b/tests/e2e-jest/specs/shopper/cart-checkout/checkout.test.js @@ -333,31 +333,6 @@ describe( 'Shopper → Checkout', () => { } ); } ); - describe( 'Place Order', () => { - it( 'Guest user can place order', async () => { - if ( await shopper.isLoggedIn() ) { - await shopper.logout(); - } - await shopper.block.goToShop(); - await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); - await shopper.block.goToCheckout(); - await shopper.block.fillBillingDetails( BILLING_DETAILS ); - await shopper.block.placeOrder(); - await expect( page ).toMatch( 'Your order has been received.' ); - } ); - - it( 'Logged in user can place an order', async () => { - await shopper.login(); - await shopper.block.goToShop(); - await shopper.addToCartFromShopPage( SIMPLE_VIRTUAL_PRODUCT_NAME ); - await shopper.block.goToCheckout(); - await shopper.block.fillBillingDetails( BILLING_DETAILS ); - await shopper.block.placeOrder(); - await expect( page ).toMatch( 'Your order has been received.' ); - await shopper.logout(); - } ); - } ); - describe.skip( `Shipping`, () => { afterEach( async () => { await merchant.login(); diff --git a/tests/e2e/global-setup.ts b/tests/e2e/global-setup.ts index 08fc5661558..ec4173706f9 100644 --- a/tests/e2e/global-setup.ts +++ b/tests/e2e/global-setup.ts @@ -4,8 +4,14 @@ */ import { FullConfig, chromium, request } from '@playwright/test'; import { RequestUtils } from '@wordpress/e2e-test-utils-playwright'; +import { expect } from '@woocommerce/e2e-playwright-utils'; import fs from 'fs'; -import { cli } from '@woocommerce/e2e-utils'; +import { + cli, + adminFile, + customerFile, + guestFile, +} from '@woocommerce/e2e-utils'; /** * Internal dependencies @@ -79,6 +85,94 @@ const loginAsCustomer = async ( config: FullConfig ) => { await browser.close(); }; +const authenticateAsAdmin = async ( config: FullConfig ) => { + const { baseURL, userAgent } = config.projects[ 0 ].use; + + // Specify user agent when running against an external test site to avoid getting HTTP 406 NOT ACCEPTABLE errors. + const contextOptions = { baseURL, userAgent }; + // Create browser, browserContext, and page for admin users + const browser = await chromium.launch(); + const context = await browser.newContext( contextOptions ); + const page = await context.newPage(); + await page.goto( '/my-account' ); + await page.getByLabel( 'Username or email address' ).fill( admin.username ); + await page.getByLabel( 'Password' ).fill( admin.password ); + await page.getByRole( 'button', { name: 'Log in' } ).click(); + // Sometimes login flow sets cookies in the process of several redirects. + // Wait for the final URL to ensure that the cookies are actually set. + await page.waitForURL( '/my-account/' ); + + await expect( + page + .getByRole( 'list' ) + .filter( { + hasText: + 'Dashboard Orders Downloads Addresses Account details Log out', + } ) + .getByRole( 'link', { name: 'Log out' } ) + ).toBeVisible(); + + await page.context().storageState( { path: adminFile } ); + + await context.close(); + await browser.close(); +}; + +const authenticateAsCustomer = async ( config: FullConfig ) => { + const { baseURL, userAgent } = config.projects[ 0 ].use; + + // Specify user agent when running against an external test site to avoid getting HTTP 406 NOT ACCEPTABLE errors. + const contextOptions = { baseURL, userAgent }; + // Create browser, browserContext, and page for customer users + const browser = await chromium.launch(); + const context = await browser.newContext( contextOptions ); + const page = await context.newPage(); + await page.goto( '/my-account' ); + await page + .getByLabel( 'Username or email address' ) + .fill( customer.username ); + await page.getByLabel( 'Password' ).fill( customer.password ); + await page.getByRole( 'button', { name: 'Log in' } ).click(); + // Sometimes login flow sets cookies in the process of several redirects. + // Wait for the final URL to ensure that the cookies are actually set. + await page.waitForURL( '/my-account/' ); + + await expect( + page + .getByRole( 'list' ) + .filter( { + hasText: + 'Dashboard Orders Downloads Addresses Account details Log out', + } ) + .getByRole( 'link', { name: 'Log out' } ) + ).toBeVisible(); + + await page.context().storageState( { path: customerFile } ); + + await context.close(); + await browser.close(); +}; + +const visitAsGuest = async ( config: FullConfig ) => { + const { baseURL, userAgent } = config.projects[ 0 ].use; + + // Specify user agent when running against an external test site to avoid getting HTTP 406 NOT ACCEPTABLE errors. + const contextOptions = { baseURL, userAgent }; + // Create browser, browserContext, and page for customer and admin users + const browser = await chromium.launch(); + const context = await browser.newContext( contextOptions ); + const page = await context.newPage(); + await page.goto( '/my-account' ); + await expect( + page.getByLabel( 'Username or email address' ) + ).toBeVisible(); + + await page.context().storageState( { path: guestFile } ); + + await context.close(); + await browser.close(); +}; + const prepareAttributes = async ( config: FullConfig ) => { const { baseURL, userAgent } = config.projects[ 0 ].use; @@ -145,6 +239,9 @@ async function globalSetup( config: FullConfig ) { await prepareAttributes( config ); await loginAsCustomer( config ); + await authenticateAsAdmin( config ); + await authenticateAsCustomer( config ); + await visitAsGuest( config ); } export default globalSetup; diff --git a/tests/e2e/tests/checkout/checkout-block-shipping.block_theme.side_effects.spec.ts b/tests/e2e/tests/checkout/checkout-block.block_theme.side_effects.spec.ts similarity index 69% rename from tests/e2e/tests/checkout/checkout-block-shipping.block_theme.side_effects.spec.ts rename to tests/e2e/tests/checkout/checkout-block.block_theme.side_effects.spec.ts index 38570ab27e2..7f379380951 100644 --- a/tests/e2e/tests/checkout/checkout-block-shipping.block_theme.side_effects.spec.ts +++ b/tests/e2e/tests/checkout/checkout-block.block_theme.side_effects.spec.ts @@ -2,11 +2,19 @@ * External dependencies */ import { test as base, expect } from '@woocommerce/e2e-playwright-utils'; +import { customerFile, guestFile } from '@woocommerce/e2e-utils'; /** * Internal dependencies */ import { CheckoutPage } from './checkout.page'; +import { + FREE_SHIPPING_NAME, + FREE_SHIPPING_PRICE, + SIMPLE_PHYSICAL_PRODUCT_NAME, + FLAT_RATE_SHIPPING_NAME, + FLAT_RATE_SHIPPING_PRICE, +} from './constants'; const test = base.extend< { pageObject: CheckoutPage } >( { pageObject: async ( { page }, use ) => { @@ -18,14 +26,7 @@ const test = base.extend< { pageObject: CheckoutPage } >( { } ); test.describe( 'Shopper → Checkout block → Shipping', () => { - const FREE_SHIPPING_NAME = 'Free shipping'; - const FREE_SHIPPING_PRICE = '$0.00'; - const FLAT_RATE_SHIPPING_NAME = 'Flat rate shipping'; - const FLAT_RATE_SHIPPING_PRICE = '$10.00'; - - test.use( { - storageState: process.env.CUSTOMERSTATE, - } ); + test.use( { storageState: customerFile } ); test( 'Shopper can choose free shipping, flat rate shipping, and can have different billing and shipping addresses', async ( { pageObject, @@ -96,3 +97,31 @@ test.describe( 'Shopper → Checkout block → Shipping', () => { await pageObject.verifyAddressDetails( 'shipping' ); } ); } ); + +// We only check if guest user can place an order because we already checked if logged in user can +// place an order in the previous test +test.describe( 'Shopper → Checkout block → Place Order', () => { + test.use( { storageState: guestFile } ); + + test( 'Guest user can place order', async ( { + pageObject, + frontendUtils, + page, + } ) => { + await frontendUtils.emptyCart(); + await frontendUtils.goToShop(); + await frontendUtils.addToCart( SIMPLE_PHYSICAL_PRODUCT_NAME ); + await frontendUtils.goToCheckout(); + await expect( + await pageObject.selectAndVerifyShippingOption( + FREE_SHIPPING_NAME, + FREE_SHIPPING_PRICE + ) + ).toBe( true ); + await pageObject.fillInCheckoutWithTestData(); + await pageObject.placeOrder(); + await expect( + page.getByText( 'Your order has been received.' ) + ).toBeVisible(); + } ); +} ); diff --git a/tests/e2e/tests/checkout/checkout.page.ts b/tests/e2e/tests/checkout/checkout.page.ts index e58e26675f0..bb1a0489234 100644 --- a/tests/e2e/tests/checkout/checkout.page.ts +++ b/tests/e2e/tests/checkout/checkout.page.ts @@ -270,7 +270,7 @@ export class CheckoutPage { shippingName: string, shippingPrice: string ) { - const shipping = this.page.getByLabel( shippingName ); + const shipping = this.page.getByLabel( shippingName ).first(); await expect( shipping ).toBeVisible(); if ( ! ( await this.isShippingRateSelected( diff --git a/tests/e2e/tests/price-filter/price-filter.block_theme.side_effects.spec.ts b/tests/e2e/tests/price-filter/price-filter.block_theme.side_effects.spec.ts index a2b90f1d27a..ca0eb33ad30 100644 --- a/tests/e2e/tests/price-filter/price-filter.block_theme.side_effects.spec.ts +++ b/tests/e2e/tests/price-filter/price-filter.block_theme.side_effects.spec.ts @@ -5,10 +5,6 @@ import { BlockData } from '@woocommerce/e2e-types'; import { test, expect } from '@woocommerce/e2e-playwright-utils'; import { BASE_URL, cli } from '@woocommerce/e2e-utils'; -/** - * Internal dependencies - */ - const blockData: BlockData< { urlSearchParamWhenFilterIsApplied: string; endpointAPI: string; diff --git a/tests/e2e/utils/constants.ts b/tests/e2e/utils/constants.ts index a0ab4d5b52d..8e711293fcd 100644 --- a/tests/e2e/utils/constants.ts +++ b/tests/e2e/utils/constants.ts @@ -12,3 +12,8 @@ export const STORAGE_STATE_PATH = path.join( process.cwd(), 'artifacts/storage-states/admin.json' ); + +// User roles file paths +export const adminFile = '.auth/admin.json'; +export const customerFile = '.auth/customer.json'; +export const guestFile = '.auth/guest.json'; diff --git a/tests/e2e/utils/frontend/frontend-utils.page.ts b/tests/e2e/utils/frontend/frontend-utils.page.ts index 1704085dd8f..36f7cbe17f7 100644 --- a/tests/e2e/utils/frontend/frontend-utils.page.ts +++ b/tests/e2e/utils/frontend/frontend-utils.page.ts @@ -177,4 +177,10 @@ export class FrontendUtils { await locator.press( 'End' ); await locator.press( 'Shift+Home' ); } + + async gotoMyAccount() { + await this.page.goto( '/my-account', { + waitUntil: 'commit', + } ); + } }