import { expect, test, type Page } from "@playwright/test"; import { mockCoreApis } from "@e2e/fixtures/api-mocks"; import { apiEnvelope } from "@e2e/fixtures/data/common"; import { clearBrowserState } from "@e2e/fixtures/test-helpers"; const testPsid = "e2e-facebook-psid-27511427698460020"; const characters = [ { id: "elio", slug: "elio", displayName: "Elio Silvestri", shortName: "Elio", }, { id: "maya-tan", slug: "maya", displayName: "Maya Tan", shortName: "Maya", }, { id: "nayeli-cervantes", slug: "nayeli", displayName: "Nayeli Cervantes", shortName: "Nayeli", }, ] as const; type Character = (typeof characters)[number]; type EntryKind = "private-space" | "voice" | "image-pack" | "coffee"; interface FacebookEntryCase { character: Character; kind: EntryKind; target: "chat" | "private-zone" | "tip"; expectedPath: string; promotionType?: "voice"; } const entryCases: FacebookEntryCase[] = characters.flatMap((character) => [ { character, kind: "private-space", target: "chat", expectedPath: `/characters/${character.slug}/chat`, }, { character, kind: "voice", target: "chat", expectedPath: `/characters/${character.slug}/chat`, promotionType: "voice", }, { character, kind: "image-pack", target: "private-zone", expectedPath: `/characters/${character.slug}/private-zone`, }, { character, kind: "coffee", target: "tip", expectedPath: `/characters/${character.slug}/tip`, }, ]); test.beforeEach(async ({ baseURL, context, page }) => { await clearBrowserState(context, page, baseURL); await mockCoreApis(page); await registerEntryPageMocks(page); }); for (const entryCase of entryCases) { test(`${entryCase.character.displayName} opens Facebook ${entryCase.kind} entry`, async ({ page, }) => { await page.goto(buildEntryUrl(entryCase)); await expect(page).toHaveURL(entryCase.expectedPath, { timeout: 20_000 }); await expect .poll(() => page.evaluate(() => localStorage.getItem("cozsweet:psid")), ) .toBe(testPsid); if (entryCase.kind === "private-space") { await expect(page.getByRole("textbox", { name: "Message" })).toBeVisible(); await expect( page.getByRole("button", { name: "Unlock voice message" }), ).toHaveCount(0); return; } if (entryCase.kind === "voice") { await expect(page.getByRole("textbox", { name: "Message" })).toBeVisible(); await expect( page.getByRole("button", { name: "Unlock voice message" }), ).toHaveCount(1); return; } if (entryCase.kind === "image-pack") { await expect( page.getByRole("heading", { name: "Private albums" }), ).toBeVisible(); await expect( page.getByText(entryCase.character.displayName).last(), ).toBeVisible(); return; } await expect( page.getByRole("heading", { name: `Buy ${entryCase.character.shortName} a coffee`, }), ).toBeVisible(); }); } function buildEntryUrl(entryCase: FacebookEntryCase): string { const params = new URLSearchParams({ target: entryCase.target, character: entryCase.character.slug, psid: testPsid, }); if (entryCase.promotionType) { params.set("mode", "promotion"); params.set("promotion_type", entryCase.promotionType); } return `/external-entry?${params.toString()}`; } async function registerEntryPageMocks(page: Page): Promise { await page.route("**/api/private-zone/posts**", async (route) => { const url = new URL(route.request().url()); const characterId = url.searchParams.get("characterId") ?? "elio"; await route.fulfill({ json: apiEnvelope({ characterId, items: [], nextCursor: null, hasMore: false, creditBalance: 1000, currency: "credits", }), }); }); await page.route("**/api/private-zone/albums**", async (route) => { await route.fulfill({ json: apiEnvelope({ creditBalance: 1000, pendingImageCount: 0, hasIncompleteContent: false, items: [], }), }); }); await page.route("**/api/payment/gift-products**", async (route) => { const url = new URL(route.request().url()); const characterId = url.searchParams.get("characterId") ?? "elio"; const planPrefix = characterId === "elio" ? "tip" : `tip_${characterId.replaceAll("-", "_")}`; await route.fulfill({ json: apiEnvelope({ characterId, categories: [ { category: "coffee", name: "Coffee", productCount: 1, imageUrl: null, }, ], plans: [ { planId: `${planPrefix}_coffee_usd_4_99`, planName: "Small Coffee", orderType: "tip", tipType: "coffee_small", category: "coffee", characterId, description: "A character-scoped coffee gift", imageUrl: null, amountCents: 499, currency: "USD", autoRenew: false, isFirstRechargeOffer: false, firstRechargeDiscountPercent: 0, promotionType: null, }, ], }), }); }); }