@@ -0,0 +1,190 @@
|
||||
// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
|
||||
// The default Agent and Help integrations over real Postgres and the simulated Cloud protocol.
|
||||
import { expect, type Page, test } from "@playwright/test"
|
||||
import { moduleSelected } from "../modules/inclusion"
|
||||
import { call, makeOrganization, signIn, unique, violations } from "./support/app"
|
||||
|
||||
test.skip(
|
||||
!["agent", "guides", "support"].every((id) => moduleSelected(id as "agent" | "guides" | "support")),
|
||||
"Assistance was explicitly excluded",
|
||||
)
|
||||
|
||||
async function prepare(page: Page) {
|
||||
await signIn(page, `${unique("owner")}@example.test`)
|
||||
const scope = await makeOrganization(page)
|
||||
await page.goto(`/${scope.org}/${scope.workspace}/modules/notes`)
|
||||
// Functional panel checks start after the person resolves privacy choices.
|
||||
// The first-visit consent flow has its own keyboard and accessibility suite.
|
||||
await page.getByRole("button", { name: "Reject analytics and replay", exact: true }).click()
|
||||
await expect(page.getByRole("region", { name: "Privacy choices", exact: true })).toHaveCount(0)
|
||||
return scope
|
||||
}
|
||||
|
||||
test("Agent streams and persists a marked answer, and keeps a draft when switching panels", async ({ page }, info) => {
|
||||
const errors: string[] = []
|
||||
page.on("pageerror", (error) => errors.push(error.message))
|
||||
const scope = await prepare(page)
|
||||
await expect(page.locator("[data-conusai-guide-root]")).toHaveCount(0)
|
||||
await page.getByRole("button", { name: "Agent", exact: true }).click()
|
||||
await expect(page.getByText("Ask the Agent", { exact: true })).toBeVisible()
|
||||
await expect(page.getByText("The Agent is AI and can make mistakes. Check anything important.")).toBeVisible()
|
||||
const input = page.getByRole("textbox", { name: "Message the Agent" })
|
||||
await input.fill("Explain this workspace")
|
||||
await input.press("Shift+Enter")
|
||||
await input.press("x")
|
||||
await expect(input).toHaveValue("Explain this workspace\nx")
|
||||
await input.press("Enter")
|
||||
const answer = page.getByRole("log").getByText("This is the fake platform: no model is behind it.", { exact: true })
|
||||
await expect(answer).toBeVisible()
|
||||
await expect
|
||||
.poll(async () => {
|
||||
const list = await call<{ conversations: { conversationId: string }[] }>(page, "assistant.conversation.list", {
|
||||
org: scope.org,
|
||||
workspace: scope.workspace,
|
||||
})
|
||||
const id = list.conversations[0]?.conversationId
|
||||
if (!id) return null
|
||||
const conversation = await call<{ turns: { status: string; answerProvenance: unknown }[] }>(
|
||||
page,
|
||||
"assistant.conversation.get",
|
||||
{ org: scope.org, workspace: scope.workspace, conversationId: id },
|
||||
)
|
||||
return conversation.turns.at(-1)
|
||||
})
|
||||
.toMatchObject({ status: "complete", answerProvenance: { ai_generated: true } })
|
||||
await expect(input).toBeFocused()
|
||||
await expect(page.locator(".kit-side-panel-head")).toBeInViewport({ ratio: 1 })
|
||||
expect(await page.evaluate(() => window.scrollY)).toBe(0)
|
||||
await page.screenshot({ path: info.outputPath("agent-desktop.png") })
|
||||
expect(await violations(page)).toEqual([])
|
||||
await page.reload()
|
||||
await page.getByRole("button", { name: "Agent", exact: true }).click()
|
||||
await expect(answer).toBeVisible()
|
||||
await input.fill("Keep this draft")
|
||||
await page.getByRole("button", { name: "Full screen", exact: true }).click()
|
||||
const full = page.getByRole("dialog", { name: "Agent", exact: true })
|
||||
await expect(full).toBeVisible()
|
||||
await expect(full.getByRole("textbox", { name: "Message the Agent" })).toHaveValue("Keep this draft")
|
||||
await expect(answer).toBeVisible()
|
||||
expect(await violations(page)).toEqual([])
|
||||
await page.screenshot({ path: info.outputPath("agent-fullscreen.png") })
|
||||
await full.getByRole("button", { name: "Restore panel", exact: true }).click()
|
||||
await expect(full).toBeHidden()
|
||||
await expect(input).toHaveValue("Keep this draft")
|
||||
await page.getByRole("button", { name: "Help", exact: true }).click()
|
||||
await page.getByRole("tab", { name: "Agent", exact: true }).click()
|
||||
await expect(input).toHaveValue("Keep this draft")
|
||||
await input.focus()
|
||||
await page.keyboard.press("Escape")
|
||||
const guard = page.getByRole("alertdialog")
|
||||
await expect(guard).toBeVisible()
|
||||
await guard.getByRole("button", { name: /Keep/ }).click()
|
||||
await expect(input).toHaveValue("Keep this draft")
|
||||
await input.fill("")
|
||||
await page.getByRole("button", { name: "Help", exact: true }).click()
|
||||
await page.getByRole("tab", { name: "Agent", exact: true }).click()
|
||||
await expect(answer).toBeVisible()
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
|
||||
test("Help runs a requested Notes tour and explains local Support on a phone", async ({ page }, info) => {
|
||||
const scope = await prepare(page)
|
||||
await page.setViewportSize({ width: 375, height: 812 })
|
||||
await page.goto(`/${scope.org}/${scope.workspace}/${scope.project}/notes`)
|
||||
await expect(page.locator("[data-conusai-guide-root]")).toHaveCount(0)
|
||||
await page.getByRole("button", { name: "Help", exact: true }).click()
|
||||
const panel = page.getByRole("dialog", { name: "Help", exact: true })
|
||||
await expect(panel.getByText("Get started with notes", { exact: true })).toBeVisible()
|
||||
await panel.getByRole("checkbox", { name: "Tell your team where the notes are" }).check()
|
||||
await expect(panel.getByRole("checkbox", { name: "Tell your team where the notes are" })).toBeChecked()
|
||||
await panel.getByRole("button", { name: "Start guide: Take the notes tour", exact: true }).click()
|
||||
const tour = page.locator("[data-conusai-guide-root]")
|
||||
await expect(tour.getByRole("heading", { name: "Write a note", exact: true })).toBeVisible()
|
||||
await expect(panel).toBeHidden()
|
||||
await page.screenshot({ path: info.outputPath("guides-mobile-tour.png") })
|
||||
await tour.getByRole("button", { name: "Next", exact: true }).click()
|
||||
await tour.getByRole("button", { name: "Next", exact: true }).click()
|
||||
await tour.getByRole("button", { name: "Done", exact: true }).click()
|
||||
await page.getByRole("button", { name: "Help", exact: true }).click()
|
||||
await panel.getByRole("tab", { name: "Support", exact: true }).click()
|
||||
await expect(panel.getByText("Support is not connected in this local demo", { exact: true })).toBeVisible()
|
||||
await expect(panel.getByRole("button", { name: "Try again" })).toHaveCount(0)
|
||||
await expect(page.locator(".support-launcher")).toHaveCount(0)
|
||||
expect(await violations(page)).toEqual([])
|
||||
expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true)
|
||||
await page.screenshot({ path: info.outputPath("help-mobile-support.png") })
|
||||
})
|
||||
|
||||
test("Support distinguishes loading, a retryable failure and a permission refusal", async ({ page }, info) => {
|
||||
await prepare(page)
|
||||
let release: (() => void) | undefined
|
||||
const wait = new Promise<void>((resolve) => {
|
||||
release = resolve
|
||||
})
|
||||
await page.route("**/api/v1/support.widget.config", async (route) => {
|
||||
await wait
|
||||
await route.fulfill({
|
||||
status: 503,
|
||||
contentType: "application/problem+json",
|
||||
body: JSON.stringify({ status: 503, title: "Unavailable" }),
|
||||
})
|
||||
})
|
||||
await page.getByRole("button", { name: "Help", exact: true }).click()
|
||||
const panel = page.locator("[data-slot=side-panel]")
|
||||
await panel.getByRole("tab", { name: "Support", exact: true }).click()
|
||||
await expect(panel.locator("[data-slot=support-loading]")).toBeVisible()
|
||||
await page.screenshot({ path: info.outputPath("support-loading.png") })
|
||||
release?.()
|
||||
await expect(panel.locator("[data-slot=support-unavailable]")).toBeVisible()
|
||||
await page.unroute("**/api/v1/support.widget.config")
|
||||
await panel.getByRole("button", { name: "Try again", exact: true }).focus()
|
||||
await page.keyboard.press("Enter")
|
||||
await expect(panel.locator("[data-slot=support-offline]")).toBeVisible()
|
||||
await page.route("**/api/v1/support.widget.config", (route) =>
|
||||
route.fulfill({
|
||||
status: 403,
|
||||
contentType: "application/problem+json",
|
||||
body: JSON.stringify({ status: 403, title: "Forbidden" }),
|
||||
}),
|
||||
)
|
||||
await page.reload()
|
||||
await page.getByRole("button", { name: "Help", exact: true }).click()
|
||||
await panel.getByRole("tab", { name: "Support", exact: true }).click()
|
||||
await expect(panel.locator("[data-slot=support-denied]")).toBeVisible()
|
||||
await expect(panel.getByRole("button", { name: "Try again" })).toHaveCount(0)
|
||||
})
|
||||
|
||||
test("navigation keeps the Agent draft and respects reduced motion", async ({ page }) => {
|
||||
const errors: string[] = []
|
||||
page.on("pageerror", (error) => errors.push(error.message))
|
||||
const scope = await prepare(page)
|
||||
await page.emulateMedia({ reducedMotion: "no-preference" })
|
||||
await page.evaluate(() => {
|
||||
const native = document.startViewTransition.bind(document)
|
||||
const proof = window as unknown as { transitions: number; sameDocument: string }
|
||||
proof.transitions = 0
|
||||
proof.sameDocument = "preserved"
|
||||
document.startViewTransition = ((...args: Parameters<typeof native>) => {
|
||||
proof.transitions += 1
|
||||
return native(...args)
|
||||
}) as typeof document.startViewTransition
|
||||
})
|
||||
await page.getByRole("button", { name: "Agent", exact: true }).click()
|
||||
const input = page.getByRole("textbox", { name: "Message the Agent" })
|
||||
await input.fill("Keep this while navigating")
|
||||
await page.getByRole("link", { name: "Workspace overview", exact: true }).click()
|
||||
await expect(page).toHaveURL(new RegExp(`/${scope.org}/${scope.workspace}$`))
|
||||
await expect(input).toHaveValue("Keep this while navigating")
|
||||
await expect
|
||||
.poll(() => page.evaluate(() => (window as unknown as { transitions: number }).transitions))
|
||||
.toBeGreaterThan(0)
|
||||
await page.waitForFunction(() => !document.documentElement.matches(":active-view-transition"))
|
||||
const before = await page.evaluate(() => (window as unknown as { transitions: number }).transitions)
|
||||
await page.emulateMedia({ reducedMotion: "reduce" })
|
||||
await page.getByRole("link", { name: "Notes in this workspace", exact: true }).click()
|
||||
await expect(page).toHaveURL(new RegExp(`/${scope.org}/${scope.workspace}/modules/notes$`))
|
||||
await expect(input).toHaveValue("Keep this while navigating")
|
||||
expect(await page.evaluate(() => (window as unknown as { transitions: number }).transitions)).toBe(before)
|
||||
expect(await page.evaluate(() => (window as unknown as { sameDocument: string }).sameDocument)).toBe("preserved")
|
||||
expect(errors).toEqual([])
|
||||
})
|
||||
Reference in New Issue
Block a user