mirror of
https://github.com/DayuanJiang/next-ai-draw-io.git
synced 2026-01-09 01:32:29 +08:00
216 lines
7.8 KiB
TypeScript
216 lines
7.8 KiB
TypeScript
|
|
import { SINGLE_BOX_XML } from "./fixtures/diagrams"
|
||
|
|
import {
|
||
|
|
expect,
|
||
|
|
expectBeforeAndAfterReload,
|
||
|
|
getChatInput,
|
||
|
|
getIframe,
|
||
|
|
getIframeContent,
|
||
|
|
openSettings,
|
||
|
|
sendMessage,
|
||
|
|
test,
|
||
|
|
waitForComplete,
|
||
|
|
waitForText,
|
||
|
|
} from "./lib/fixtures"
|
||
|
|
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(
|
||
|
|
SINGLE_BOX_XML,
|
||
|
|
"Created your test diagram.",
|
||
|
|
),
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await test.step("create a conversation", async () => {
|
||
|
|
await sendMessage(page, "Create a test diagram")
|
||
|
|
await waitForText(page, "Created your test diagram.")
|
||
|
|
})
|
||
|
|
|
||
|
|
await test.step("click new chat button", async () => {
|
||
|
|
const newChatButton = page.locator(
|
||
|
|
'[data-testid="new-chat-button"]',
|
||
|
|
)
|
||
|
|
await expect(newChatButton).toBeVisible({ timeout: 5000 })
|
||
|
|
await newChatButton.click()
|
||
|
|
})
|
||
|
|
|
||
|
|
await test.step("verify conversation is cleared", async () => {
|
||
|
|
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 getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
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])',
|
||
|
|
)
|
||
|
|
|
||
|
|
const buttonCount = await historyButton.count()
|
||
|
|
if (buttonCount === 0) {
|
||
|
|
test.skip()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
await historyButton.first().click()
|
||
|
|
await expect(getChatInput(page)).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(
|
||
|
|
SINGLE_BOX_XML,
|
||
|
|
"This message should persist.",
|
||
|
|
),
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await test.step("create conversation", async () => {
|
||
|
|
await sendMessage(page, "Create persistent diagram")
|
||
|
|
await waitForText(page, "This message should persist.")
|
||
|
|
})
|
||
|
|
|
||
|
|
await test.step("verify message appears before reload", async () => {
|
||
|
|
await expect(getChatInput(page)).toBeVisible({ timeout: 10000 })
|
||
|
|
await expect(
|
||
|
|
page.locator('text="This message should persist."'),
|
||
|
|
).toBeVisible({ timeout: 10000 })
|
||
|
|
})
|
||
|
|
|
||
|
|
// Note: After reload, mocked responses won't persist since we're not
|
||
|
|
// testing with real localStorage. We just verify the app loads correctly.
|
||
|
|
await test.step("verify app loads after reload", async () => {
|
||
|
|
await page.reload({ waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
await expect(getChatInput(page)).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(
|
||
|
|
SINGLE_BOX_XML,
|
||
|
|
"Created a diagram that should be saved.",
|
||
|
|
),
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await sendMessage(page, "Create saveable diagram")
|
||
|
|
await waitForComplete(page)
|
||
|
|
|
||
|
|
await page.reload({ waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
const frame = getIframeContent(page)
|
||
|
|
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(
|
||
|
|
SINGLE_BOX_XML,
|
||
|
|
"Testing browser navigation.",
|
||
|
|
),
|
||
|
|
})
|
||
|
|
})
|
||
|
|
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await sendMessage(page, "Test navigation")
|
||
|
|
await waitForText(page, "Testing browser navigation.")
|
||
|
|
|
||
|
|
await page.goto("/about", { waitUntil: "networkidle" })
|
||
|
|
await page.goBack({ waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await expect(getChatInput(page)).toBeVisible({ timeout: 10000 })
|
||
|
|
})
|
||
|
|
|
||
|
|
test("settings are restored after reload", async ({ page }) => {
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await openSettings(page)
|
||
|
|
await page.keyboard.press("Escape")
|
||
|
|
|
||
|
|
await page.reload({ waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await openSettings(page)
|
||
|
|
})
|
||
|
|
|
||
|
|
test("model selection persists", async ({ page }) => {
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
const modelSelector = page.locator(
|
||
|
|
'button[aria-label*="Model"], [data-testid="model-selector"], button:has-text("Claude")',
|
||
|
|
)
|
||
|
|
|
||
|
|
const selectorCount = await modelSelector.count()
|
||
|
|
if (selectorCount === 0) {
|
||
|
|
test.skip()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const initialModel = await modelSelector.first().textContent()
|
||
|
|
|
||
|
|
await page.reload({ waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
const modelAfterReload = await modelSelector.first().textContent()
|
||
|
|
expect(modelAfterReload).toBe(initialModel)
|
||
|
|
})
|
||
|
|
|
||
|
|
test("handles localStorage quota exceeded gracefully", async ({ page }) => {
|
||
|
|
await page.goto("/", { waitUntil: "networkidle" })
|
||
|
|
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
|
||
|
|
|
||
|
|
await page.evaluate(() => {
|
||
|
|
try {
|
||
|
|
const largeData = "x".repeat(5 * 1024 * 1024)
|
||
|
|
localStorage.setItem("test-large-data", largeData)
|
||
|
|
} catch {
|
||
|
|
// Expected to fail on some browsers
|
||
|
|
}
|
||
|
|
})
|
||
|
|
|
||
|
|
await expect(getChatInput(page)).toBeVisible({ timeout: 10000 })
|
||
|
|
|
||
|
|
await page.evaluate(() => {
|
||
|
|
localStorage.removeItem("test-large-data")
|
||
|
|
})
|
||
|
|
})
|
||
|
|
})
|