Skip to content

Commit

Permalink
Add more e2e tests
Browse files Browse the repository at this point in the history
  • Loading branch information
annemirasol committed Feb 26, 2025
1 parent a27f2d6 commit ae4a44f
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 18 deletions.
64 changes: 63 additions & 1 deletion tests/e2e/tests/checkout/shortcode/retries.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
setupCart,
setupShortcodeCheckout,
fillCreditCardDetailsShortcode,
handleCheckout3DSChallenge,
} = payments;

test.beforeAll( 'enable Cash App Pay', async ( { browser } ) => {
Expand All @@ -25,7 +26,17 @@ test.beforeAll( 'enable Cash App Pay', async ( { browser } ) => {
await expect( page.getByLabel( 'Cash App Pay' ) ).toBeChecked();
} );

test( 'customer can retry payment @smoke', async ( { page } ) => {
/**
* When retrying payments, we will reuse a compatible payment intent, if the order already has one.
* In addition, the payment method ID is included when generating the idempotency key
* when creating a payment intent.
*
* This test verifies that the same payment method type can be used when retrying a payment, e.g.
* chaging from one credit card to another.
*/
test( 'customer can retry payment, with a different card @smoke', async ( {
page,
} ) => {
await emptyCart( page );
await setupCart( page );
await setupShortcodeCheckout(
Expand Down Expand Up @@ -56,6 +67,57 @@ test( 'customer can retry payment @smoke', async ( { page } ) => {
);
} );

/**
* When retrying payments, we will reuse a compatible payment intent, if the order already has one.
* In addition, the payment method ID is included when generating the idempotency key
* when creating a payment intent.
*
* This test verifies that the same payment method type can be used when retrying the same payment,
* after changing the billing details.
*/
test( 'customer can retry payment, with changed billing details @smoke', async ( {
page,
} ) => {
await emptyCart( page );
await setupCart( page );
await setupShortcodeCheckout(
page,
config.get( 'addresses.customer.billing' )
);
await fillCreditCardDetailsShortcode( page, config.get( 'cards.3ds' ) );
await page
.getByRole( 'button', { name: 'Place order' } )
.dispatchEvent( 'click' );

// Fail the 3DS challenge
await handleCheckout3DSChallenge( page, 'fail' );

// Change billing details
await page.fill( '#billing_postcode', '12345' );

// Retry the payment
await page
.getByRole( 'button', { name: 'Place order' } )
.dispatchEvent( 'click' );

// Complete the 3DS challenge
await handleCheckout3DSChallenge( page );

// Expect the order to succeed
await page.waitForURL( '**/order-received/**' );

// Expect the order to succeed
await expect( page.locator( 'h1.entry-title' ) ).toHaveText(
'Order received'
);
} );

/**
* The idempotency key for creating a payment intent includes the payment method ID.
*
* This test verifies that a different payment method type can be used when retrying a payment
* for the same order.
*/
test( 'customer can retry payment, using a different payment method @smoke', async ( {
page,
} ) => {
Expand Down
20 changes: 3 additions & 17 deletions tests/e2e/tests/checkout/shortcode/sca-card.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ const {
setupCart,
setupShortcodeCheckout,
fillCreditCardDetailsShortcode,
handleCheckout3DSChallenge,
} = payments;

test( 'customer can checkout with a SCA card @smoke', async ( { page } ) => {
Expand All @@ -19,23 +20,8 @@ test( 'customer can checkout with a SCA card @smoke', async ( { page } ) => {
await fillCreditCardDetailsShortcode( page, config.get( 'cards.3ds' ) );
await page.locator( 'text=Place order' ).dispatchEvent( 'click' );

// Wait until the SCA frame is available
while (
! page.frame( {
name: 'stripe-challenge-frame',
} )
) {
await page.waitForTimeout( 1000 );
}
// Not ideal, but the iframe body gets repalced after load, so a waitFor does not work here.
await page.waitForTimeout( 2000 );

await page
.frame( {
name: 'stripe-challenge-frame',
} )
.getByRole( 'button', { name: 'Complete' } )
.click();
// Complete the 3DS challenge
await handleCheckout3DSChallenge( page );

await page.waitForURL( '**/checkout/order-received/**' );

Expand Down
33 changes: 33 additions & 0 deletions tests/e2e/utils/payments.js
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,36 @@ export async function setupBlocksCheckout( page, billingDetails = null ) {
)
.click();
}

/**
* Handles the 3DS challenge on the checkout page.
* @param {Page} page Playwright page fixture.
* @param {string} action The action to take on the challenge modal.
*/
export async function handleCheckout3DSChallenge( page, action = 'authorize' ) {
const outerFrameLocator = page
.locator( 'iframe[name^="__privateStripeFrame"]' )
.first()
.contentFrame();
const innerFrameLocator = outerFrameLocator.frameLocator(
'iframe[name="stripe-challenge-frame"]'
);

// Wait for the challenge modal to be ready -- the inner frame is "visible"
// and the loading indicator is hidden.
await innerFrameLocator.owner().waitFor();
await outerFrameLocator
.locator( '.LightboxModalLoadingIndicator' )
.waitFor( { state: 'hidden' } );

const buttonId =
action === 'authorize'
? '#test-source-authorize-3ds'
: '#test-source-fail-3ds';
await innerFrameLocator.locator( buttonId ).waitFor( { state: 'visible' } );
await innerFrameLocator.locator( buttonId ).click();

if ( action === 'fail' ) {
await innerFrameLocator.owner().waitFor( { state: 'detached' } );
}
}

0 comments on commit ae4a44f

Please sign in to comment.