Skip to content

Commit 961edc8

Browse files
committed
Initial shopper-checkout-purchase.spec.ts migration
To review required changes and refactors to also get rid of @woocommerce/e2e-utils
1 parent b5a8d52 commit 961edc8

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed

tests/e2e-pw/config/default.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,3 +271,5 @@ export const config = {
271271
},
272272
},
273273
};
274+
275+
export type CustomerAddress = typeof config.addresses.customer.billing;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { test, expect } from '@playwright/test';
5+
6+
/**
7+
* Internal dependencies
8+
*/
9+
10+
import { config } from '../../config/default';
11+
import * as shopper from '../../utils/shopper';
12+
13+
test.describe( 'Successful purchase', () => {
14+
test.beforeEach( async ( { page } ) => {
15+
await page.goto( '/cart/' );
16+
await shopper.addCartProduct( page );
17+
18+
await page.goto( '/checkout/' );
19+
await shopper.fillBillingAddress(
20+
page,
21+
config.addresses.customer.billing
22+
);
23+
} );
24+
25+
test( 'using a basic card', async ( { page } ) => {
26+
await shopper.fillCardDetails( page );
27+
await shopper.placeOrder( page );
28+
29+
await expect(
30+
page.getByText( 'Order received' ).first()
31+
).toBeVisible();
32+
} );
33+
34+
test( 'using a 3DS card', async ( { page } ) => {
35+
await shopper.fillCardDetails( page, config.cards[ '3ds' ] );
36+
await shopper.placeOrder( page );
37+
await shopper.confirmCardAuthentication( page, '3DS' );
38+
39+
await expect(
40+
page.getByText( 'Order received' ).first()
41+
).toBeVisible();
42+
} );
43+
} );

tests/e2e-pw/utils/shopper.ts

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { Page } from 'playwright/test';
5+
6+
/**
7+
* Internal dependencies
8+
*/
9+
10+
import { config, CustomerAddress } from '../config/default';
11+
12+
export const fillBillingAddress = async (
13+
page: Page,
14+
billingAddress: CustomerAddress
15+
): Promise< void > => {
16+
await page
17+
.locator( '#billing_first_name' )
18+
.fill( billingAddress.firstname );
19+
await page.locator( '#billing_last_name' ).fill( billingAddress.lastname );
20+
await page.locator( '#billing_company' ).fill( billingAddress.company );
21+
await page
22+
.locator( '#billing_country' )
23+
.selectOption( billingAddress.country );
24+
await page
25+
.locator( '#billing_address_1' )
26+
.fill( billingAddress.addressfirstline );
27+
await page
28+
.locator( '#billing_address_2' )
29+
.fill( billingAddress.addresssecondline );
30+
await page.locator( '#billing_city' ).fill( billingAddress.city );
31+
await page.locator( '#billing_state' ).selectOption( billingAddress.state );
32+
await page.locator( '#billing_postcode' ).fill( billingAddress.postcode );
33+
await page.locator( '#billing_phone' ).fill( billingAddress.phone );
34+
await page.locator( '#billing_email' ).fill( billingAddress.email );
35+
};
36+
37+
export const placeOrder = async ( page: Page ): Promise< void > => {
38+
await page.locator( '#place_order' ).click();
39+
};
40+
41+
export const addCartProduct = async (
42+
page: Page,
43+
productSlug = 'beanie'
44+
): Promise< void > => {
45+
await page.goto( `/product/${ productSlug }/` );
46+
await page.getByRole( 'button', { name: /add to cart/i } ).click();
47+
await page.waitForLoadState( 'networkidle' );
48+
};
49+
50+
export const fillCardDetails = async (
51+
page: Page,
52+
card = config.cards.basic
53+
): Promise< void > => {
54+
if (
55+
await page.$(
56+
'#payment .payment_method_woocommerce_payments .wcpay-upe-element'
57+
)
58+
) {
59+
const frameHandle = await page.waitForSelector(
60+
'#payment .payment_method_woocommerce_payments .wcpay-upe-element iframe'
61+
);
62+
63+
const stripeFrame = await frameHandle.contentFrame();
64+
65+
if ( ! stripeFrame ) return;
66+
67+
await stripeFrame.locator( '[name="number"]' ).fill( card.number );
68+
69+
await stripeFrame
70+
.locator( '[name="expiry"]' )
71+
.fill( card.expires.month + card.expires.year );
72+
73+
await stripeFrame.locator( '[name="cvc"]' ).fill( card.cvc );
74+
75+
const zip = stripeFrame.locator( '[name="postalCode"]' );
76+
77+
if ( await zip.isVisible() ) {
78+
await zip.fill( '90210' );
79+
}
80+
} else {
81+
const frameHandle = await page.waitForSelector(
82+
'#payment #wcpay-card-element iframe[name^="__privateStripeFrame"]'
83+
);
84+
const stripeFrame = await frameHandle.contentFrame();
85+
86+
if ( ! stripeFrame ) return;
87+
88+
await stripeFrame.locator( '[name="cardnumber"]' ).fill( card.number );
89+
90+
await stripeFrame
91+
.locator( '[name="exp-date"]' )
92+
.fill( card.expires.month + card.expires.year );
93+
94+
await stripeFrame.locator( '[name="cvc"]' ).fill( card.cvc );
95+
}
96+
};
97+
98+
export const confirmCardAuthentication = async (
99+
page: Page,
100+
cardType = '3DS',
101+
authorize = true
102+
): Promise< void > => {
103+
const target = authorize
104+
? '#test-source-authorize-3ds'
105+
: '#test-source-fail-3ds';
106+
107+
// Stripe card input also uses __privateStripeFrame as a prefix, so need to make sure we wait for an iframe that
108+
// appears at the top of the DOM.
109+
const frameHandle = await page.waitForSelector(
110+
'body>div>iframe[name^="__privateStripeFrame"]'
111+
);
112+
const stripeFrame = await frameHandle.contentFrame();
113+
if ( ! stripeFrame ) return;
114+
115+
const challengeFrameHandle = await stripeFrame.waitForSelector(
116+
'iframe#challengeFrame'
117+
);
118+
let challengeFrame = await challengeFrameHandle.contentFrame();
119+
if ( ! challengeFrame ) return;
120+
121+
// 3DS 1 cards have another iframe enclosing the authorize form
122+
if ( cardType.toUpperCase() === '3DS' ) {
123+
const acsFrameHandle = await challengeFrame.waitForSelector(
124+
'iframe[name="acsFrame"]'
125+
);
126+
challengeFrame = await acsFrameHandle.contentFrame();
127+
}
128+
if ( ! challengeFrame ) return;
129+
// Need to wait for the CSS animations to complete.
130+
await page.waitForTimeout( 500 );
131+
const button = await challengeFrame.waitForSelector( target );
132+
await button.click();
133+
};

0 commit comments

Comments
 (0)