diff --git a/.changeset/curly-hats-cheer.md b/.changeset/curly-hats-cheer.md new file mode 100644 index 00000000000..58abdff061e --- /dev/null +++ b/.changeset/curly-hats-cheer.md @@ -0,0 +1,5 @@ +--- +'@clerk/testing': patch +--- + +Improve reliability of checkout testing helpers. diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index ed19032e2ba..84852a9d6b3 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -306,9 +306,9 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl .getByText(/Trial/i) .locator('xpath=..') .getByText(/Free trial/i), - ).toBeVisible(); + ).toBeVisible({ timeout: 15000 }); - await expect(u.po.page.getByText(/Trial ends/i)).toBeVisible(); + await expect(u.po.page.getByText(/Trial ends/i)).toBeVisible({ timeout: 15000 }); await u.po.page.getByRole('button', { name: 'Manage' }).first().click(); await u.po.subscriptionDetails.waitForMounted(); @@ -573,8 +573,10 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl await u.po.checkout.waitForMounted(); await u.po.checkout.closeDrawer(); - await u.po.checkout.waitForMounted(); + // Ensure the checkout gets hidden before opening again to force revalidation + await u.po.checkout.root.waitFor({ state: 'hidden' }); await u.po.pricingTable.startCheckout({ planSlug: 'plus', period: 'monthly' }); + await u.po.checkout.waitForMounted(); await u.po.checkout.fillTestCard(); await u.po.checkout.clickPayOrSubscribe(); await u.po.checkout.confirmAndContinue(); diff --git a/packages/testing/src/playwright/unstable/page-objects/checkout.ts b/packages/testing/src/playwright/unstable/page-objects/checkout.ts index 6e30931c653..5ce70782e2a 100644 --- a/packages/testing/src/playwright/unstable/page-objects/checkout.ts +++ b/packages/testing/src/playwright/unstable/page-objects/checkout.ts @@ -6,7 +6,7 @@ export const createCheckoutPageObject = (testArgs: { page: EnhancedPage }) => { const self = { ...common(testArgs), waitForMounted: (selector = '.cl-checkout-root') => { - return page.waitForSelector(selector, { state: 'attached' }); + return page.waitForSelector(selector, { state: 'attached', timeout: 20000 }); }, closeDrawer: () => { return page.locator('.cl-drawerClose').click(); @@ -21,6 +21,7 @@ export const createCheckoutPageObject = (testArgs: { page: EnhancedPage }) => { }); }, fillCard: async (card: { number: string; expiration: string; cvc: string; country: string; zip: string }) => { + await self.waitForStripeElements({ state: 'visible' }); const frame = page.frameLocator('iframe[src*="elements-inner-payment"]'); await frame.getByLabel('Card number').fill(card.number); await frame.getByLabel('Expiration date').fill(card.expiration); @@ -29,7 +30,19 @@ export const createCheckoutPageObject = (testArgs: { page: EnhancedPage }) => { await frame.getByLabel('ZIP code').fill(card.zip); }, waitForStripeElements: async ({ state = 'visible' }: { state?: 'visible' | 'hidden' } = {}) => { - return page.frameLocator('iframe[src*="elements-inner-payment"]').getByLabel('Card number').waitFor({ state }); + const iframe = page.locator('iframe[src*="elements-inner-payment"]'); + if (state === 'visible') { + await iframe.waitFor({ state: 'attached', timeout: 20000 }); + await page.frameLocator('iframe[src*="elements-inner-payment"]').getByLabel('Card number').waitFor({ + state: 'visible', + timeout: 20000, + }); + } else { + await page.frameLocator('iframe[src*="elements-inner-payment"]').getByLabel('Card number').waitFor({ + state: 'hidden', + timeout: 20000, + }); + } }, clickPayOrSubscribe: async () => { await self.root.getByRole('button', { name: /subscribe|pay\s\$|start/i }).click();