/** * E2E: Chat persistence across page reloads. * * Validates: send message -> receive AI response -> reload -> conversation restored. * Uses mocked /api/chat endpoint via page.route() (no real LLM calls). */ import { test, expect } from "./fixtures.js"; test.describe("Chat persistence", () => { test("conversation survives page reload", async ({ appPage, serverUrl }) => { // 1. Open chat panel await appPage.click("[aria-label='AI Assistant']"); const input = appPage.getByPlaceholder("Ask anything..."); await expect(input).toBeVisible({ timeout: 3_000 }); // 2. Send a message await input.fill("say hello"); await input.press("Enter"); // 3. Wait for AI response to appear await expect(appPage.locator("text=Hello! I'm the AI assistant.")).toBeVisible({ timeout: 10_000 }); // 4. Wait for persistence to trigger (status transitions to "ready") await appPage.waitForTimeout(2_000); // 5. Verify localStorage has chat data const chatKeys = await appPage.evaluate(() => Object.keys(localStorage).filter((k) => k.startsWith("chat:")) ); expect(chatKeys.length).toBeGreaterThan(0); expect(chatKeys[0]).toMatch(/^chat:.+:article$/); // 6. Reload the page await appPage.reload(); await appPage.waitForSelector("[aria-label='Undo']", { timeout: 15_000 }); // 7. Re-open chat and verify conversation is restored await appPage.click("[aria-label='AI Assistant']"); await expect(appPage.locator("text=say hello")).toBeVisible({ timeout: 5_000 }); await expect(appPage.locator("text=Hello! I'm the AI assistant.")).toBeVisible({ timeout: 5_000 }); }); test("new conversation button clears messages", async ({ appPage }) => { // 1. Open chat and send a message await appPage.click("[aria-label='AI Assistant']"); const input = appPage.getByPlaceholder("Ask anything..."); await expect(input).toBeVisible({ timeout: 3_000 }); await input.fill("test message"); await input.press("Enter"); await expect(appPage.locator("text=Hello! I'm the AI assistant.")).toBeVisible({ timeout: 10_000 }); // Wait for persistence await appPage.waitForTimeout(2_000); // 2. Click "New conversation" await appPage.click("[aria-label='New conversation']"); // 3. Verify the empty state is back await expect( appPage.locator("text=Ask me to write, edit, expand, or improve your article.") ).toBeVisible({ timeout: 3_000 }); // 4. Verify localStorage was cleared const chatKeys = await appPage.evaluate(() => Object.keys(localStorage).filter((k) => k.startsWith("chat:")) ); expect(chatKeys.length).toBe(0); }); test("stable user identity across reloads", async ({ appPage }) => { const userName1 = await appPage.evaluate( () => JSON.parse(localStorage.getItem("collab-editor:fallback-user")!).name ); await appPage.reload(); await appPage.waitForSelector("[aria-label='Undo']", { timeout: 15_000 }); const userName2 = await appPage.evaluate( () => JSON.parse(localStorage.getItem("collab-editor:fallback-user")!).name ); expect(userName1).toBe(userName2); }); });