Skip to content

Commit 35a8cae

Browse files
authored
chore(e2e): E2e tests for trials (#6607)
1 parent 453cf86 commit 35a8cae

File tree

4 files changed

+113
-2
lines changed

4 files changed

+113
-2
lines changed

.changeset/thick-onions-strive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@clerk/testing': patch
3+
---
4+
5+
Update click events to support labels related to trials.

integration/tests/pricing-table.test.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl
6262
await u.po.signIn.waitForMounted();
6363
await expect(u.po.page.getByText('Checkout')).toBeHidden();
6464
});
65+
66+
test('when signed out, clicking trial plan redirects to sign in', async ({ page, context }) => {
67+
const u = createTestUtils({ app, page, context });
68+
await u.po.page.goToRelative('/pricing-table');
69+
70+
await u.po.pricingTable.waitForMounted();
71+
await expect(u.po.page.getByText(/Start \d+-day free trial/i)).toBeVisible();
72+
await u.po.pricingTable.startCheckout({ planSlug: 'trial' });
73+
74+
await u.po.signIn.waitForMounted();
75+
await expect(u.po.page.getByText('Checkout')).toBeHidden();
76+
});
6577
});
6678

6779
test.describe('when signed in flow', () => {
@@ -243,6 +255,96 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl
243255
await newFakeUser.deleteIfExists();
244256
});
245257

258+
test('starts free trial subscription for new user', async ({ page, context }) => {
259+
const u = createTestUtils({ app, page, context });
260+
261+
// Create a new user specifically for this trial test
262+
const trialUser = u.services.users.createFakeUser();
263+
await u.services.users.createBapiUser(trialUser);
264+
265+
try {
266+
// Sign in the new user
267+
await u.po.signIn.goTo();
268+
await u.po.signIn.signInWithEmailAndInstantPassword({
269+
email: trialUser.email,
270+
password: trialUser.password,
271+
});
272+
273+
// Navigate to pricing table
274+
await u.po.page.goToRelative('/pricing-table');
275+
await u.po.pricingTable.waitForMounted();
276+
277+
// Verify trial plan is displayed with trial CTA
278+
// Note: This assumes there's a plan with trial enabled in the test environment
279+
// The button text should show "Start [X]-day free trial" for trial-enabled plans
280+
await expect(u.po.page.getByText(/Start \d+-day free trial/i)).toBeVisible();
281+
282+
// Start checkout for a trial plan (assuming 'pro' has trial enabled in test env)
283+
await u.po.pricingTable.startCheckout({ planSlug: 'trial' });
284+
await u.po.checkout.waitForMounted();
285+
286+
// Verify checkout shows trial details
287+
await expect(u.po.checkout.root.getByText('Checkout')).toBeVisible();
288+
await expect(u.po.checkout.root.getByText('Free trial')).toBeVisible();
289+
await expect(u.po.checkout.root.getByText('Total Due after')).toBeVisible();
290+
291+
await u.po.checkout.fillTestCard();
292+
await u.po.checkout.clickPayOrSubscribe();
293+
294+
await expect(u.po.checkout.root.getByText(/Trial.*successfully.*started/i)).toBeVisible();
295+
await u.po.checkout.confirmAndContinue();
296+
297+
await u.po.page.goToRelative('/pricing-table');
298+
await u.po.pricingTable.waitForMounted();
299+
300+
// Verify the user is now shown as having an active free trial
301+
// The pricing table should show their current plan as active
302+
await u.po.pricingTable.waitToBeActive({ planSlug: 'trial' });
303+
304+
await u.po.page.goToRelative('/user');
305+
await u.po.userProfile.waitForMounted();
306+
await u.po.userProfile.switchToBillingTab();
307+
308+
await expect(
309+
u.po.page
310+
.locator('.cl-profileSectionContent__subscriptionsList')
311+
.getByText(/Trial/i)
312+
.locator('xpath=..')
313+
.getByText(/Free trial/i),
314+
).toBeVisible();
315+
316+
await expect(u.po.page.getByText(/Trial ends/i)).toBeVisible();
317+
318+
await u.po.page.getByRole('button', { name: 'Manage subscription' }).first().click();
319+
await u.po.subscriptionDetails.waitForMounted();
320+
await u.po.subscriptionDetails.root.locator('.cl-menuButtonEllipsisBordered').click();
321+
await u.po.subscriptionDetails.root.getByText('Cancel free trial').click();
322+
await u.po.subscriptionDetails.root.locator('.cl-drawerConfirmationRoot').waitFor({ state: 'visible' });
323+
await u.po.subscriptionDetails.root.getByRole('button', { name: 'Cancel free trial' }).click();
324+
await u.po.subscriptionDetails.waitForUnmounted();
325+
326+
await expect(
327+
u.po.page
328+
.locator('.cl-profileSectionContent__subscriptionsList')
329+
.getByText(/Trial/i)
330+
.locator('xpath=..')
331+
.getByText(/Free trial/i),
332+
).toBeVisible();
333+
334+
// Verify the Free plan with Upcoming status exists
335+
await expect(
336+
u.po.page
337+
.locator('.cl-profileSectionContent__subscriptionsList')
338+
.getByText('Free')
339+
.locator('xpath=..')
340+
.getByText('Upcoming'),
341+
).toBeVisible();
342+
} finally {
343+
// Clean up the trial user
344+
await trialUser.deleteIfExists();
345+
}
346+
});
347+
246348
test.describe('in UserProfile', () => {
247349
// test.describe.configure({ mode: 'serial' });
248350
test('renders pricing table, subscribes to a plan, revalidates payment sources on complete and then downgrades to free', async ({

packages/testing/src/playwright/unstable/page-objects/checkout.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export const createCheckoutPageObject = (testArgs: { page: EnhancedPage }) => {
3232
return page.frameLocator('iframe[src*="elements-inner-payment"]').getByLabel('Card number').waitFor({ state });
3333
},
3434
clickPayOrSubscribe: async () => {
35-
await self.root.getByRole('button', { name: /subscribe|pay\s\$/i }).click();
35+
await self.root.getByRole('button', { name: /subscribe|pay\s\$|start/i }).click();
3636
},
3737
waitForSubscribeButton: async () => {
3838
await self.root.getByRole('button', { name: /^subscribe$/i }).waitFor({ state: 'visible' });

packages/testing/src/playwright/unstable/page-objects/pricingTable.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,11 @@ export const createPricingTablePageObject = (testArgs: { page: EnhancedPage }) =
7474
period?: BillingPeriod;
7575
}) => {
7676
const targetButtonName =
77-
shouldSwitch === true ? 'Switch to this plan' : shouldSwitch === false ? /subscribe/i : /get|switch|subscribe/i;
77+
shouldSwitch === true
78+
? 'Switch to this plan'
79+
: shouldSwitch === false
80+
? /subscribe/i
81+
: /get|switch|subscribe|Start \d+-day free trial/i;
7882

7983
if (period) {
8084
await ensurePricingPeriod(planSlug, period);

0 commit comments

Comments
 (0)