refactor: add shared fixtures and test.step() patterns

- Add tests/e2e/lib/fixtures.ts with shared test helpers
- Add tests/e2e/fixtures/diagrams.ts with XML test data
- Add expectBeforeAndAfterReload() helper for persistence tests
- Add test.step() for better test reporting in complex tests
- Consolidate mock helpers into fixtures module
- Reduce code duplication across 17 test files
This commit is contained in:
dayuan.jiang
2026-01-05 00:37:40 +09:00
parent 948cc4666d
commit 828bf43e31
19 changed files with 662 additions and 815 deletions

View File

@@ -1,17 +1,21 @@
import { expect, test } from "@playwright/test"
import { SINGLE_BOX_XML } from "./fixtures/diagrams"
import {
expect,
getChatInput,
getIframe,
sendMessage,
test,
} from "./lib/fixtures"
import { createMockSSEResponse } from "./lib/helpers"
test.describe("Copy/Paste Functionality", () => {
test("can paste text into chat input", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
const chatInput = page.locator('textarea[aria-label="Chat input"]')
const chatInput = getChatInput(page)
await expect(chatInput).toBeVisible({ timeout: 10000 })
// Focus and paste text
await chatInput.focus()
await page.keyboard.insertText("Create a flowchart diagram")
@@ -20,11 +24,9 @@ test.describe("Copy/Paste Functionality", () => {
test("can paste multiline text into chat input", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
const chatInput = page.locator('textarea[aria-label="Chat input"]')
const chatInput = getChatInput(page)
await expect(chatInput).toBeVisible({ timeout: 10000 })
await chatInput.focus()
@@ -40,23 +42,16 @@ test.describe("Copy/Paste Functionality", () => {
status: 200,
contentType: "text/event-stream",
body: createMockSSEResponse(
`<mxCell id="box" value="Test" style="rounded=1;" vertex="1" parent="1"><mxGeometry x="100" y="100" width="100" height="50" as="geometry"/></mxCell>`,
SINGLE_BOX_XML,
"Here is your diagram with a test box.",
),
})
})
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).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 box")
await chatInput.press("ControlOrMeta+Enter")
await sendMessage(page, "Create a test box")
// Wait for response
await expect(
@@ -68,7 +63,7 @@ test.describe("Copy/Paste Functionality", () => {
'[data-testid="copy-button"], button[aria-label*="Copy"], button:has(svg.lucide-copy), button:has(svg.lucide-clipboard)',
)
// Copy button feature may not exist in all versions - skip if not available
// Copy button feature may not exist - skip if not available
const buttonCount = await copyButton.count()
if (buttonCount === 0) {
test.skip()
@@ -76,7 +71,6 @@ test.describe("Copy/Paste Functionality", () => {
}
await copyButton.first().click()
// Should show copied confirmation (toast or button state change)
await expect(
page.locator('text="Copied"').or(page.locator("svg.lucide-check")),
).toBeVisible({ timeout: 3000 })
@@ -84,16 +78,14 @@ test.describe("Copy/Paste Functionality", () => {
test("paste XML into XML input works", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
// Find XML input textarea (different from chat input)
// Find XML input textarea
const xmlInput = page.locator(
'textarea[placeholder*="XML"], textarea[placeholder*="mxCell"]',
)
// XML input might be in a collapsed section - try to expand it
// Try to expand XML section if collapsed
const xmlToggle = page.locator(
'button:has-text("XML"), [data-testid*="xml"], details summary',
)
@@ -101,7 +93,7 @@ test.describe("Copy/Paste Functionality", () => {
await xmlToggle.first().click()
}
// XML input feature may not exist in all versions - skip if not available
// Skip if XML input not available
const xmlInputCount = await xmlInput.count()
const isXmlVisible =
xmlInputCount > 0 && (await xmlInput.first().isVisible())
@@ -120,20 +112,13 @@ test.describe("Copy/Paste Functionality", () => {
test("keyboard shortcuts work in chat input", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
const chatInput = page.locator('textarea[aria-label="Chat input"]')
const chatInput = getChatInput(page)
await expect(chatInput).toBeVisible({ timeout: 10000 })
// Type some text
await chatInput.fill("Hello world")
// Select all with Ctrl+A
await chatInput.press("ControlOrMeta+a")
// Type replacement text
await chatInput.fill("New text")
await expect(chatInput).toHaveValue("New text")
@@ -141,21 +126,16 @@ test.describe("Copy/Paste Functionality", () => {
test("can undo/redo in chat input", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
const chatInput = page.locator('textarea[aria-label="Chat input"]')
const chatInput = getChatInput(page)
await expect(chatInput).toBeVisible({ timeout: 10000 })
// Type text
await chatInput.fill("First text")
await chatInput.press("Tab") // Blur to register change
await chatInput.press("Tab")
await chatInput.focus()
await chatInput.fill("Second text")
// Undo with Ctrl+Z
await chatInput.press("ControlOrMeta+z")
// Verify page is still functional after undo
@@ -164,11 +144,9 @@ test.describe("Copy/Paste Functionality", () => {
test("chat input handles special characters", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
const chatInput = page.locator('textarea[aria-label="Chat input"]')
const chatInput = getChatInput(page)
await expect(chatInput).toBeVisible({ timeout: 10000 })
const specialText = "Test <>&\"' special chars 日本語 中文 🎉"
@@ -179,18 +157,14 @@ test.describe("Copy/Paste Functionality", () => {
test("long text in chat input scrolls", async ({ page }) => {
await page.goto("/", { waitUntil: "networkidle" })
await page
.locator("iframe")
.waitFor({ state: "visible", timeout: 30000 })
await getIframe(page).waitFor({ state: "visible", timeout: 30000 })
const chatInput = page.locator('textarea[aria-label="Chat input"]')
const chatInput = getChatInput(page)
await expect(chatInput).toBeVisible({ timeout: 10000 })
// Very long text
const longText = "This is a very long text. ".repeat(50)
await chatInput.fill(longText)
// Input should handle it without error
const value = await chatInput.inputValue()
expect(value.length).toBeGreaterThan(500)
})