202 lines
5.2 KiB
TypeScript
202 lines
5.2 KiB
TypeScript
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<void> {
|
|
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,
|
|
},
|
|
],
|
|
}),
|
|
});
|
|
});
|
|
}
|