Files
cozsweet-frontend-nextjs/e2e/specs/mock/payment/checkout-handoff-external-browser.spec.ts
T
Codex 59e4eac736
Docker Image / Build and Push Docker Image (push) Successful in 2m10s
feat(payment): expand Stripe methods and checkout handoff
2026-07-28 16:48:11 +08:00

95 lines
3.1 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { mockCoreApis } from "@e2e/fixtures/api-mocks";
import { apiEnvelope } from "@e2e/fixtures/data/common";
import {
clearBrowserState,
seedEmailSession,
} from "@e2e/fixtures/test-helpers";
const facebookAndroidUserAgent =
"Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 " +
"(KHTML, like Gecko) Version/4.0 Chrome/126.0.0.0 Mobile Safari/537.36 " +
"[FB_IAB/FB4A;FBAV/566.0.0.48.73;]";
const handoffToken = "checkout-handoff-token-that-is-at-least-32-characters";
test.use({ userAgent: facebookAndroidUserAgent });
test.beforeEach(async ({ baseURL, context, page }) => {
await clearBrowserState(context, page, baseURL);
});
test("consumes the handoff, removes its token, and does not create an order", async ({
page,
}) => {
await mockCoreApis(page, { checkoutHandoffFlow: true });
let createOrderRequests = 0;
page.on("request", (request) => {
if (request.url().includes("/api/payment/create-order")) {
createOrderRequests += 1;
}
});
const consumeRequest = page.waitForRequest(
"**/api/auth/handoff/checkout/consume",
);
await page.goto(
`/external-entry?target=checkout&handoffToken=${encodeURIComponent(handoffToken)}`,
);
expect((await consumeRequest).postDataJSON()).toEqual({ handoffToken });
await expect(page).toHaveURL(
/\/subscription\?planId=vip_monthly&autoRenew=1&commercialOfferId=.*&payChannel=stripe/,
);
expect(page.url()).not.toContain("handoffToken");
await expect.poll(() => createOrderRequests).toBe(0);
await expect
.poll(() =>
page.evaluate(() => ({
loginProvider: localStorage.getItem("cozsweet:login_provider"),
loginToken: localStorage.getItem("cozsweet:login_token"),
})),
)
.toEqual({ loginProvider: "email", loginToken: "e2e-checkout-token" });
});
test("offers the Facebook external-browser path before creating a Stripe order", async ({
page,
}) => {
await mockCoreApis(page);
let createOrderRequests = 0;
page.on("request", (request) => {
if (request.url().includes("/api/payment/create-order")) {
createOrderRequests += 1;
}
});
await page.route("**/api/auth/handoff/checkout", async (route) => {
await route.fulfill({
json: apiEnvelope({
externalUrl:
"https://cozsweet.com/external-entry?target=checkout&handoffToken=opaque-token",
expiresAt: "2026-07-28T16:00:00+00:00",
}),
});
});
await seedEmailSession(page);
await page.goto("/subscription?type=vip&payChannel=stripe");
await page.getByRole("button", { name: /Monthly,/i }).click();
await page
.getByRole("dialog", { name: "Automatic Renewal Confirmation" })
.getByRole("button", { name: "Confirm" })
.click();
const externalButton = page.getByRole("button", {
name: "Open in browser for more payment methods",
});
await expect(externalButton).toBeVisible();
const handoffRequest = page.waitForRequest("**/api/auth/handoff/checkout");
await externalButton.click();
expect((await handoffRequest).postDataJSON()).toMatchObject({
planId: "vip_monthly",
autoRenew: true,
});
expect(createOrderRequests).toBe(0);
});