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',
|
||||
};
|
||||
|
||||
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.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('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)
|
||||
await page.goto(`${baseUrl}/${locale}/${checkoutSlug}/2`);
|
||||
|
||||
@@ -69,6 +84,7 @@ test.describe('Paddle sandbox full flow (staging)', () => {
|
||||
await expect(
|
||||
page.getByText(/Checkout geöffnet|Checkout opened|Paddle-Checkout/i).first()
|
||||
).toBeVisible({ timeout: 20_000 });
|
||||
await waitForPaddleCardInputs(page, ['input[autocomplete="cc-number"]', 'input[name="cardnumber"]', 'input[name="card_number"]']);
|
||||
} else if (checkoutUrl) {
|
||||
await page.goto(checkoutUrl);
|
||||
await expect(page).toHaveURL(/paddle/);
|
||||
@@ -196,6 +212,62 @@ async function completeHostedPaddleCheckout(
|
||||
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 {
|
||||
if (payload) {
|
||||
const directUrl = payload.checkout_url ?? payload.url ?? payload.checkoutUrl;
|
||||
|
||||
Reference in New Issue
Block a user