import { expect, test } from "@playwright/test" // Helper to create SSE response function createMockSSEResponse(xml: string, text: string) { const messageId = `msg_${Date.now()}` const toolCallId = `call_${Date.now()}` const textId = `text_${Date.now()}` const events = [ { type: "start", messageId }, { type: "text-start", id: textId }, { type: "text-delta", id: textId, delta: text }, { type: "text-end", id: textId }, { type: "tool-input-start", toolCallId, toolName: "display_diagram" }, { type: "tool-input-available", toolCallId, toolName: "display_diagram", input: { xml }, }, { type: "tool-output-available", toolCallId, output: "Successfully displayed the diagram", }, { type: "finish" }, ] return ( events.map((e) => `data: ${JSON.stringify(e)}\n\n`).join("") + "data: [DONE]\n\n" ) } 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 }) const chatInput = page.locator('textarea[aria-label="Chat input"]') await expect(chatInput).toBeVisible({ timeout: 10000 }) // Focus and paste text await chatInput.focus() await page.keyboard.insertText("Create a flowchart diagram") await expect(chatInput).toHaveValue("Create a flowchart diagram") }) test("can paste multiline text into chat input", async ({ page }) => { 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 }) await chatInput.focus() const multilineText = "Line 1\nLine 2\nLine 3" await page.keyboard.insertText(multilineText) await expect(chatInput).toHaveValue(multilineText) }) test("copy button copies response text", async ({ page }) => { await page.route("**/api/chat", async (route) => { await route.fulfill({ status: 200, contentType: "text/event-stream", body: createMockSSEResponse( ``, "Here is your diagram with a test box.", ), }) }) 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 box") await chatInput.press("ControlOrMeta+Enter") // Wait for response await expect( page.locator('text="Here is your diagram with a test box."'), ).toBeVisible({ timeout: 15000 }) // Find copy button in message const copyButton = page.locator( 'button[aria-label*="Copy"], button:has(svg.lucide-copy), button:has(svg.lucide-clipboard)', ) if ((await copyButton.count()) > 0) { 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 }) } }) test("paste XML into XML input works", async ({ page }) => { await page.goto("/", { waitUntil: "networkidle" }) await page .locator("iframe") .waitFor({ state: "visible", timeout: 30000 }) // Find XML input textarea (different from chat input) const xmlInput = page.locator( 'textarea[placeholder*="XML"], textarea[placeholder*="mxCell"]', ) // XML input might be in a collapsed section - try to expand it const xmlToggle = page.locator( 'button:has-text("XML"), [data-testid*="xml"], details summary', ) if ((await xmlToggle.count()) > 0) { await xmlToggle.first().click() await page.waitForTimeout(500) } // Check if XML input is now visible if ( (await xmlInput.count()) > 0 && (await xmlInput.first().isVisible()) ) { const testXml = ` ` await xmlInput.first().fill(testXml) await expect(xmlInput.first()).toHaveValue(testXml) } // Test passes if XML input doesn't exist or isn't accessible }) test("keyboard shortcuts work in chat input", async ({ page }) => { 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 }) // 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") }) test("can undo/redo in chat input", async ({ page }) => { 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 }) // Type text await chatInput.fill("First text") await chatInput.press("Tab") // Blur to register change await chatInput.focus() await chatInput.fill("Second text") // Undo with Ctrl+Z await chatInput.press("ControlOrMeta+z") // Value might revert (depends on browser/implementation) // At minimum, shouldn't crash await page.waitForTimeout(500) }) test("chat input handles special characters", async ({ page }) => { 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 }) const specialText = "Test <>&\"' special chars 日本語 中文 🎉" await chatInput.fill(specialText) await expect(chatInput).toHaveValue(specialText) }) test("long text in chat input scrolls", async ({ page }) => { 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 }) // 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) }) })