From d066ed3a40b229371902216990d5417bfddb3a86 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sun, 9 Nov 2025 18:06:46 +0200 Subject: [PATCH 1/4] tests for statements and payment attempts --- integration/tests/pricing-table.test.ts | 104 ++++++++++++++++++ .../playwright/unstable/page-objects/index.ts | 4 + .../unstable/page-objects/paymentAttempt.ts | 35 ++++++ .../unstable/page-objects/statement.ts | 38 +++++++ .../unstable/page-objects/userProfile.ts | 34 ++++++ 5 files changed, 215 insertions(+) create mode 100644 packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts create mode 100644 packages/testing/src/playwright/unstable/page-objects/statement.ts diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index ee3ff03c024..eb159927f85 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -655,6 +655,110 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl await fakeUser.deleteIfExists(); }); + test('displays billing history and navigates through statement and payment attempt details', async ({ + page, + context, + }) => { + const u = createTestUtils({ app, page, context }); + + const fakeUser = u.services.users.createFakeUser(); + await u.services.users.createBapiUser(fakeUser); + + try { + await u.po.signIn.goTo(); + await u.po.signIn.signInWithEmailAndInstantPassword({ email: fakeUser.email, password: fakeUser.password }); + + await u.po.page.goToRelative('/user'); + await u.po.userProfile.waitForMounted(); + await u.po.userProfile.switchToBillingTab(); + + await u.po.userProfile.openBillingStatementsTab(); + await u.po.userProfile.waitForBillingTableRows(); + await expect(u.po.userProfile.getBillingEmptyStateMessage('No statements to display')).toBeVisible(); + + await u.po.page.goToRelative('/user'); + await u.po.userProfile.waitForMounted(); + await u.po.userProfile.switchToBillingTab(); + await u.po.page.getByRole('button', { name: 'Switch plans' }).click(); + + await u.po.pricingTable.waitForMounted(); + await u.po.pricingTable.startCheckout({ planSlug: 'plus' }); + await u.po.checkout.waitForMounted(); + await u.po.checkout.fillTestCard(); + await u.po.checkout.clickPayOrSubscribe(); + await expect(u.po.page.getByText('Payment was successful!')).toBeVisible({ + timeout: 15000, + }); + await u.po.checkout.confirmAndContinue(); + + await u.po.pricingTable.startCheckout({ planSlug: 'pro', shouldSwitch: true }); + await u.po.checkout.waitForMounted(); + await u.po.checkout.root.getByText('Add payment method').click(); + await u.po.checkout.fillCard({ + number: '4100000000000019', + expiration: '1234', + cvc: '123', + country: 'United States', + zip: '12345', + }); + await u.po.checkout.clickPayOrSubscribe(); + await expect(u.po.checkout.root.getByText('The card was declined.').first()).toBeVisible({ + timeout: 15000, + }); + await u.po.checkout.closeDrawer(); + + await u.po.page.goToRelative('/user'); + await u.po.userProfile.waitForMounted(); + await u.po.userProfile.switchToBillingTab(); + + await u.po.userProfile.openBillingStatementsTab(); + const date = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long' }); + await u.po.userProfile.waitForBillingTableRows({ hasText: new RegExp(date, 'i') }); + // await u.po.userProfile.waitForBillingTableRows(); + await expect(u.po.userProfile.getBillingEmptyStateMessage('No statements to display')).toBeHidden(); + + const firstStatementRow = u.po.userProfile.getActiveBillingTableRows().first(); + await firstStatementRow.click(); + + await u.po.statement.waitForMounted(); + await expect(u.po.statement.getPlanLineItems().filter({ hasText: /Plus/i }).first()).toBeVisible(); + + const statementTotalText = (await u.po.statement.getTotalPaidValue().textContent())?.trim(); + expect(statementTotalText).toBeTruthy(); + + await u.po.statement.clickViewPaymentButton(); + await u.po.paymentAttempt.waitForMounted(); + await expect(u.po.paymentAttempt.getStatusBadge()).toHaveText(/paid/i); + + const paymentTotalText = (await u.po.paymentAttempt.getTotalAmount().textContent())?.trim(); + expect(paymentTotalText).toBe(statementTotalText); + + await expect(u.po.paymentAttempt.getLineItemTitles().filter({ hasText: /Plus/i }).first()).toBeVisible(); + + await u.po.paymentAttempt.goBackToPaymentsList(); + await u.po.userProfile.waitForMounted(); + await u.po.userProfile.waitForBillingTableRows({ hasText: /paid/i }); + + await u.po.userProfile.openBillingPaymentsTab(); + await u.po.userProfile.waitForBillingTableRows({ hasText: /Failed/i }); + await expect(u.po.userProfile.getBillingEmptyStateMessage('No payment history')).toBeHidden(); + + const failedPaymentRow = u.po.userProfile + .getActiveBillingTableRows() + .filter({ hasText: /Failed/i }) + .first(); + await failedPaymentRow.click(); + + await u.po.paymentAttempt.waitForMounted(); + await expect(u.po.paymentAttempt.getStatusBadge()).toHaveText(/failed/i); + await expect(u.po.paymentAttempt.getLineItemTitles().filter({ hasText: /Pro/i }).first()).toBeVisible(); + + await u.po.paymentAttempt.goBackToPaymentsList(); + } finally { + await fakeUser.deleteIfExists(); + } + }); + test('adds two payment methods and sets the last as default', async ({ page, context }) => { const u = createTestUtils({ app, page, context }); diff --git a/packages/testing/src/playwright/unstable/page-objects/index.ts b/packages/testing/src/playwright/unstable/page-objects/index.ts index 01c5836e8ad..832ea333d20 100644 --- a/packages/testing/src/playwright/unstable/page-objects/index.ts +++ b/packages/testing/src/playwright/unstable/page-objects/index.ts @@ -9,6 +9,8 @@ import { createImpersonationPageObject } from './impersonation'; import { createKeylessPopoverPageObject } from './keylessPopover'; import { createOrganizationSwitcherComponentPageObject } from './organizationSwitcher'; import { createPlanDetailsPageObject } from './planDetails'; +import { createStatementPageObject } from './statement'; +import { createPaymentAttemptPageObject } from './paymentAttempt'; import { createPricingTablePageObject } from './pricingTable'; import { createSessionTaskComponentPageObject } from './sessionTask'; import { createSignInComponentPageObject } from './signIn'; @@ -42,6 +44,8 @@ export const createPageObjects = ({ keylessPopover: createKeylessPopoverPageObject(testArgs), organizationSwitcher: createOrganizationSwitcherComponentPageObject(testArgs), pricingTable: createPricingTablePageObject(testArgs), + statement: createStatementPageObject(testArgs), + paymentAttempt: createPaymentAttemptPageObject(testArgs), sessionTask: createSessionTaskComponentPageObject(testArgs), signIn: createSignInComponentPageObject(testArgs), signUp: createSignUpComponentPageObject(testArgs), diff --git a/packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts b/packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts new file mode 100644 index 00000000000..dbc06829402 --- /dev/null +++ b/packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts @@ -0,0 +1,35 @@ +import type { EnhancedPage } from './app'; +import { common } from './common'; + +export const createPaymentAttemptPageObject = (testArgs: { page: EnhancedPage }) => { + const { page } = testArgs; + const root = page.locator('.cl-paymentAttemptRoot'); + const self = { + ...common(testArgs), + waitForMounted: () => { + return root.waitFor({ state: 'visible', timeout: 15000 }); + }, + waitForUnmounted: () => { + return root.waitFor({ state: 'detached', timeout: 15000 }); + }, + goBackToPaymentsList: async () => { + await Promise.all([ + page.waitForURL(/tab=payments/, { timeout: 15000 }), + page.getByRole('link', { name: /Payments/i }).click(), + ]); + await root.waitFor({ state: 'detached', timeout: 15000 }).catch(() => {}); + }, + getStatusBadge: () => { + return self.root.locator('.cl-paymentAttemptHeaderBadge'); + }, + getTotalAmount: () => { + return self.root.locator('.cl-paymentAttemptFooterValue'); + }, + getLineItemTitles: () => { + return self.root.locator('.cl-lineItemsTitle'); + }, + root, + }; + + return self; +}; diff --git a/packages/testing/src/playwright/unstable/page-objects/statement.ts b/packages/testing/src/playwright/unstable/page-objects/statement.ts new file mode 100644 index 00000000000..2d3deb5059d --- /dev/null +++ b/packages/testing/src/playwright/unstable/page-objects/statement.ts @@ -0,0 +1,38 @@ +import type { EnhancedPage } from './app'; +import { common } from './common'; + +export const createStatementPageObject = (testArgs: { page: EnhancedPage }) => { + const { page } = testArgs; + const root = page.locator('.cl-statementRoot'); + const self = { + ...common(testArgs), + waitForMounted: () => { + return root.waitFor({ state: 'visible', timeout: 15000 }); + }, + waitForUnmounted: () => { + return root.waitFor({ state: 'detached', timeout: 15000 }); + }, + goBackToStatementsList: async () => { + await Promise.all([ + page.waitForURL(/tab=statements/, { timeout: 15000 }), + page.getByRole('link', { name: /Statements/i }).click(), + ]); + await root.waitFor({ state: 'detached', timeout: 15000 }).catch(() => {}); + }, + clickViewPaymentButton: async () => { + await self.root + .getByRole('button', { name: /View payment/i }) + .first() + .click(); + }, + getPlanLineItems: () => { + return self.root.locator('.cl-statementSectionContentDetailsHeaderTitle'); + }, + getTotalPaidValue: () => { + return self.root.locator('.cl-statementFooterValue'); + }, + root, + }; + + return self; +}; diff --git a/packages/testing/src/playwright/unstable/page-objects/userProfile.ts b/packages/testing/src/playwright/unstable/page-objects/userProfile.ts index f80b591e7c3..adf2d38dc43 100644 --- a/packages/testing/src/playwright/unstable/page-objects/userProfile.ts +++ b/packages/testing/src/playwright/unstable/page-objects/userProfile.ts @@ -17,6 +17,40 @@ export const createUserProfileComponentPageObject = (testArgs: { page: EnhancedP switchToBillingTab: async () => { await page.getByText(/Billing/i).click(); }, + openBillingStatementsTab: async () => { + await page.getByRole('tab', { name: /Statements/i }).click(); + await self.waitForActiveBillingTabPanel(); + }, + openBillingPaymentsTab: async () => { + await page.getByRole('tab', { name: /Payments/i }).click(); + await self.waitForActiveBillingTabPanel(); + }, + waitForActiveBillingTabPanel: () => { + return page.locator('.cl-userProfile-root .cl-profilePage .cl-table').waitFor({ + state: 'visible', + timeout: 15000, + }); + }, + getActiveBillingTableRows: () => { + return page.locator('.cl-userProfile-root .cl-profilePage .cl-tableBody .cl-tableRow'); + }, + waitForBillingTableRows: async (options?: { hasText?: string | RegExp }) => { + const rows = self.getActiveBillingTableRows(); + if (options?.hasText) { + await rows + .filter({ + hasText: options.hasText, + }) + .first() + .waitFor({ state: 'visible', timeout: 15000 }); + } else { + await rows.first().waitFor({ state: 'visible', timeout: 15000 }); + } + return rows; + }, + getBillingEmptyStateMessage: (text: string | RegExp) => { + return page.locator('.cl-userProfile-root .cl-table').getByText(text); + }, waitForMounted: () => { return page.waitForSelector('.cl-userProfile-root', { state: 'attached' }); }, From 75ce4e62de07e5a767725b3453f1038aab29010e Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sun, 9 Nov 2025 18:24:51 +0200 Subject: [PATCH 2/4] keep it minimal --- integration/tests/pricing-table.test.ts | 109 +++++++++++++----- .../playwright/unstable/page-objects/index.ts | 4 - .../unstable/page-objects/paymentAttempt.ts | 35 ------ .../unstable/page-objects/statement.ts | 38 ------ .../unstable/page-objects/userProfile.ts | 34 ------ 5 files changed, 80 insertions(+), 140 deletions(-) delete mode 100644 packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts delete mode 100644 packages/testing/src/playwright/unstable/page-objects/statement.ts diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index eb159927f85..9865bba8ef1 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -672,9 +672,53 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl await u.po.userProfile.waitForMounted(); await u.po.userProfile.switchToBillingTab(); - await u.po.userProfile.openBillingStatementsTab(); - await u.po.userProfile.waitForBillingTableRows(); - await expect(u.po.userProfile.getBillingEmptyStateMessage('No statements to display')).toBeVisible(); + const openBillingTab = async (label: RegExp) => { + await page.getByRole('tab', { name: label }).click(); + await page + .locator('.cl-userProfile-root [role="tabpanel"] .cl-table') + .waitFor({ state: 'visible', timeout: 15000 }); + }; + const getBillingTableRows = () => { + return page.locator('.cl-userProfile-root .cl-tableBody .cl-tableRow'); + }; + const waitForBillingTableRows = async (options?: { hasText?: string | RegExp }) => { + const rows = getBillingTableRows(); + if (options?.hasText) { + await rows + .filter({ + hasText: options.hasText, + }) + .first() + .waitFor({ state: 'visible', timeout: 15000 }); + } else { + await rows.first().waitFor({ state: 'visible', timeout: 15000 }); + } + return rows; + }; + const getBillingEmptyStateMessage = (text: string | RegExp) => { + return page.locator('.cl-userProfile-root .cl-table').getByText(text); + }; + const waitForStatementPage = async () => { + const statementRoot = page.locator('.cl-statementRoot'); + await statementRoot.waitFor({ state: 'visible', timeout: 15000 }); + return statementRoot; + }; + const waitForPaymentAttemptPage = async () => { + const paymentAttemptRoot = page.locator('.cl-paymentAttemptRoot'); + await paymentAttemptRoot.waitFor({ state: 'visible', timeout: 15000 }); + return paymentAttemptRoot; + }; + const goBackToPaymentsList = async () => { + const paymentAttemptRoot = page.locator('.cl-paymentAttemptRoot'); + await Promise.all([ + page.waitForURL(/tab=payments/, { timeout: 15000 }), + page.getByRole('link', { name: /Payments/i }).click(), + ]); + await paymentAttemptRoot.waitFor({ state: 'detached', timeout: 15000 }).catch(() => {}); + }; + + await openBillingTab(/Statements/i); + await expect(getBillingEmptyStateMessage('No statements to display')).toBeVisible(); await u.po.page.goToRelative('/user'); await u.po.userProfile.waitForMounted(); @@ -711,49 +755,56 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl await u.po.userProfile.waitForMounted(); await u.po.userProfile.switchToBillingTab(); - await u.po.userProfile.openBillingStatementsTab(); + await openBillingTab(/Statements/i); const date = new Date().toLocaleDateString('en-US', { year: 'numeric', month: 'long' }); - await u.po.userProfile.waitForBillingTableRows({ hasText: new RegExp(date, 'i') }); - // await u.po.userProfile.waitForBillingTableRows(); - await expect(u.po.userProfile.getBillingEmptyStateMessage('No statements to display')).toBeHidden(); + await waitForBillingTableRows({ hasText: new RegExp(date, 'i') }); + await expect(getBillingEmptyStateMessage('No statements to display')).toBeHidden(); - const firstStatementRow = u.po.userProfile.getActiveBillingTableRows().first(); + const firstStatementRow = getBillingTableRows().first(); await firstStatementRow.click(); - await u.po.statement.waitForMounted(); - await expect(u.po.statement.getPlanLineItems().filter({ hasText: /Plus/i }).first()).toBeVisible(); + const statementRoot = await waitForStatementPage(); + await expect( + statementRoot.locator('.cl-statementSectionContentDetailsHeaderTitle').filter({ hasText: /Plus/i }).first(), + ).toBeVisible(); - const statementTotalText = (await u.po.statement.getTotalPaidValue().textContent())?.trim(); + const statementTotalText = (await statementRoot.locator('.cl-statementFooterValue').textContent())?.trim(); expect(statementTotalText).toBeTruthy(); - await u.po.statement.clickViewPaymentButton(); - await u.po.paymentAttempt.waitForMounted(); - await expect(u.po.paymentAttempt.getStatusBadge()).toHaveText(/paid/i); + await statementRoot + .getByRole('button', { name: /View payment/i }) + .first() + .click(); + const paymentAttemptRoot = await waitForPaymentAttemptPage(); + await expect(paymentAttemptRoot.locator('.cl-paymentAttemptHeaderBadge')).toHaveText(/paid/i); - const paymentTotalText = (await u.po.paymentAttempt.getTotalAmount().textContent())?.trim(); + const paymentTotalText = ( + await paymentAttemptRoot.locator('.cl-paymentAttemptFooterValue').textContent() + )?.trim(); expect(paymentTotalText).toBe(statementTotalText); - await expect(u.po.paymentAttempt.getLineItemTitles().filter({ hasText: /Plus/i }).first()).toBeVisible(); - - await u.po.paymentAttempt.goBackToPaymentsList(); - await u.po.userProfile.waitForMounted(); - await u.po.userProfile.waitForBillingTableRows({ hasText: /paid/i }); + await expect( + paymentAttemptRoot.locator('.cl-lineItemsTitle').filter({ hasText: /Plus/i }).first(), + ).toBeVisible(); - await u.po.userProfile.openBillingPaymentsTab(); - await u.po.userProfile.waitForBillingTableRows({ hasText: /Failed/i }); - await expect(u.po.userProfile.getBillingEmptyStateMessage('No payment history')).toBeHidden(); + await goBackToPaymentsList(); + await openBillingTab(/Payments/i); + await waitForBillingTableRows({ hasText: /paid/i }); + await waitForBillingTableRows({ hasText: /Failed/i }); + await expect(getBillingEmptyStateMessage('No payment history')).toBeHidden(); - const failedPaymentRow = u.po.userProfile - .getActiveBillingTableRows() + const failedPaymentRow = getBillingTableRows() .filter({ hasText: /Failed/i }) .first(); await failedPaymentRow.click(); - await u.po.paymentAttempt.waitForMounted(); - await expect(u.po.paymentAttempt.getStatusBadge()).toHaveText(/failed/i); - await expect(u.po.paymentAttempt.getLineItemTitles().filter({ hasText: /Pro/i }).first()).toBeVisible(); + const failedPaymentAttemptRoot = await waitForPaymentAttemptPage(); + await expect(failedPaymentAttemptRoot.locator('.cl-paymentAttemptHeaderBadge')).toHaveText(/failed/i); + await expect( + failedPaymentAttemptRoot.locator('.cl-lineItemsTitle').filter({ hasText: /Pro/i }).first(), + ).toBeVisible(); - await u.po.paymentAttempt.goBackToPaymentsList(); + await goBackToPaymentsList(); } finally { await fakeUser.deleteIfExists(); } diff --git a/packages/testing/src/playwright/unstable/page-objects/index.ts b/packages/testing/src/playwright/unstable/page-objects/index.ts index 832ea333d20..01c5836e8ad 100644 --- a/packages/testing/src/playwright/unstable/page-objects/index.ts +++ b/packages/testing/src/playwright/unstable/page-objects/index.ts @@ -9,8 +9,6 @@ import { createImpersonationPageObject } from './impersonation'; import { createKeylessPopoverPageObject } from './keylessPopover'; import { createOrganizationSwitcherComponentPageObject } from './organizationSwitcher'; import { createPlanDetailsPageObject } from './planDetails'; -import { createStatementPageObject } from './statement'; -import { createPaymentAttemptPageObject } from './paymentAttempt'; import { createPricingTablePageObject } from './pricingTable'; import { createSessionTaskComponentPageObject } from './sessionTask'; import { createSignInComponentPageObject } from './signIn'; @@ -44,8 +42,6 @@ export const createPageObjects = ({ keylessPopover: createKeylessPopoverPageObject(testArgs), organizationSwitcher: createOrganizationSwitcherComponentPageObject(testArgs), pricingTable: createPricingTablePageObject(testArgs), - statement: createStatementPageObject(testArgs), - paymentAttempt: createPaymentAttemptPageObject(testArgs), sessionTask: createSessionTaskComponentPageObject(testArgs), signIn: createSignInComponentPageObject(testArgs), signUp: createSignUpComponentPageObject(testArgs), diff --git a/packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts b/packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts deleted file mode 100644 index dbc06829402..00000000000 --- a/packages/testing/src/playwright/unstable/page-objects/paymentAttempt.ts +++ /dev/null @@ -1,35 +0,0 @@ -import type { EnhancedPage } from './app'; -import { common } from './common'; - -export const createPaymentAttemptPageObject = (testArgs: { page: EnhancedPage }) => { - const { page } = testArgs; - const root = page.locator('.cl-paymentAttemptRoot'); - const self = { - ...common(testArgs), - waitForMounted: () => { - return root.waitFor({ state: 'visible', timeout: 15000 }); - }, - waitForUnmounted: () => { - return root.waitFor({ state: 'detached', timeout: 15000 }); - }, - goBackToPaymentsList: async () => { - await Promise.all([ - page.waitForURL(/tab=payments/, { timeout: 15000 }), - page.getByRole('link', { name: /Payments/i }).click(), - ]); - await root.waitFor({ state: 'detached', timeout: 15000 }).catch(() => {}); - }, - getStatusBadge: () => { - return self.root.locator('.cl-paymentAttemptHeaderBadge'); - }, - getTotalAmount: () => { - return self.root.locator('.cl-paymentAttemptFooterValue'); - }, - getLineItemTitles: () => { - return self.root.locator('.cl-lineItemsTitle'); - }, - root, - }; - - return self; -}; diff --git a/packages/testing/src/playwright/unstable/page-objects/statement.ts b/packages/testing/src/playwright/unstable/page-objects/statement.ts deleted file mode 100644 index 2d3deb5059d..00000000000 --- a/packages/testing/src/playwright/unstable/page-objects/statement.ts +++ /dev/null @@ -1,38 +0,0 @@ -import type { EnhancedPage } from './app'; -import { common } from './common'; - -export const createStatementPageObject = (testArgs: { page: EnhancedPage }) => { - const { page } = testArgs; - const root = page.locator('.cl-statementRoot'); - const self = { - ...common(testArgs), - waitForMounted: () => { - return root.waitFor({ state: 'visible', timeout: 15000 }); - }, - waitForUnmounted: () => { - return root.waitFor({ state: 'detached', timeout: 15000 }); - }, - goBackToStatementsList: async () => { - await Promise.all([ - page.waitForURL(/tab=statements/, { timeout: 15000 }), - page.getByRole('link', { name: /Statements/i }).click(), - ]); - await root.waitFor({ state: 'detached', timeout: 15000 }).catch(() => {}); - }, - clickViewPaymentButton: async () => { - await self.root - .getByRole('button', { name: /View payment/i }) - .first() - .click(); - }, - getPlanLineItems: () => { - return self.root.locator('.cl-statementSectionContentDetailsHeaderTitle'); - }, - getTotalPaidValue: () => { - return self.root.locator('.cl-statementFooterValue'); - }, - root, - }; - - return self; -}; diff --git a/packages/testing/src/playwright/unstable/page-objects/userProfile.ts b/packages/testing/src/playwright/unstable/page-objects/userProfile.ts index adf2d38dc43..f80b591e7c3 100644 --- a/packages/testing/src/playwright/unstable/page-objects/userProfile.ts +++ b/packages/testing/src/playwright/unstable/page-objects/userProfile.ts @@ -17,40 +17,6 @@ export const createUserProfileComponentPageObject = (testArgs: { page: EnhancedP switchToBillingTab: async () => { await page.getByText(/Billing/i).click(); }, - openBillingStatementsTab: async () => { - await page.getByRole('tab', { name: /Statements/i }).click(); - await self.waitForActiveBillingTabPanel(); - }, - openBillingPaymentsTab: async () => { - await page.getByRole('tab', { name: /Payments/i }).click(); - await self.waitForActiveBillingTabPanel(); - }, - waitForActiveBillingTabPanel: () => { - return page.locator('.cl-userProfile-root .cl-profilePage .cl-table').waitFor({ - state: 'visible', - timeout: 15000, - }); - }, - getActiveBillingTableRows: () => { - return page.locator('.cl-userProfile-root .cl-profilePage .cl-tableBody .cl-tableRow'); - }, - waitForBillingTableRows: async (options?: { hasText?: string | RegExp }) => { - const rows = self.getActiveBillingTableRows(); - if (options?.hasText) { - await rows - .filter({ - hasText: options.hasText, - }) - .first() - .waitFor({ state: 'visible', timeout: 15000 }); - } else { - await rows.first().waitFor({ state: 'visible', timeout: 15000 }); - } - return rows; - }, - getBillingEmptyStateMessage: (text: string | RegExp) => { - return page.locator('.cl-userProfile-root .cl-table').getByText(text); - }, waitForMounted: () => { return page.waitForSelector('.cl-userProfile-root', { state: 'attached' }); }, From fac07e452c2c57c43fea4797c2a785c9bca8db0e Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sun, 9 Nov 2025 18:25:20 +0200 Subject: [PATCH 3/4] add changeset --- .changeset/yellow-pants-act.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .changeset/yellow-pants-act.md diff --git a/.changeset/yellow-pants-act.md b/.changeset/yellow-pants-act.md new file mode 100644 index 00000000000..a845151cc84 --- /dev/null +++ b/.changeset/yellow-pants-act.md @@ -0,0 +1,2 @@ +--- +--- From ff396888072ac8461d7e090940f9cb5d3e6c8e05 Mon Sep 17 00:00:00 2001 From: panteliselef Date: Sun, 9 Nov 2025 18:52:22 +0200 Subject: [PATCH 4/4] Update integration/tests/pricing-table.test.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- integration/tests/pricing-table.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/integration/tests/pricing-table.test.ts b/integration/tests/pricing-table.test.ts index 9865bba8ef1..0d808e79ba0 100644 --- a/integration/tests/pricing-table.test.ts +++ b/integration/tests/pricing-table.test.ts @@ -714,7 +714,7 @@ testAgainstRunningApps({ withEnv: [appConfigs.envs.withBilling] })('pricing tabl page.waitForURL(/tab=payments/, { timeout: 15000 }), page.getByRole('link', { name: /Payments/i }).click(), ]); - await paymentAttemptRoot.waitFor({ state: 'detached', timeout: 15000 }).catch(() => {}); + await paymentAttemptRoot.waitFor({ state: 'detached', timeout: 15000 }); }; await openBillingTab(/Statements/i);