Improve Paddle sandbox inline readiness checks
This commit is contained in:
@@ -16,11 +16,26 @@ const sandboxCard = {
|
|||||||
postal: process.env.E2E_PADDLE_CARD_POSTAL ?? '10115',
|
postal: process.env.E2E_PADDLE_CARD_POSTAL ?? '10115',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
test.use({
|
||||||
|
userAgent:
|
||||||
|
process.env.E2E_USER_AGENT ??
|
||||||
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36',
|
||||||
|
launchOptions: {
|
||||||
|
args: ['--disable-blink-features=AutomationControlled'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
test.describe('Paddle sandbox full flow (staging)', () => {
|
test.describe('Paddle sandbox full flow (staging)', () => {
|
||||||
test.skip(!shouldRun, 'Set E2E_PADDLE_SANDBOX=1 to run live sandbox checkout on staging.');
|
test.skip(!shouldRun, 'Set E2E_PADDLE_SANDBOX=1 to run live sandbox checkout on staging.');
|
||||||
test.skip(!tenantEmail || !tenantPassword, 'Set E2E_TENANT_EMAIL and E2E_TENANT_PASSWORD for sandbox flow.');
|
test.skip(!tenantEmail || !tenantPassword, 'Set E2E_TENANT_EMAIL and E2E_TENANT_PASSWORD for sandbox flow.');
|
||||||
|
|
||||||
test('register, pay via Paddle sandbox, and login to event admin', async ({ page, request }) => {
|
test('register, pay via Paddle sandbox, and login to event admin', async ({ page, request }) => {
|
||||||
|
await page.addInitScript(() => {
|
||||||
|
Object.defineProperty(navigator, 'webdriver', {
|
||||||
|
get: () => undefined,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Jump directly into wizard for Standard package (2)
|
// Jump directly into wizard for Standard package (2)
|
||||||
await page.goto(`${baseUrl}/${locale}/${checkoutSlug}/2`);
|
await page.goto(`${baseUrl}/${locale}/${checkoutSlug}/2`);
|
||||||
|
|
||||||
@@ -69,6 +84,7 @@ test.describe('Paddle sandbox full flow (staging)', () => {
|
|||||||
await expect(
|
await expect(
|
||||||
page.getByText(/Checkout geöffnet|Checkout opened|Paddle-Checkout/i).first()
|
page.getByText(/Checkout geöffnet|Checkout opened|Paddle-Checkout/i).first()
|
||||||
).toBeVisible({ timeout: 20_000 });
|
).toBeVisible({ timeout: 20_000 });
|
||||||
|
await waitForPaddleCardInputs(page, ['input[autocomplete="cc-number"]', 'input[name="cardnumber"]', 'input[name="card_number"]']);
|
||||||
} else if (checkoutUrl) {
|
} else if (checkoutUrl) {
|
||||||
await page.goto(checkoutUrl);
|
await page.goto(checkoutUrl);
|
||||||
await expect(page).toHaveURL(/paddle/);
|
await expect(page).toHaveURL(/paddle/);
|
||||||
@@ -196,6 +212,62 @@ async function completeHostedPaddleCheckout(
|
|||||||
await payButton.click();
|
await payButton.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function waitForPaddleCardInputs(page: Page, selectors: string[], timeoutMs = 30_000): Promise<void> {
|
||||||
|
const deadline = Date.now() + timeoutMs;
|
||||||
|
|
||||||
|
while (Date.now() < deadline) {
|
||||||
|
if (await hasAnySelector(page, selectors)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (await hasAnyText(page, /Something went wrong|try again later/i)) {
|
||||||
|
throw new Error('Paddle inline checkout returned an error in the iframe.');
|
||||||
|
}
|
||||||
|
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error('Paddle card inputs did not appear in time.');
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hasAnySelector(page: Page, selectors: string[]): Promise<boolean> {
|
||||||
|
if (await hasSelectorInFrame(page, selectors)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const frame of page.frames()) {
|
||||||
|
if (await hasSelectorInFrame(frame, selectors)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hasSelectorInFrame(scope: Page | import('@playwright/test').Frame, selectors: string[]): Promise<boolean> {
|
||||||
|
for (const selector of selectors) {
|
||||||
|
if ((await scope.locator(selector).count()) > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function hasAnyText(page: Page, matcher: RegExp): Promise<boolean> {
|
||||||
|
if ((await page.getByText(matcher).count()) > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const frame of page.frames()) {
|
||||||
|
if ((await frame.getByText(matcher).count()) > 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
function extractCheckoutUrl(payload: Record<string, unknown> | null, rawBody: string): string | null {
|
function extractCheckoutUrl(payload: Record<string, unknown> | null, rawBody: string): string | null {
|
||||||
if (payload) {
|
if (payload) {
|
||||||
const directUrl = payload.checkout_url ?? payload.url ?? payload.checkoutUrl;
|
const directUrl = payload.checkout_url ?? payload.url ?? payload.checkoutUrl;
|
||||||
|
|||||||
Reference in New Issue
Block a user