Add new E2E tests to cover Shopper Checkout Block flow (#41218)

* Add few E2E tests to cover half of the milestone

* Improve welcome modal closing

* Add new test scenarios

* Add more tests to cover creating account and shipping

* Update shipping zone labels to fulfil latest design changes

* Add tests for checkout block coupons and cart block coupons

* Remove only flag leftover

* Add checkout block tax tests and merge with cart test

* Update selectors to match checkout page

* Update checkout block test and comment scenario until issue resolved

* Remove only leftover

* Update checkout block test

* Improve filling shipping and billing checkout

* Create order via API with coupon

* Include checkout filling util helper

* Update test to use get by role for alerts

* Remove only leftover

* Adjust scenario for placing an order as a customer

* Improve existing customer placing order scenario

* Add util helper for adding products to cart

* Remove leftover from testing

---------

Co-authored-by: Jon Lane <jon.lane@automattic.com>
This commit is contained in:
Veljko V 2024-01-10 20:36:11 +01:00 committed by GitHub
parent 9c2048f029
commit eb49308781
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 1743 additions and 179 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: dev
Adds tests for shopper checkout block flows

View File

@ -3,7 +3,7 @@ const { admin } = require( '../../test-data/data' );
const { closeWelcomeModal } = require( '../../utils/editor' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
const simpleProductName = 'A Simple Product';
const simpleProductName = 'Cart Coupons Product';
const singleProductFullPrice = '110.00';
const singleProductSalePrice = '55.00';
const coupons = [
@ -23,11 +23,15 @@ const coupons = [
amount: '10.00',
},
];
const couponLimitedCode = '10fixedcartlimited';
const customerBilling = {
email: 'john.doe.merchant.test@example.com',
};
const pageTitle = 'Cart Block';
const pageSlug = pageTitle.replace( / /gi, '-' ).toLowerCase();
let product1Id;
let productId, orderId, limitedCouponId;
test.describe( 'Cart Block Applying Coupons', () => {
const couponBatchId = new Array();
@ -52,7 +56,7 @@ test.describe( 'Cart Block Applying Coupons', () => {
sale_price: singleProductSalePrice,
} )
.then( ( response ) => {
product1Id = response.data.id;
productId = response.data.id;
} );
// add coupons
await api
@ -64,6 +68,32 @@ test.describe( 'Cart Block Applying Coupons', () => {
couponBatchId.push( response.data.create[ i ].id );
}
} );
// add limited coupon
await api
.post( 'coupons', {
code: couponLimitedCode,
discount_type: 'fixed_cart',
amount: '10.00',
usage_limit: 1,
usage_count: 1,
} )
.then( ( response ) => {
limitedCouponId = response.data.id;
} );
// add order with applied limited coupon
await api
.post( 'orders', {
status: 'processing',
billing: customerBilling,
coupon_lines: [
{
code: couponLimitedCode,
},
],
} )
.then( ( response ) => {
orderId = response.data.id;
} );
} );
test.afterAll( async ( { baseURL } ) => {
@ -74,9 +104,14 @@ test.describe( 'Cart Block Applying Coupons', () => {
version: 'wc/v3',
} );
await api.post( 'products/batch', {
delete: [ product1Id ],
delete: [ productId ],
} );
await api.post( 'coupons/batch', {
delete: [ ...couponBatchId, limitedCouponId ],
} );
await api.post( 'orders/batch', {
delete: [ orderId ],
} );
await api.post( 'coupons/batch', { delete: [ ...couponBatchId ] } );
} );
test.beforeEach( async ( { context } ) => {
@ -121,7 +156,7 @@ test.describe( 'Cart Block Applying Coupons', () => {
} ) => {
const totals = [ '$50.00', '$27.50', '$45.00' ];
// add product to cart block
await page.goto( `/shop/?add-to-cart=${ product1Id }` );
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
@ -165,7 +200,7 @@ test.describe( 'Cart Block Applying Coupons', () => {
const totalsReverse = [ '$17.50', '$45.00', '$55.00' ];
const discounts = [ '-$5.00', '-$32.50', '-$42.50' ];
// add product to cart block
await page.goto( `/shop/?add-to-cart=${ product1Id }` );
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
@ -214,7 +249,7 @@ test.describe( 'Cart Block Applying Coupons', () => {
page,
} ) => {
// add product to cart block
await page.goto( `/shop/?add-to-cart=${ product1Id }` );
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
@ -247,4 +282,28 @@ test.describe( 'Cart Block Applying Coupons', () => {
)
).toBeVisible();
} );
test( 'prevents cart block applying coupon with usage limit', async ( {
page,
} ) => {
// add product to cart block and go to cart
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// add coupon with usage limit
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
await page
.locator( '#wc-block-components-totals-coupon__input-0' )
.fill( couponLimitedCode );
await page.getByText( 'Apply', { exact: true } ).click();
await expect(
page
.getByRole( 'alert' )
.getByText( 'Coupon usage limit has been reached.' )
).toBeVisible();
} );
} );

View File

@ -8,8 +8,14 @@ const productPrice = '100.00';
const messyProductPrice = '13.47';
const secondProductName = 'Second Product Cart Block Taxing';
const pageTitle = 'Cart Block';
const pageSlug = pageTitle.replace( / /gi, '-' ).toLowerCase();
const cartBlockPageTitle = 'Cart Block';
const cartBlockPageSlug = cartBlockPageTitle
.replace( / /gi, '-' )
.toLowerCase();
const checkoutBlockPageTitle = 'Checkout Block';
const checkoutBlockPageSlug = checkoutBlockPageTitle
.replace( / /gi, '-' )
.toLowerCase();
let productId,
productId2,
@ -24,7 +30,7 @@ let productId,
shippingZoneId,
shippingMethodId;
test.describe( 'Shopper Cart Block Tax Display', () => {
test.describe( 'Shopper Cart & Checkout Block Tax Display', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
@ -111,7 +117,7 @@ test.describe( 'Shopper Cart Block Tax Display', () => {
await page
.getByRole( 'textbox', { name: 'Add title' } )
.fill( pageTitle );
.fill( cartBlockPageTitle );
await page.getByRole( 'button', { name: 'Add default block' } ).click();
await page
.getByRole( 'document', {
@ -127,32 +133,92 @@ test.describe( 'Shopper Cart Block Tax Display', () => {
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await expect(
page.getByText( `${ pageTitle } is now live.` )
page.getByText( `${ cartBlockPageTitle } is now live.` )
).toBeVisible();
} );
test( 'that inclusive tax is displayed properly in Cart Block page', async ( {
test( 'can create Checkout Block page', async ( { page } ) => {
// create a new page with checkout block
await page.goto( 'wp-admin/post-new.php?post_type=page' );
await page.waitForLoadState( 'networkidle' );
await page.locator( 'input[name="log"]' ).fill( admin.username );
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
await page.locator( 'text=Log In' ).click();
// Close welcome popup if prompted
try {
await page
.getByLabel( 'Close', { exact: true } )
.click( { timeout: 5000 } );
} catch ( error ) {
console.log( "Welcome modal wasn't present, skipping action." );
}
await page
.getByRole( 'textbox', { name: 'Add title' } )
.fill( checkoutBlockPageTitle );
await page.getByRole( 'button', { name: 'Add default block' } ).click();
await page
.getByRole( 'document', {
name: 'Empty block; start writing or type forward slash to choose a block',
} )
.fill( '/checkout' );
await page.keyboard.press( 'Enter' );
await page
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await page
.getByRole( 'region', { name: 'Editor publish' } )
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await expect(
page.getByText( `${ checkoutBlockPageTitle } is now live.` )
).toBeVisible();
} );
test( 'that inclusive tax is displayed properly in blockbased Cart & Checkout pages', async ( {
page,
} ) => {
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item-tax' )
).toHaveText( 'Including $25.00 Nasty Tax' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item-tax' )
).toHaveText( 'Including $25.00 Nasty Tax' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item-tax' )
).toHaveText( 'Including $25.00 Nasty Tax' );
} );
} );
test( 'that exclusive tax is displayed properly in Cart Block page', async ( {
test( 'that exclusive tax is displayed properly in blockbased Cart & Checkout pages', async ( {
page,
baseURL,
} ) => {
@ -165,26 +231,48 @@ test.describe( 'Shopper Cart Block Tax Display', () => {
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'excl',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-taxes' )
).toContainText( '$25.00' );
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-taxes' )
).toContainText( '$25.00' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$125.00' );
await expect(
page.locator( '.wc-block-components-totals-taxes' )
).toContainText( '$25.00' );
} );
} );
} );
test.describe( 'Shopper Cart Block Tax Rounding', () => {
test.describe( 'Shopper Cart & Checkout Block Tax Rounding', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
@ -294,7 +382,7 @@ test.describe( 'Shopper Cart Block Tax Rounding', () => {
} );
} );
test( 'that tax rounding is present at subtotal level', async ( {
test( 'that tax rounding is present at subtotal level in blockbased Cart & Checkout pages', async ( {
page,
baseURL,
} ) => {
@ -313,25 +401,47 @@ test.describe( 'Shopper Cart Block Tax Rounding', () => {
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'single',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$40.41' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$50.12' );
await expect(
page.locator( '.wc-block-components-totals-taxes' )
).toContainText( '$9.71' );
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$40.41' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$50.12' );
await expect(
page.locator( '.wc-block-components-totals-taxes' )
).toContainText( '$9.71' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$40.41' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$50.12' );
await expect(
page.locator( '.wc-block-components-totals-taxes' )
).toContainText( '$9.71' );
} );
} );
test( 'that tax rounding is off at subtotal level', async ( {
test( 'that tax rounding is off at subtotal level in blockbased Cart & Checkout pages', async ( {
page,
baseURL,
} ) => {
@ -350,33 +460,62 @@ test.describe( 'Shopper Cart Block Tax Rounding', () => {
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$40.41' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$50.12' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$6.87' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$2.84' );
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$40.41' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$50.12' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$6.87' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$2.84' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$40.41' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$50.12' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$6.87' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$2.84' );
} );
} );
} );
test.describe( 'Shopper Cart Block Tax Levels', () => {
test.describe( 'Shopper Cart & Checkout Block Tax Levels', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
@ -558,41 +697,78 @@ test.describe( 'Shopper Cart Block Tax Levels', () => {
'Shipping costs updated.'
);
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$118.75' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$10.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$5.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$2.50' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$1.25' );
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$118.75' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$10.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$5.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$2.50' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$1.25' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$118.75' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$10.00' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$5.00' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$2.50' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$1.25' );
} );
} );
test( 'that applying taxes in Cart Block of 2 different levels (2 excluded) calculates properly', async ( {
test( 'that applying taxes in blockbased Cart & Checkout of 2 different levels (2 excluded) calculates properly', async ( {
page,
baseURL,
} ) => {
@ -605,32 +781,60 @@ test.describe( 'Shopper Cart Block Tax Levels', () => {
await api.put( 'settings/tax/woocommerce_tax_total_display', {
value: 'itemized',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$115.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$10.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$5.00' );
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$115.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$10.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-taxes-block'
)
).toContainText( '$5.00' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$100.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$115.00' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$10.00' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-taxes-block'
)
).toContainText( '$5.00' );
} );
} );
} );
test.describe( 'Shipping Cart Block Tax', () => {
test.describe( 'Shipping Cart & Checkout Block Tax', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
@ -743,23 +947,47 @@ test.describe( 'Shipping Cart Block Tax', () => {
await api.put( 'settings/tax/woocommerce_tax_display_cart', {
value: 'incl',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$115.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-shipping-block'
)
).toContainText( '$23.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$138.00' );
await test.step( 'Load cart page and confirm price display', async () => {
await page.goto( cartBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: cartBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-subtotal-block'
)
).toContainText( '$115.00' );
await expect(
page.locator(
'.wp-block-woocommerce-cart-order-summary-shipping-block'
)
).toContainText( '$23.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$138.00' );
} );
await test.step( 'Load checkout page and confirm price display', async () => {
await page.goto( checkoutBlockPageSlug );
await expect(
page.getByRole( 'heading', { name: checkoutBlockPageTitle } )
).toBeVisible();
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-subtotal-block'
)
).toContainText( '$115.00' );
await expect(
page.locator(
'.wp-block-woocommerce-checkout-order-summary-shipping-block'
)
).toContainText( '$23.00' );
await expect(
page.locator( '.wc-block-components-totals-footer-item' )
).toContainText( '$138.00' );
} );
} );
} );

View File

@ -0,0 +1,317 @@
const { test, expect } = require( '@playwright/test' );
const { admin } = require( '../../test-data/data' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
const simpleProductName = 'Checkout Coupons Product';
const singleProductFullPrice = '110.00';
const singleProductSalePrice = '55.00';
const coupons = [
{
code: '5fixedcheckout',
discount_type: 'fixed_cart',
amount: '5.00',
},
{
code: '50percoffcheckout',
discount_type: 'percent',
amount: '50',
},
{
code: '10fixedproductcheckout',
discount_type: 'fixed_product',
amount: '10.00',
},
];
const couponLimitedCode = '10fixedcheckoutlimited';
const customerBilling = {
email: 'john.doe.merchant.test@example.com',
};
const pageTitle = 'Checkout Block';
const pageSlug = pageTitle.replace( / /gi, '-' ).toLowerCase();
let productId, orderId, limitedCouponId;
test.describe( 'Checkout Block Applying Coupons', () => {
const couponBatchId = new Array();
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
// make sure the currency is USD
await api.put( 'settings/general/woocommerce_currency', {
value: 'USD',
} );
// add a product
await api
.post( 'products', {
name: simpleProductName,
type: 'simple',
regular_price: singleProductFullPrice,
sale_price: singleProductSalePrice,
} )
.then( ( response ) => {
productId = response.data.id;
} );
// add coupons
await api
.post( 'coupons/batch', {
create: coupons,
} )
.then( ( response ) => {
for ( let i = 0; i < response.data.create.length; i++ ) {
couponBatchId.push( response.data.create[ i ].id );
}
} );
// add limited coupon
await api
.post( 'coupons', {
code: couponLimitedCode,
discount_type: 'fixed_cart',
amount: '10.00',
usage_limit: 1,
usage_count: 1,
} )
.then( ( response ) => {
limitedCouponId = response.data.id;
} );
// add order with applied limited coupon
await api
.post( 'orders', {
status: 'processing',
billing: customerBilling,
coupon_lines: [
{
code: couponLimitedCode,
},
],
} )
.then( ( response ) => {
orderId = response.data.id;
} );
} );
test.afterAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.post( 'products/batch', {
delete: [ productId ],
} );
await api.post( 'coupons/batch', {
delete: [ ...couponBatchId, limitedCouponId ],
} );
await api.post( 'orders/batch', {
delete: [ orderId ],
} );
} );
test.beforeEach( async ( { context } ) => {
// Shopping cart is very sensitive to cookies, so be explicit
await context.clearCookies();
} );
test( 'can create checkout block page', async ( { page } ) => {
// create a new page with checkout block
await page.goto( 'wp-admin/post-new.php?post_type=page' );
await page.waitForLoadState( 'networkidle' );
await page.locator( 'input[name="log"]' ).fill( admin.username );
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
await page.locator( 'text=Log In' ).click();
// Close welcome popup if prompted
try {
await page
.getByLabel( 'Close', { exact: true } )
.click( { timeout: 5000 } );
} catch ( error ) {
console.log( "Welcome modal wasn't present, skipping action." );
}
await page
.getByRole( 'textbox', { name: 'Add title' } )
.fill( pageTitle );
await page.getByRole( 'button', { name: 'Add default block' } ).click();
await page
.getByRole( 'document', {
name: 'Empty block; start writing or type forward slash to choose a block',
} )
.fill( '/checkout' );
await page.keyboard.press( 'Enter' );
await page
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await page
.getByRole( 'region', { name: 'Editor publish' } )
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await expect(
page.getByText( `${ pageTitle } is now live.` )
).toBeVisible();
} );
test( 'allows checkout block to apply coupon of any type', async ( {
page,
} ) => {
const totals = [ '$50.00', '$27.50', '$45.00' ];
// add product to cart block and go to checkout
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// apply all coupon types
for ( let i = 0; i < coupons.length; i++ ) {
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
await page
.locator( '#wc-block-components-totals-coupon__input-0' )
.fill( coupons[ i ].code );
await page.getByText( 'Apply', { exact: true } ).click();
await expect(
page
.locator( '.wc-block-components-notice-banner__content' )
.getByText(
`Coupon code "${ coupons[ i ].code }" has been applied to your cart.`
)
).toBeVisible();
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toHaveText( totals[ i ] );
await page
.getByLabel( `Remove coupon "${ coupons[ i ].code }"` )
.click();
await expect(
page
.locator( '.wc-block-components-notice-banner__content' )
.getByText(
`Coupon code "${ coupons[ i ].code }" has been removed from your cart.`
)
).toBeVisible();
}
} );
test( 'allows checkout block to apply multiple coupons', async ( {
page,
} ) => {
const totals = [ '$50.00', '$22.50', '$12.50' ];
const totalsReverse = [ '$17.50', '$45.00', '$55.00' ];
const discounts = [ '-$5.00', '-$32.50', '-$42.50' ];
// add product to cart block and go to checkout
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// add all coupons and verify prices
for ( let i = 0; i < coupons.length; i++ ) {
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
await page
.locator( '#wc-block-components-totals-coupon__input-0' )
.fill( coupons[ i ].code );
await page.getByText( 'Apply', { exact: true } ).click();
await expect(
page
.locator( '.wc-block-components-notice-banner__content' )
.getByText(
`Coupon code "${ coupons[ i ].code }" has been applied to your cart.`
)
).toBeVisible();
await expect(
page.locator(
'.wc-block-components-totals-discount > .wc-block-components-totals-item__value'
)
).toHaveText( discounts[ i ] );
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toHaveText( totals[ i ] );
}
for ( let i = 0; i < coupons.length; i++ ) {
await page
.getByLabel( `Remove coupon "${ coupons[ i ].code }"` )
.click();
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toHaveText( totalsReverse[ i ] );
}
} );
test( 'prevents checkout block applying same coupon twice', async ( {
page,
} ) => {
// add product to cart block and go to checkout
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// try to add two same coupons and verify the error message
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
await page
.locator( '#wc-block-components-totals-coupon__input-0' )
.fill( coupons[ 0 ].code );
await page.getByText( 'Apply', { exact: true } ).click();
await expect(
page
.locator( '.wc-block-components-notice-banner__content' )
.getByText(
`Coupon code "${ coupons[ 0 ].code }" has been applied to your cart.`
)
).toBeVisible();
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
await page
.locator( '#wc-block-components-totals-coupon__input-0' )
.fill( coupons[ 0 ].code );
await page.getByText( 'Apply', { exact: true } ).click();
await expect(
page
.getByRole( 'alert' )
.getByText(
`Coupon code "${ coupons[ 0 ].code }" has already been applied.`
)
).toBeVisible();
} );
test( 'prevents checkout block applying coupon with usage limit', async ( {
page,
} ) => {
// add product to cart block and go to checkout
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// add coupon with usage limit
await page.getByRole( 'button', { name: 'Add a coupon' } ).click();
await page
.locator( '#wc-block-components-totals-coupon__input-0' )
.fill( couponLimitedCode );
await page.getByText( 'Apply', { exact: true } ).click();
await expect(
page
.getByRole( 'alert' )
.getByText( 'Coupon usage limit has been reached.' )
).toBeVisible();
} );
} );

View File

@ -0,0 +1,865 @@
const { test, expect } = require( '@playwright/test' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
const { admin, customer } = require( '../../test-data/data' );
const { setFilterValue, clearFilters } = require( '../../utils/filters' );
const { addProductsToCart } = require( '../../utils/pdp' );
const {
fillShippingCheckoutBlocks,
fillBillingCheckoutBlocks,
} = require( '../../utils/checkout' );
const guestEmail = 'checkout-guest@example.com';
const newAccountEmail = 'marge-test-account@example.com';
const simpleProductName = 'Very Simple Product';
const simpleProductDesc = 'Lorem ipsum dolor.';
const singleProductFullPrice = '150.00';
const singleProductSalePrice = '75.00';
const twoProductPrice = ( singleProductSalePrice * 2 ).toString();
const threeProductPrice = ( singleProductSalePrice * 3 ).toString();
const pageTitle = 'Checkout Block';
const pageSlug = pageTitle.replace( / /gi, '-' ).toLowerCase();
let guestOrderId1,
guestOrderId2,
customerOrderId,
newAccountOrderId,
productId,
shippingZoneId;
test.describe( 'Checkout Block page', () => {
test.beforeAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
// ensure store address is US
await api.post( 'settings/general/batch', {
update: [
{
id: 'woocommerce_store_address',
value: 'addr 1',
},
{
id: 'woocommerce_store_city',
value: 'San Francisco',
},
{
id: 'woocommerce_default_country',
value: 'US:CA',
},
{
id: 'woocommerce_store_postcode',
value: '94107',
},
],
} );
// add product
await api
.post( 'products', {
name: simpleProductName,
description: simpleProductDesc,
type: 'simple',
regular_price: singleProductFullPrice,
sale_price: singleProductSalePrice,
} )
.then( ( response ) => {
productId = response.data.id;
} );
// enable logging through checkout
await api.put(
'settings/account/woocommerce_enable_checkout_login_reminder',
{
value: 'yes',
}
);
// enable creating account through checkout
await api.put(
'settings/account/woocommerce_enable_signup_and_login_from_checkout',
{
value: 'yes',
}
);
// add a shipping zone and method
await api
.post( 'shipping/zones', {
name: 'California and Oregon Shipping Zone',
} )
.then( ( response ) => {
shippingZoneId = response.data.id;
} );
await api.put( `shipping/zones/${ shippingZoneId }/locations`, [
{
code: 'US:CA',
type: 'state',
},
{
code: 'US:OR',
type: 'state',
},
] );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'free_shipping',
settings: {
title: 'Free shipping',
},
} );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'local_pickup',
settings: {
title: 'Local pickup',
},
} );
await api.post( `shipping/zones/${ shippingZoneId }/methods`, {
method_id: 'flat_rate',
settings: {
title: 'Flat rate',
cost: singleProductSalePrice,
},
} );
// enable bank transfers and COD for payment
await api.put( 'payment_gateways/bacs', {
enabled: true,
} );
await api.put( 'payment_gateways/cod', {
enabled: true,
} );
// make sure there's no pre-existing customer that has the same email we're going to use for account creation
const { data: customersList } = await api.get( 'customers', {
email: newAccountEmail,
} );
if ( customersList && customersList.length ) {
const customerId = customersList[ 0 ].id;
console.log(
`Customer with email ${ newAccountEmail } exists! Deleting it before starting test...`
);
await api.delete( `customers/${ customerId }`, { force: true } );
}
} );
test.afterAll( async ( { baseURL } ) => {
const api = new wcApi( {
url: baseURL,
consumerKey: process.env.CONSUMER_KEY,
consumerSecret: process.env.CONSUMER_SECRET,
version: 'wc/v3',
} );
await api.delete( `products/${ productId }`, {
force: true,
} );
await api.delete( `shipping/zones/${ shippingZoneId }`, {
force: true,
} );
await api.put( 'payment_gateways/bacs', {
enabled: false,
} );
await api.put( 'payment_gateways/cod', {
enabled: false,
} );
await api.put(
'settings/account/woocommerce_enable_checkout_login_reminder',
{
value: 'no',
}
);
await api.put(
'settings/account/woocommerce_enable_signup_and_login_from_checkout',
{
value: 'no',
}
);
// delete the orders we created
if ( guestOrderId1 ) {
await api.delete( `orders/${ guestOrderId1 }`, { force: true } );
}
if ( guestOrderId2 ) {
await api.delete( `orders/${ guestOrderId2 }`, { force: true } );
}
if ( customerOrderId ) {
await api.delete( `orders/${ customerOrderId }`, { force: true } );
}
if ( newAccountOrderId ) {
await api.delete( `orders/${ newAccountOrderId }`, {
force: true,
} );
}
// clear out the customer we create during the test
await api.get( 'customers' ).then( async ( response ) => {
for ( let i = 0; i < response.data.length; i++ ) {
if ( response.data[ i ].billing.email === newAccountEmail ) {
await api.delete( `customers/${ response.data[ i ].id }`, {
force: true,
} );
}
}
} );
} );
test.beforeEach( async ( { context } ) => {
// Shopping cart is very sensitive to cookies, so be explicit
await context.clearCookies();
} );
test( 'can see empty checkout block page', async ( { page } ) => {
// create a new page with checkout block
await page.goto( 'wp-admin/post-new.php?post_type=page' );
await page.waitForLoadState( 'networkidle' );
await page.locator( 'input[name="log"]' ).fill( admin.username );
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
await page.locator( 'text=Log In' ).click();
// Close welcome popup if prompted
try {
await page
.getByLabel( 'Close', { exact: true } )
.click( { timeout: 5000 } );
} catch ( error ) {
console.log( "Welcome modal wasn't present, skipping action." );
}
await page
.getByRole( 'textbox', { name: 'Add title' } )
.fill( pageTitle );
await page.getByRole( 'button', { name: 'Add default block' } ).click();
await page
.getByRole( 'document', {
name: 'Empty block; start writing or type forward slash to choose a block',
} )
.fill( '/checkout' );
await page.keyboard.press( 'Enter' );
await page
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await page
.getByRole( 'region', { name: 'Editor publish' } )
.getByRole( 'button', { name: 'Publish', exact: true } )
.click();
await expect(
page.getByText( `${ pageTitle } is now live.` )
).toBeVisible();
// go to the page to test empty cart block
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await expect(
page.getByText( 'Your cart is currently empty!' )
).toBeVisible();
await expect(
page.getByRole( 'link', { name: 'Browse store' } )
).toBeVisible();
await page.getByRole( 'link', { name: 'Browse store' } ).click();
await expect(
page.getByRole( 'heading', { name: 'Shop' } )
).toBeVisible();
} );
test( 'allows customer to choose available payment methods', async ( {
page,
} ) => {
// this time we're going to add two products to the cart
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// check the order summary
await expect(
page.locator( '.wc-block-components-order-summary-item__quantity' )
).toContainText( '2' );
await expect(
page.locator(
'.wc-block-components-order-summary-item__individual-price'
)
).toContainText( `$${ singleProductSalePrice }` );
await expect(
page.locator( '.wc-block-components-product-metadata__description' )
).toContainText( simpleProductDesc );
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toContainText( twoProductPrice );
// check the payment methods
await expect( page.getByLabel( 'Direct bank transfer' ) ).toBeVisible();
await expect( page.getByLabel( 'Cash on delivery' ) ).toBeVisible();
await page.getByLabel( 'Cash on delivery' ).check();
await expect( page.getByLabel( 'Cash on delivery' ) ).toBeChecked();
} );
test( 'allows customer to fill shipping details', async ( { page } ) => {
// this time we're going to add three products to the cart
await addProductsToCart( page, simpleProductName, '3' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// check the order summary
await expect(
page.locator( '.wc-block-components-order-summary-item__quantity' )
).toContainText( '3' );
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toContainText( threeProductPrice );
// asserting that you can fill in the shipping details
await expect( page.getByLabel( 'Email address' ) ).toBeEditable();
await expect( page.getByLabel( 'First name' ) ).toBeEditable();
await expect( page.getByLabel( 'Last name' ) ).toBeEditable();
await expect(
page.getByLabel( 'Address', { exact: true } )
).toBeEditable();
await expect(
page.getByLabel( 'Apartment, suite, etc. (optional)' )
).toBeEnabled();
await expect(
page.getByLabel( 'United States (US), Country/Region' )
).toBeEditable();
await expect( page.getByLabel( 'California, State' ) ).toBeEditable();
await expect( page.getByLabel( 'City' ) ).toBeEditable();
await expect( page.getByLabel( 'ZIP Code' ) ).toBeEnabled();
await expect( page.getByLabel( 'Phone (optional)' ) ).toBeEditable();
} );
test( 'allows customer to fill different shipping and billing details', async ( {
page,
} ) => {
await page.goto( `/shop/?add-to-cart=${ productId }`, {
waitUntil: 'networkidle',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await page.getByLabel( 'Email address' ).fill( guestEmail );
// fill shipping address
await fillShippingCheckoutBlocks( page );
await page.getByLabel( 'Use same address for billing' ).click();
// fill billing details
await fillBillingCheckoutBlocks( page );
// add note to the order
await page.getByLabel( 'Add a note to your order' ).check();
await page
.getByPlaceholder(
'Notes about your order, e.g. special notes for delivery.'
)
.fill( 'This is to avoid flakiness' );
// place an order
await page.getByRole( 'button', { name: 'Place order' } ).click();
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
// get order ID from the page
const orderReceivedText = await page
.locator( '.woocommerce-order-overview__order.order' )
.textContent();
guestOrderId2 = await orderReceivedText
.split( /(\s+)/ )[ 6 ]
.toString();
// go again to the checkout to verify details
await page.goto( `/shop/?add-to-cart=${ productId }`, {
waitUntil: 'networkidle',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// verify shipping details
await page
.getByLabel( 'Edit address', { exact: true } )
.first()
.click();
await expect(
page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'First name' )
).toHaveValue( 'Homer' );
await expect(
page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'Last name' )
).toHaveValue( 'Simpson' );
await expect(
page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'Address', { exact: true } )
).toHaveValue( '123 Evergreen Terrace' );
await expect(
page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'City' )
).toHaveValue( 'Springfield' );
await expect(
page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'ZIP Code' )
).toHaveValue( '97403' );
// verify billing details
// ISSUE REPORTED #42967, please uncomment below once fixed
// await page.getByLabel( 'Edit address', { exact: true } ).last().click();
// await expect(
// page
// .getByRole( 'group', { name: 'Billing address' } )
// .getByLabel( 'First name' )
// ).toHaveValue( 'Mister' );
// await expect(
// page
// .getByRole( 'group', { name: 'Billing address' } )
// .getByLabel( 'Last name' )
// ).toHaveValue( 'Burns' );
// await expect(
// page
// .getByRole( 'group', { name: 'Billing address' } )
// .getByLabel( 'Address', { exact: true } )
// ).toHaveValue( '156th Street' );
// await expect(
// page
// .getByRole( 'group', { name: 'Billing address' } )
// .getByLabel( 'City' )
// ).toHaveValue( 'Springfield' );
// await expect(
// page
// .getByRole( 'group', { name: 'Billing address' } )
// .getByLabel( 'ZIP Code' )
// ).toHaveValue( '98500' );
} );
test( 'warn when customer is missing required details', async ( {
page,
} ) => {
await page.goto( `/shop/?add-to-cart=${ productId }`, {
waitUntil: 'networkidle',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// first try submitting the form with no fields complete
await page.getByRole( 'button', { name: 'Place order' } ).click();
await expect(
page.getByText( 'Please enter a valid email address' )
).toBeVisible();
await expect(
page.getByText( 'Please enter a valid first name' )
).toBeVisible();
await expect(
page.getByText( 'Please enter a valid last name' )
).toBeVisible();
await expect(
page.getByText( 'Please enter a valid address' )
).toBeVisible();
await expect(
page.getByText( 'Please enter a valid city' )
).toBeVisible();
await expect(
page.getByText( 'Please enter a valid zip code' )
).toBeVisible();
} );
test( 'allows customer to fill shipping details and toggle different billing', async ( {
page,
} ) => {
await page.goto( `/shop/?add-to-cart=${ productId }`, {
waitUntil: 'networkidle',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await page.getByLabel( 'Email address' ).fill( customer.email );
// fill shipping address and check the toggle to use a different address for billing
await fillShippingCheckoutBlocks( page );
await expect(
page.getByLabel( 'Use same address for billing' )
).toBeVisible();
await page.getByLabel( 'Use same address for billing' ).click();
await expect(
page
.getByRole( 'group', { name: 'Billing address' } )
.locator( 'h2' )
).toBeVisible();
} );
test( 'can choose different shipping types in the checkout', async ( {
page,
} ) => {
await page.goto( `/shop/?add-to-cart=${ productId }`, {
waitUntil: 'networkidle',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await page.getByLabel( 'Email address' ).fill( customer.email );
// fill shipping address
await fillShippingCheckoutBlocks( page );
await page
.locator( '.wc-block-components-loading-mask' )
.waitFor( { state: 'visible' } );
await page
.locator( '.wc-block-components-loading-mask' )
.waitFor( { state: 'hidden' } );
// check if you see all three shipping options
await expect( page.getByLabel( 'Free shipping' ) ).toBeVisible();
await expect( page.getByLabel( 'Local pickup' ) ).toBeVisible();
await expect( page.getByLabel( 'Flat rate' ) ).toBeVisible();
// check free shipping option
await page.getByLabel( 'Free shipping' ).check();
await page
.locator( '.wc-block-components-loading-mask' )
.waitFor( { state: 'visible' } );
await page
.locator( '.wc-block-components-loading-mask' )
.waitFor( { state: 'hidden' } );
await expect( page.getByLabel( 'Free shipping' ) ).toBeChecked();
await expect(
page.locator( '.wc-block-components-totals-shipping__via' )
).toHaveText( 'Free shipping' );
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toContainText( singleProductSalePrice );
// check local pickup option
await page.getByLabel( 'Local pickup' ).check();
await page
.locator( '.wc-block-components-loading-mask' )
.waitFor( { state: 'hidden' } );
await expect( page.getByLabel( 'Local pickup' ) ).toBeChecked();
await expect(
page.locator( '.wc-block-components-totals-shipping__via' )
).toHaveText( 'Local pickup' );
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toContainText( singleProductSalePrice );
// check flat rate option
await page.getByLabel( 'Flat rate' ).check();
await page
.locator( '.wc-block-components-loading-mask' )
.waitFor( { state: 'hidden' } );
await expect( page.getByLabel( 'Flat rate' ) ).toBeChecked();
await expect(
page.locator( '.wc-block-components-totals-shipping__via' )
).toHaveText( 'Flat rate' );
await expect(
page.locator(
'.wc-block-components-totals-footer-item > .wc-block-components-totals-item__value'
)
).toContainText( twoProductPrice );
} );
test( 'allows guest customer to place an order', async ( { page } ) => {
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
await page.getByLabel( 'Email address' ).fill( guestEmail );
// fill shipping address and check cash on delivery method
await fillShippingCheckoutBlocks( page );
await page.getByLabel( 'Cash on delivery' ).check();
await expect( page.getByLabel( 'Cash on delivery' ) ).toBeChecked();
// add note to the order
await page.getByLabel( 'Add a note to your order' ).check();
await page
.getByPlaceholder(
'Notes about your order, e.g. special notes for delivery.'
)
.fill( 'This is to avoid flakiness' );
// place an order
await page.getByRole( 'button', { name: 'Place order' } ).click();
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
// get order ID from the page
const orderReceivedText = await page
.locator( '.woocommerce-order-overview__order.order' )
.textContent();
guestOrderId1 = await orderReceivedText
.split( /(\s+)/ )[ 6 ]
.toString();
// Let's simulate a new browser context (by dropping all cookies), and reload the page. This approximates a
// scenario where the server can no longer identify the shopper. However, so long as we are within the 10 minute
// grace period following initial order placement, the 'order received' page should still be rendered.
await page.context().clearCookies();
await page.reload();
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
// Let's simulate a scenario where the 10 minute grace period has expired. This time, we expect the shopper to
// be presented with a request to verify their email address.
await setFilterValue(
page,
'woocommerce_order_email_verification_grace_period',
0
);
await page.reload();
await expect(
page.locator( 'form.woocommerce-verify-email p:nth-child(3)' )
).toContainText( /verify the email address associated with the order/ );
// Supplying an email address other than the actual order billing email address will take them back to the same
// page with an error message.
await page.fill( '#email', 'incorrect@email.address' );
await page.locator( 'form.woocommerce-verify-email button' ).click();
await expect(
page.locator( 'form.woocommerce-verify-email p:nth-child(4)' )
).toContainText( /verify the email address associated with the order/ );
await expect(
page
.getByRole( 'alert' )
.getByText(
'We were unable to verify the email address you provided. Please try again.'
)
).toBeVisible();
// However if they supply the *correct* billing email address, they should see the order received page again.
await page.fill( '#email', guestEmail );
await page.locator( 'form.woocommerce-verify-email button' ).click();
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
await page.goto( 'wp-login.php' );
await page.locator( 'input[name="log"]' ).fill( admin.username );
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
await page.locator( 'text=Log In' ).click();
// load the order placed as a guest
await page.goto(
`wp-admin/post.php?post=${ guestOrderId1 }&action=edit`
);
await expect(
page.getByRole( 'heading', {
name: `Order #${ guestOrderId1 } details`,
} )
).toBeVisible();
await expect( page.locator( '.wc-order-item-name' ) ).toContainText(
simpleProductName
);
await expect( page.locator( 'td.quantity >> nth=0' ) ).toContainText(
'2'
);
await expect( page.locator( 'td.item_cost >> nth=0' ) ).toContainText(
singleProductSalePrice
);
await expect( page.locator( 'td.line_cost >> nth=0' ) ).toContainText(
twoProductPrice
);
await clearFilters( page );
} );
test( 'allows existing customer to place an order', async ( { page } ) => {
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// click to log in and make sure you are on the same page after logging in
await page.locator( 'text=Log in.' ).click();
await page.waitForLoadState( 'networkidle' );
await page
.locator( 'input[name="username"]' )
.fill( customer.username );
await page
.locator( 'input[name="password"]' )
.fill( customer.password );
await page.locator( 'text=Log in' ).click();
await page.waitForLoadState( 'networkidle' );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// if edit address is present click it, otherwise fill shipping details
if (
await page
.getByLabel( 'Edit address', { exact: true } )
.first()
.isVisible()
) {
await page
.getByLabel( 'Edit address', { exact: true } )
.first()
.click();
} else {
console.log(
'No saved shipping address found, filling it instead.'
);
// fill shipping address
await fillShippingCheckoutBlocks( page );
}
// check COD payment method
await page.getByLabel( 'Cash on delivery' ).check();
await expect( page.getByLabel( 'Cash on delivery' ) ).toBeChecked();
// add note to the order
await page.getByLabel( 'Add a note to your order' ).check();
await page
.getByPlaceholder(
'Notes about your order, e.g. special notes for delivery.'
)
.fill( 'This is to avoid flakiness' );
// place an order
await page.getByRole( 'button', { name: 'Place order' } ).click();
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
// get order ID from the page
const orderReceivedText = await page
.locator( '.woocommerce-order-overview__order.order' )
.textContent();
customerOrderId = await orderReceivedText
.split( /(\s+)/ )[ 6 ]
.toString();
// Effect a log out/simulate a new browsing session by dropping all cookies.
await page.context().clearCookies();
await page.reload();
// Now we are logged out, return to the confirmation page: we should be asked to log back in.
await expect(
page
.getByRole( 'alert' )
.getByText( 'Please log in to your account to view this order' )
).toBeVisible();
// Switch to admin user.
await page.goto( 'wp-login.php?loggedout=true' );
await page.locator( 'input[name="log"]' ).fill( admin.username );
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
await page.locator( 'text=Log In' ).click();
// load the order placed as a customer
await page.goto(
`wp-admin/post.php?post=${ customerOrderId }&action=edit`
);
await expect(
page.locator( 'h2.woocommerce-order-data__heading' )
).toContainText( `Order #${ customerOrderId } details` );
await expect( page.locator( '.wc-order-item-name' ) ).toContainText(
simpleProductName
);
await expect( page.locator( 'td.quantity >> nth=0' ) ).toContainText(
'2'
);
await expect( page.locator( 'td.item_cost >> nth=0' ) ).toContainText(
singleProductSalePrice
);
await expect( page.locator( 'td.line_cost >> nth=0' ) ).toContainText(
twoProductPrice
);
} );
test( 'can create an account during checkout', async ( { page } ) => {
await page.goto( `/shop/?add-to-cart=${ productId }`, {
waitUntil: 'networkidle',
} );
await page.goto( pageSlug );
await expect(
page.getByRole( 'heading', { name: pageTitle } )
).toBeVisible();
// check create account during checkout
await expect( page.getByLabel( 'Create an account?' ) ).toBeVisible();
await page.getByLabel( 'Create an account?' ).check();
await expect( page.getByLabel( 'Create an account?' ) ).toBeChecked();
await page.getByLabel( 'Email address' ).fill( newAccountEmail );
// fill shipping address and check cash on delivery method
await fillShippingCheckoutBlocks( page, 'Marge' );
await page.getByLabel( 'Cash on delivery' ).check();
await expect( page.getByLabel( 'Cash on delivery' ) ).toBeChecked();
// add note to the order
await page.getByLabel( 'Add a note to your order' ).check();
await page
.getByPlaceholder(
'Notes about your order, e.g. special notes for delivery.'
)
.fill( 'This is to avoid flakiness' );
// place an order
await page.getByRole( 'button', { name: 'Place order' } ).click();
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
// get order ID from the page
const orderReceivedText = await page
.locator( '.woocommerce-order-overview__order.order' )
.textContent();
newAccountOrderId = orderReceivedText.split( /(\s+)/ )[ 6 ].toString();
// confirms that an account was created
await page.goto( '/my-account/' );
await expect(
page.getByRole( 'heading', { name: 'My account' } )
).toBeVisible();
await page
.getByRole( 'navigation' )
.getByRole( 'link', { name: 'Log out' } )
.click();
// sign in as admin to confirm account creation
await page.goto( 'wp-admin/users.php' );
await page.waitForLoadState( 'networkidle' );
await page.locator( 'input[name="log"]' ).fill( admin.username );
await page.locator( 'input[name="pwd"]' ).fill( admin.password );
await page.locator( 'text=Log in' ).click();
await expect( page.locator( 'tbody#the-list' ) ).toContainText(
newAccountEmail
);
} );
} );

View File

@ -2,6 +2,7 @@ const { test, expect } = require( '@playwright/test' );
const wcApi = require( '@woocommerce/woocommerce-rest-api' ).default;
const { admin, customer } = require( '../../test-data/data' );
const { setFilterValue, clearFilters } = require( '../../utils/filters' );
const { addProductsToCart } = require( '../../utils/pdp' );
const guestEmail = 'checkout-guest@example.com';
@ -143,10 +144,7 @@ test.describe( 'Checkout page', () => {
page,
} ) => {
// this time we're going to add two products to the cart
for ( let i = 1; i < 3; i++ ) {
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
}
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( '/checkout/' );
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
@ -169,10 +167,7 @@ test.describe( 'Checkout page', () => {
test( 'allows customer to fill billing details', async ( { page } ) => {
// this time we're going to add three products to the cart
for ( let i = 1; i < 4; i++ ) {
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
}
await addProductsToCart( page, simpleProductName, '3' );
await page.goto( '/checkout/' );
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
@ -272,10 +267,7 @@ test.describe( 'Checkout page', () => {
} );
test( 'allows customer to fill shipping details', async ( { page } ) => {
for ( let i = 1; i < 3; i++ ) {
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
}
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( '/checkout/' );
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
@ -306,10 +298,7 @@ test.describe( 'Checkout page', () => {
} );
test( 'allows guest customer to place an order', async ( { page } ) => {
for ( let i = 1; i < 3; i++ ) {
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
}
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( '/checkout/' );
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(
@ -430,10 +419,7 @@ test.describe( 'Checkout page', () => {
.fill( customer.password );
await page.locator( 'text=Log In' ).click();
await page.waitForLoadState( 'networkidle' );
for ( let i = 1; i < 3; i++ ) {
await page.goto( `/shop/?add-to-cart=${ productId }` );
await page.waitForLoadState( 'networkidle' );
}
await addProductsToCart( page, simpleProductName, '2' );
await page.goto( '/checkout/' );
await expect( page.locator( 'strong.product-quantity' ) ).toContainText(

View File

@ -0,0 +1,79 @@
/**
* Util helper made for filling shipping details in the blockbased checkout
*
* @param page
* @param firstName
* @param lastName
* @param address
* @param city
* @param zip
*/
export async function fillShippingCheckoutBlocks(
page,
firstName = 'Homer',
lastName = 'Simpson',
address = '123 Evergreen Terrace',
city = 'Springfield',
zip = '97403'
) {
await page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'First name' )
.fill( firstName );
await page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'Last name' )
.fill( lastName );
await page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'Address', { exact: true } )
.fill( address );
await page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'City' )
.fill( city );
await page
.getByRole( 'group', { name: 'Shipping address' } )
.getByLabel( 'ZIP Code' )
.fill( zip );
}
/**
* Util helper made for filling billing details in the blockbased checkout
*
* @param page
* @param firstName
* @param lastName
* @param address
* @param city
* @param zip
*/
export async function fillBillingCheckoutBlocks(
page,
firstName = 'Mister',
lastName = 'Burns',
address = '156th Street',
city = 'Springfield',
zip = '98500'
) {
await page
.getByRole( 'group', { name: 'Billing address' } )
.getByLabel( 'First name' )
.fill( firstName );
await page
.getByRole( 'group', { name: 'Billing address' } )
.getByLabel( 'Last name' )
.fill( lastName );
await page
.getByRole( 'group', { name: 'Billing address' } )
.getByLabel( 'Address', { exact: true } )
.fill( address );
await page
.getByRole( 'group', { name: 'Billing address' } )
.getByLabel( 'City' )
.fill( city );
await page
.getByRole( 'group', { name: 'Billing address' } )
.getByLabel( 'ZIP Code' )
.fill( zip );
}

View File

@ -0,0 +1,26 @@
const { expect } = require( '@playwright/test' );
/**
* Util helper made for adding multiple same products to cart
*
* @param page
* @param productName
* @param quantityCount
*/
export async function addProductsToCart( page, productName, quantityCount ) {
await page.goto(
`product/${ productName.replace( / /gi, '-' ).toLowerCase() }`
);
await expect( page.locator( '.product_title' ) ).toContainText(
productName
);
await page.getByLabel( 'Product quantity' ).fill( quantityCount );
await page.getByRole( 'button', { name: 'Add to cart' } ).click();
await expect(
page
.getByRole( 'alert' )
.getByText(
`${ quantityCount } ×${ productName }” have been added to your cart.`
)
).toBeVisible();
}