Skip to content

Commit

Permalink
Migrate the Shopper Renew Subscription E2E test to Playwright (#10144)
Browse files Browse the repository at this point in the history
  • Loading branch information
eduardoumpierre authored Jan 14, 2025
1 parent 95473ac commit b763f99
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 97 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: dev

Migrate the Shopper Renew Subscription spec to Playwright and remove the corresponding Puppeteer test.
16 changes: 8 additions & 8 deletions tests/e2e-pw/config/default.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,25 +76,25 @@ export const config = {
},
'subscriptions-customer': {
billing: {
first_name: 'I am',
last_name: 'Subscriptions Customer',
firstname: 'I am',
lastname: 'Subscriptions Customer',
company: 'Automattic',
country: 'United States (US)',
address_1: '60 29th Street #343',
address_2: 'billing',
addressfirstline: '60 29th Street #343',
addresssecondline: 'billing',
city: 'San Francisco',
state: 'CA',
postcode: '94110',
phone: '123456789',
email: '[email protected]',
},
shipping: {
first_name: 'I am',
last_name: 'Subscriptions Recipient',
firstname: 'I am',
lastname: 'Subscriptions Recipient',
company: 'Automattic',
country: 'United States (US)',
address_1: '60 29th Street #343',
address_2: 'shipping',
addressfirstline: '60 29th Street #343',
addresssecondline: 'shipping',
city: 'San Francisco',
state: 'CA',
postcode: '94110',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/**
* External dependencies
*/
import { test, expect, Page } from '@playwright/test';

/**
* Internal dependencies
*/
import { config } from '../../config/default';
import { describeif, getAnonymousShopper } from '../../utils/helpers';
import * as shopper from '../../utils/shopper';
import * as navigation from '../../utils/shopper-navigation';
import { products, shouldRunSubscriptionsTests } from '../../utils/constants';
import RestAPI from '../../utils/rest-api';

describeif( shouldRunSubscriptionsTests )(
'Subscriptions > Renew a subscription in my account',
() => {
const customerBillingConfig =
config.addresses[ 'subscriptions-customer' ].billing;

let subscriptionId: string;
let page: Page;

test.beforeAll( async ( { browser }, { project } ) => {
const restApi = new RestAPI( project.use.baseURL );
await restApi.deleteCustomerByEmailAddress(
customerBillingConfig.email
);

const { shopperPage } = await getAnonymousShopper( browser );
page = shopperPage;
} );

test( 'should be able to purchase a subscription', async () => {
await shopper.addCartProduct(
page,
products.SUBSCRIPTION_SIGNUP_FEE
);
await shopper.setupCheckout( page, customerBillingConfig );
await shopper.fillCardDetails( page, config.cards.basic );
await shopper.placeOrder( page );
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();

subscriptionId = await page
.getByLabel( 'View subscription number' )
.innerText();
} );

test( 'should be able to renew a subscription in my account', async () => {
await navigation.goToSubscriptions( page );

if ( ! subscriptionId ) {
throw new Error( 'Subscription ID is not set' );
}

const numericSubscriptionId = subscriptionId.substring( 1 );

await page
.getByLabel(
`View subscription number ${ numericSubscriptionId }`
)
.click();

await page.getByText( 'Renew now' ).click();
await page
.getByText( 'Complete checkout to renew now.' )
.isVisible();
await shopper.focusPlaceOrderButton( page );
await shopper.placeOrder( page );
await expect(
page.getByRole( 'heading', { name: 'Order received' } )
).toBeVisible();
} );
}
);
6 changes: 6 additions & 0 deletions tests/e2e-pw/utils/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const shouldRunSubscriptionsTests =
process.env.SKIP_WC_SUBSCRIPTIONS_TESTS !== '1';

export const products = {
SUBSCRIPTION_SIGNUP_FEE: 70,
};
6 changes: 6 additions & 0 deletions tests/e2e-pw/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,9 @@ export const getAnonymousShopper = async (
const shopperPage = await shopperContext.newPage();
return { shopperPage, shopperContext };
};

/**
* Conditionally determine whether or not to skip a test suite.
*/
export const describeif = ( condition: boolean ) =>
condition ? test.describe : test.describe.skip;
68 changes: 68 additions & 0 deletions tests/e2e-pw/utils/rest-api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* External dependencies
*/
import { HTTPClientFactory } from '@woocommerce/api';

/**
* Internal dependencies
*/
import { config } from '../config/default';

const userEndpoint = '/wp/v2/users';

class RestAPI {
private baseUrl: string;

constructor( baseUrl: string ) {
if ( ! baseUrl ) {
throw new Error( 'Base URL is required.' );
}
this.baseUrl = baseUrl;
}

private getAdminClient() {
return HTTPClientFactory.build( this.baseUrl )
.withBasicAuth(
config.users.admin.username,
config.users.admin.password
)
.create();
}

/**
* Deletes a customer account by their email address if the user exists.
*
* Copied from https://github.com/woocommerce/woocommerce/blob/trunk/packages/js/e2e-utils/src/flows/with-rest-api.js#L374
*
* @param {string} emailAddress Customer user account email address.
* @return {Promise<void>}
*/
async deleteCustomerByEmailAddress(
emailAddress: string
): Promise< void > {
const client = this.getAdminClient();

const query = {
search: emailAddress,
context: 'edit',
};
const customers = await client.get( userEndpoint, query );

if ( customers.data && customers.data.length ) {
for ( let c = 0; c < customers.data.length; c++ ) {
const deleteUser = {
id: customers.data[ c ].id,
force: true,
reassign: 1,
};

await client.delete(
`${ userEndpoint }/${ deleteUser.id }`,
deleteUser
);
}
}
}
}

export default RestAPI;
5 changes: 5 additions & 0 deletions tests/e2e-pw/utils/shopper-navigation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,8 @@ export const goToOrder = async ( page: Page, orderId: string ) => {
waitUntil: 'load',
} );
};

export const goToSubscriptions = ( page: Page ) =>
page.goto( '/my-account/subscriptions/', {
waitUntil: 'load',
} );
28 changes: 20 additions & 8 deletions tests/e2e-pw/utils/shopper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,24 @@ export const isUIUnblocked = async ( page: Page ) => {
await expect( page.locator( '.blockUI' ) ).toHaveCount( 0 );
};

/**
* Waits for the UI to refresh after a user interaction.
*
* Woo core blocks and refreshes the UI after 1s after each key press
* in a text field or immediately after a select field changes.
* We need to wait to make sure that all key presses were processed by that mechanism.
*/
export const waitForUiRefresh = ( page: Page ) => page.waitForTimeout( 1000 );

/**
* Takes off the focus out of the Stripe elements to let Stripe logic
* wrap up and make sure the Place Order button is clickable.
*/
export const focusPlaceOrderButton = async ( page: Page ) => {
await page.locator( '#place_order' ).focus();
await waitForUiRefresh( page );
};

export const fillBillingAddress = async (
page: Page,
billingAddress: CustomerAddress
Expand Down Expand Up @@ -188,10 +206,7 @@ export const setupCheckout = async (
) => {
await navigation.goToCheckout( page );
await fillBillingAddress( page, billingAddress );
// Woo core blocks and refreshes the UI after 1s after each key press
// in a text field or immediately after a select field changes.
// We need to wait to make sure that all key presses were processed by that mechanism.
await page.waitForTimeout( 1000 );
await waitForUiRefresh( page );
await isUIUnblocked( page );
await page
.locator( '.wc_payment_method.payment_method_woocommerce_payments' )
Expand Down Expand Up @@ -252,10 +267,7 @@ export const placeOrderWithCurrency = async (
await navigation.goToShopWithCurrency( page, currency );
await setupProductCheckout( page, [ [ config.products.simple.name, 1 ] ] );
await fillCardDetails( page, config.cards.basic );
// Takes off the focus out of the Stripe elements to let Stripe logic
// wrap up and make sure the Place Order button is clickable.
await page.locator( '#place_order' ).focus();
await page.waitForTimeout( 1000 );
await focusPlaceOrderButton( page );
await placeOrder( page );
await page.waitForURL( /\/order-received\//, { waitUntil: 'load' } );
await expect(
Expand Down

This file was deleted.

0 comments on commit b763f99

Please sign in to comment.