Files
app/frontend/tests/chat.spec.ts
T
stroblme 4cb028de41 Chat: one phone header button, quick-prompt chips, double-click to reuse a message
The phone header carries a single speech bubble: Control on Home, the
assistant elsewhere. Chips above the input offer the user's most sent
messages per tab, then starters; a tap or a double-click on a sent or
heard message fills the input without sending.
2026-10-27 17:39:36 +01:00

124 lines
4.1 KiB
TypeScript

import { expect, type Page, test } from "@playwright/test"
/**
* The chat on a phone: its one button in the header, which tab it opens, and
* how a message gets back into the input (a double-click or double-tap, a
* quick-prompt chip).
*
* A provider and a wren node are instance state, not something a suite should
* set up, so the three endpoints the panel reads are answered here.
*/
test.use({ viewport: { width: 393, height: 851 }, hasTouch: true })
const SAID = "which flows are running"
test.beforeEach(async ({ page }) => {
await page.route("**/api/v1/ai/status", (route) =>
route.fulfill({ json: { available: true, model: "test" } }),
)
await page.route("**/api/v1/ai/control", (route) =>
route.fulfill({
json: [
{
id: "home.control",
flow: "home",
title: "Home · Control",
prompt_msg: "home.command",
watch: [{ msg: "home.command", kind: "text", node: "" }],
},
],
}),
)
await page.route("**/api/v1/ai/chat", (route) =>
route.request().method() === "GET"
? route.fulfill({
json: [
{ role: "user", content: SAID },
{ role: "assistant", content: "Two." },
],
})
: route.fallback(),
)
})
const pressed = (page: Page, tab: string) =>
page.getByTestId("chat-tabs").getByRole("button", { name: tab })
test("one chat button opens Control on Home", async ({ page }) => {
await page.goto("/")
await expect(page.getByTestId("chat-trigger")).toHaveCount(1)
// Neither the old assistant button nor Home's wider-screen one is on screen.
await expect(page.getByRole("button", { name: "Assistant" })).toHaveCount(0)
await expect(page.getByRole("button", { name: "Chat" })).toHaveCount(1)
await page.getByTestId("chat-trigger").click()
await expect(pressed(page, "Control")).toHaveAttribute("aria-pressed", "true")
})
test("the same button opens the assistant elsewhere", async ({ page }) => {
await page.goto("/flows")
await page.getByTestId("chat-trigger").click()
await expect(pressed(page, "Build")).toHaveAttribute("aria-pressed", "true")
})
test("a message and a chip go back into the input", async ({ page }) => {
await page.goto("/flows")
await page.getByTestId("chat-trigger").click()
const input = page.getByTestId("chat-input")
const said = page.getByText(SAID)
await said.dblclick()
await expect(input).toHaveValue(SAID)
await expect(input).toBeFocused()
// Something typed leaves no room for the row.
await expect(page.getByTestId("chat-prompts")).toHaveCount(0)
await input.fill("")
const box = await said.boundingBox()
if (!box) throw new Error("the message is not on screen")
const x = box.x + box.width / 2
const y = box.y + box.height / 2
await page.touchscreen.tap(x, y)
await page.touchscreen.tap(x, y)
await expect(input).toHaveValue(SAID)
await input.fill("")
const chip = page.getByTestId("chat-prompt").first()
const text = (await chip.textContent()) ?? ""
expect(text).not.toBe("")
await chip.click()
await expect(input).toHaveValue(text)
// A chip fills the input; it does not send.
await expect(page.getByText(text)).toHaveCount(0)
})
test("what was sent comes back as a chip, the most used first", async ({
page,
}) => {
await page.route("**/api/v1/ai/chat", (route) =>
route.request().method() === "POST"
? route.fulfill({
contentType: "text/event-stream",
body: 'data: {"type":"done"}\n\n',
})
: route.fallback(),
)
await page.goto("/flows")
await page.getByTestId("chat-trigger").click()
const input = page.getByTestId("chat-input")
for (const text of ["once", "twice", "twice"]) {
await input.fill(text)
// Sending is refused while the last turn is still running.
await expect(page.getByRole("button", { name: "Send" })).toBeEnabled()
await input.press("Enter")
await expect(input).toHaveValue("")
}
await page.reload()
await page.getByTestId("chat-trigger").click()
const chips = page.getByTestId("chat-prompt")
await expect(chips.nth(0)).toHaveText("twice")
await expect(chips.nth(1)).toHaveText("once")
})