import { expect, test } from "@playwright/test" import { createMockSSEResponse } from "./lib/helpers" test.describe("History and Session Restore", () => { test("new chat button clears conversation", async ({ page }) => { await page.route("**/api/chat", async (route) => { await route.fulfill({ status: 200, contentType: "text/event-stream", body: createMockSSEResponse( ``, "Created your test diagram.", ), }) }) await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) const chatInput = page.locator('textarea[aria-label="Chat input"]') await expect(chatInput).toBeVisible({ timeout: 10000 }) // Send a message await chatInput.fill("Create a test diagram") await chatInput.press("ControlOrMeta+Enter") // Wait for response await expect( page.locator('text="Created your test diagram."'), ).toBeVisible({ timeout: 15000, }) // Find and click new chat button const newChatButton = page.locator('[data-testid="new-chat-button"]') await expect(newChatButton).toBeVisible({ timeout: 5000 }) await newChatButton.click() // Conversation should be cleared await expect( page.locator('text="Created your test diagram."'), ).not.toBeVisible({ timeout: 5000 }) }) test("chat history sidebar shows past conversations", async ({ page }) => { await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Look for history/sidebar button that is enabled const historyButton = page.locator( 'button[aria-label*="History"]:not([disabled]), button:has(svg.lucide-history):not([disabled]), button:has(svg.lucide-menu):not([disabled]), button:has(svg.lucide-sidebar):not([disabled]), button:has(svg.lucide-panel-left):not([disabled])', ) // History feature may not exist in all versions - skip if not available const buttonCount = await historyButton.count() if (buttonCount === 0) { test.skip() return } await historyButton.first().click() // Wait for sidebar/panel to appear or verify page still works await expect( page.locator('textarea[aria-label="Chat input"]'), ).toBeVisible({ timeout: 3000 }) }) test("conversation persists after page reload", async ({ page }) => { await page.route("**/api/chat", async (route) => { await route.fulfill({ status: 200, contentType: "text/event-stream", body: createMockSSEResponse( ``, "This message should persist.", ), }) }) await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) const chatInput = page.locator('textarea[aria-label="Chat input"]') await expect(chatInput).toBeVisible({ timeout: 10000 }) // Send a message await chatInput.fill("Create persistent diagram") await chatInput.press("ControlOrMeta+Enter") // Wait for response await expect( page.locator('text="This message should persist."'), ).toBeVisible({ timeout: 15000 }) // Reload page await page.reload({ waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Verify page is functional after reload await expect( page.locator('textarea[aria-label="Chat input"]'), ).toBeVisible({ timeout: 10000 }) // Verify the message persisted after reload await expect( page.locator('text="This message should persist."'), ).toBeVisible({ timeout: 10000 }) }) test("diagram state persists after reload", async ({ page }) => { await page.route("**/api/chat", async (route) => { await route.fulfill({ status: 200, contentType: "text/event-stream", body: createMockSSEResponse( ``, "Created a diagram that should be saved.", ), }) }) await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) const chatInput = page.locator('textarea[aria-label="Chat input"]') await expect(chatInput).toBeVisible({ timeout: 10000 }) // Generate a diagram await chatInput.fill("Create saveable diagram") await chatInput.press("ControlOrMeta+Enter") await expect(page.locator('text="Complete"')).toBeVisible({ timeout: 15000, }) // Reload await page.reload({ waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Diagram state is typically stored - check iframe is still functional const frame = page.frameLocator("iframe") await expect( frame .locator(".geMenubarContainer, .geDiagramContainer, canvas") .first(), ).toBeVisible({ timeout: 30000 }) }) test("can restore from browser back/forward", async ({ page }) => { await page.route("**/api/chat", async (route) => { await route.fulfill({ status: 200, contentType: "text/event-stream", body: createMockSSEResponse( ``, "Testing browser navigation.", ), }) }) await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) const chatInput = page.locator('textarea[aria-label="Chat input"]') await expect(chatInput).toBeVisible({ timeout: 10000 }) // Send a message await chatInput.fill("Test navigation") await chatInput.press("ControlOrMeta+Enter") await expect( page.locator('text="Testing browser navigation."'), ).toBeVisible({ timeout: 15000, }) // Navigate to about page await page.goto("/about", { waitUntil: "networkidle" }) // Go back await page.goBack({ waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Page should be functional await expect(chatInput).toBeVisible({ timeout: 10000 }) }) test("settings are restored after reload", async ({ page }) => { await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Open settings const settingsButton = page.locator('[data-testid="settings-button"]') await settingsButton.click() // Settings dialog should open await expect( page.locator('[role="dialog"], [role="menu"], form').first(), ).toBeVisible({ timeout: 5000 }) // Close settings await page.keyboard.press("Escape") // Reload await page.reload({ waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Open settings again await settingsButton.click() // Settings should still be accessible await expect( page.locator('[role="dialog"], [role="menu"], form').first(), ).toBeVisible({ timeout: 5000 }) }) test("model selection persists", async ({ page }) => { await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Find model selector const modelSelector = page.locator( 'button[aria-label*="Model"], [data-testid="model-selector"], button:has-text("Claude")', ) // Model selector feature may not exist in all versions - skip if not available const selectorCount = await modelSelector.count() if (selectorCount === 0) { test.skip() return } const initialModel = await modelSelector.first().textContent() // Reload page await page.reload({ waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Check model is still selected const modelAfterReload = await modelSelector.first().textContent() expect(modelAfterReload).toBe(initialModel) }) test("handles localStorage quota exceeded gracefully", async ({ page }) => { await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Fill up localStorage (simulate quota exceeded scenario) await page.evaluate(() => { try { // This might throw if quota is exceeded const largeData = "x".repeat(5 * 1024 * 1024) // 5MB localStorage.setItem("test-large-data", largeData) } catch { // Expected to fail on some browsers } }) // App should still function const chatInput = page.locator('textarea[aria-label="Chat input"]') await expect(chatInput).toBeVisible({ timeout: 10000 }) // Clean up await page.evaluate(() => { localStorage.removeItem("test-large-data") }) }) })