From bc6373f4c0d26cfbea250dddfd4f3f527b803e29 Mon Sep 17 00:00:00 2001 From: chenhang Date: Mon, 20 Jul 2026 15:38:51 +0800 Subject: [PATCH] test(e2e): fix auth specs for character routes --- e2e/fixtures/api-mocks.ts | 2 ++ e2e/fixtures/api/character.ts | 10 +++++++ e2e/fixtures/api/chat.ts | 6 ++++- e2e/fixtures/data/character.ts | 26 +++++++++++++++++++ e2e/fixtures/data/chat.ts | 2 ++ e2e/fixtures/helpers/auth.ts | 4 ++- .../mock/auth/logout-from-sidebar.spec.ts | 17 +++++++++--- 7 files changed, 62 insertions(+), 5 deletions(-) create mode 100644 e2e/fixtures/api/character.ts create mode 100644 e2e/fixtures/data/character.ts diff --git a/e2e/fixtures/api-mocks.ts b/e2e/fixtures/api-mocks.ts index 79894e1e..9460179b 100644 --- a/e2e/fixtures/api-mocks.ts +++ b/e2e/fixtures/api-mocks.ts @@ -2,6 +2,7 @@ import type { Page } from "@playwright/test"; import { registerAuthMocks } from "./api/auth"; import { registerChatMocks } from "./api/chat"; +import { registerCharacterMocks } from "./api/character"; import { registerPaymentMocks } from "./api/payment"; import { registerUserMocks } from "./api/user"; @@ -30,6 +31,7 @@ export async function mockCoreApis(page: Page, options: MockCoreApisOptions = {} isChatSendTokenExpired: () => chatState.hasExpiredChatSend, psidLoginFlow: chatOptions.psidLoginFlow, }); + await registerCharacterMocks(page); await registerUserMocks(page); await registerChatMocks(page, chatOptions, chatState); await registerPaymentMocks(page); diff --git a/e2e/fixtures/api/character.ts b/e2e/fixtures/api/character.ts new file mode 100644 index 00000000..2107b9b0 --- /dev/null +++ b/e2e/fixtures/api/character.ts @@ -0,0 +1,10 @@ +import type { Page } from "@playwright/test"; + +import { apiEnvelope } from "../data/common"; +import { chatCharactersResponse } from "../data/character"; + +export async function registerCharacterMocks(page: Page) { + await page.route("**/api/characters**", async (route) => { + await route.fulfill({ json: apiEnvelope(chatCharactersResponse) }); + }); +} diff --git a/e2e/fixtures/api/chat.ts b/e2e/fixtures/api/chat.ts index 33c1a5a8..fb0f9389 100644 --- a/e2e/fixtures/api/chat.ts +++ b/e2e/fixtures/api/chat.ts @@ -1,7 +1,7 @@ import type { Page } from "@playwright/test"; import { apiEnvelope } from "../data/common"; -import { chatSendResponse, createChatSendResponse, createPaidImageHistoryResponse, createWeeklyLimitChatSendResponse, emptyChatHistoryResponse, insufficientCreditsUnlockImageResponse, insufficientCreditsUnlockVoiceResponse, paidImageChatSendResponse, paidImageUnlockHistoryResponse, paidVoiceHistoryResponse } from "../data/chat"; +import { chatSendResponse, createChatSendResponse, createPaidImageHistoryResponse, createWeeklyLimitChatSendResponse, emptyChatHistoryResponse, emptyChatPreviewsResponse, insufficientCreditsUnlockImageResponse, insufficientCreditsUnlockVoiceResponse, paidImageChatSendResponse, paidImageUnlockHistoryResponse, paidVoiceHistoryResponse } from "../data/chat"; export interface ChatMockOptions { chatLimitTriggerAt?: number; @@ -31,6 +31,10 @@ export async function registerChatMocks(page: Page, options: ChatMockOptions, st await route.fulfill({ json: apiEnvelope(response) }); }); + await page.route("**/api/chat/previews", async (route) => { + await route.fulfill({ json: apiEnvelope(emptyChatPreviewsResponse) }); + }); + await page.route("**/api/chat/send", async (route) => { sendCount += 1; const requestBody = route.request().postDataJSON() as { message?: unknown } | undefined; diff --git a/e2e/fixtures/data/character.ts b/e2e/fixtures/data/character.ts new file mode 100644 index 00000000..2089ea7d --- /dev/null +++ b/e2e/fixtures/data/character.ts @@ -0,0 +1,26 @@ +export const chatCharactersResponse = { + items: [ + { + id: "elio", + displayName: "Elio Silvestri", + isActive: true, + capabilities: { chat: true, privateContent: true }, + sortOrder: 10, + }, + { + id: "maya-tan", + displayName: "Maya Tan", + isActive: true, + capabilities: { chat: true, privateContent: true }, + sortOrder: 20, + }, + { + id: "nayeli-cervantes", + displayName: "Nayeli Cervantes", + isActive: true, + capabilities: { chat: true, privateContent: true }, + sortOrder: 30, + }, + ], + defaultCharacterId: "elio", +}; diff --git a/e2e/fixtures/data/chat.ts b/e2e/fixtures/data/chat.ts index c007342a..43ccbb8d 100644 --- a/e2e/fixtures/data/chat.ts +++ b/e2e/fixtures/data/chat.ts @@ -9,6 +9,8 @@ export const emptyChatHistoryResponse = { privateCanViewFree: false, }; +export const emptyChatPreviewsResponse = { items: [] }; + export const chatSendResponse = { reply: "Hello from the E2E boyfriend.", audioUrl: "", diff --git a/e2e/fixtures/helpers/auth.ts b/e2e/fixtures/helpers/auth.ts index b190b0e4..a37c55e8 100644 --- a/e2e/fixtures/helpers/auth.ts +++ b/e2e/fixtures/helpers/auth.ts @@ -11,7 +11,7 @@ export const splashStartChatButtonName = "Start Chatting"; export const defaultCharacterSlug = "elio"; export const defaultCharacterSplashPath = `/characters/${defaultCharacterSlug}/splash`; export const defaultCharacterChatPath = `/characters/${defaultCharacterSlug}/chat`; -export const defaultCharacterSidebarPath = `/characters/${defaultCharacterSlug}/sidebar`; +export const defaultCharacterSidebarPath = `/sidebar?returnTo=%2Fcharacters%2F${defaultCharacterSlug}%2Fchat`; export const defaultCharacterChatUrl = new RegExp( `/characters/${defaultCharacterSlug}/chat(?:\\?.*)?$`, ); @@ -34,6 +34,8 @@ export async function seedEmailSession(page: Page) { await page.goto(defaultCharacterSplashPath); await setEmailSessionStorage(page); await page.reload(); + // Let the root auth provider hydrate the seeded session before navigation. + await page.waitForTimeout(750); } export async function switchToEmailSignIn(page: Page) { diff --git a/e2e/specs/mock/auth/logout-from-sidebar.spec.ts b/e2e/specs/mock/auth/logout-from-sidebar.spec.ts index 56a79e9c..27bb0484 100644 --- a/e2e/specs/mock/auth/logout-from-sidebar.spec.ts +++ b/e2e/specs/mock/auth/logout-from-sidebar.spec.ts @@ -5,8 +5,11 @@ import { clearBrowserState, defaultCharacterSidebarPath, defaultCharacterSplashPath, + defaultCharacterChatUrl, + enterChatFromSplash, getSplashStartChatButton, - signInWithEmailAndOpenChat, + submitEmailLogin, + switchToEmailSignIn, } from "@e2e/fixtures/test-helpers"; test.beforeEach(async ({ baseURL, context, page }) => { @@ -17,11 +20,19 @@ test.beforeEach(async ({ baseURL, context, page }) => { test("user can log out from the sidebar after email login", async ({ page, }) => { - await signInWithEmailAndOpenChat(page); + await enterChatFromSplash(page); + await page + .getByRole("button", { name: "Sign up to unlock more features" }) + .click(); + await expect(page).toHaveURL(/\/auth(?:\?.*)?$/); + await switchToEmailSignIn(page); + await submitEmailLogin(page, { expectedUrl: defaultCharacterChatUrl }); await expect(page.getByRole("button", { name: "Menu" })).toBeVisible(); await page.getByRole("button", { name: "Menu" }).click(); - await expect(page).toHaveURL(new RegExp(`${defaultCharacterSidebarPath}$`)); + await expect(page).toHaveURL( + new RegExp(defaultCharacterSidebarPath.replace("?", "\\?") + "$"), + ); const logoutRequestPromise = page.waitForRequest("**/api/auth/logout"); await page.getByRole("button", { name: /Log out/i }).click();