Files
template-app/e2e/notes-live.spec.ts
2026-11-14 09:56:45 +00:00

67 lines
3.6 KiB
TypeScript

// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0
// The live note list (umbrella TS6.2 to TS6.6) in a real browser against the whole stack: a note
// added, renamed and deleted in one tab appears, changes and goes in another without a reload; the
// write answers ConusAI-Txid; another organization's member sees none of it and cannot open the
// first organization's stream. Every screen passes axe.
import { expect, test } from "@playwright/test"
import { call, callRaw, makeOrganization, signIn, unique, violations } from "./support/app"
test("a note written in one tab appears, changes and goes in another without a reload", async ({ browser }) => {
const context = await browser.newContext()
const watcher = await context.newPage()
await signIn(watcher, `${unique("live")}@example.test`)
const scope = await makeOrganization(watcher, "live")
const list = `/${scope.org}/${scope.workspace}/${scope.project}/notes`
await watcher.goto(list)
await expect(watcher.getByText("No notes yet.")).toBeVisible()
expect(await violations(watcher)).toEqual([])
// Mark the document: a reload would lose the mark, so finding it later proves there was none.
await watcher.evaluate(() => {
;(window as unknown as { liveMark: string }).liveMark = "kept"
})
const writer = await context.newPage()
await writer.goto(list)
const csrf = (await writer.locator('meta[name="csrf-token"]').getAttribute("content")) ?? ""
const created = await callRaw(writer.request, csrf, "notes.note.create", { ...scope, title: "Live one", body: "b" })
expect(created.ok()).toBe(true)
expect(created.headers()["conusai-txid"]).toMatch(/^\d+$/)
const note = (await created.json()) as { noteId: string; version: number }
await expect(watcher.getByRole("row", { name: /Live one/ })).toBeVisible()
await call(writer, "notes.note.update", {
...scope,
noteId: note.noteId,
title: "Live renamed",
body: "b",
expectedVersion: note.version,
})
await expect(watcher.getByRole("row", { name: /Live renamed/ })).toBeVisible()
await call(writer, "notes.note.delete", { ...scope, noteId: note.noteId })
await expect(watcher.getByRole("row", { name: /Live renamed/ })).toHaveCount(0)
expect(await watcher.evaluate(() => (window as unknown as { liveMark?: string }).liveMark)).toBe("kept")
expect(await violations(watcher)).toEqual([])
// A read answers no transaction id.
const read = await callRaw(writer.request, csrf, "notes.note.list", scope)
expect(read.headers()["conusai-txid"]).toBeUndefined()
// Another organization's member: refused the first organization's stream, and blind to its writes.
const other = await (await browser.newContext()).newPage()
await signIn(other, `${unique("outsider")}@example.test`)
const theirs = await makeOrganization(other, "other")
const otherCsrf = (await other.locator('meta[name="csrf-token"]').getAttribute("content")) ?? ""
const foreign = await other.request.post(`/api/v1/orgs/${scope.org}/live`, {
data: { protocol: "clp/1", after: null, entities: ["notes.note"], topics: [] },
headers: { "x-csrf-token": otherCsrf, "content-type": "application/json", "sec-fetch-site": "same-origin" },
})
expect(foreign.status()).toBe(404)
await other.goto(`/${theirs.org}/${theirs.workspace}/${theirs.project}/notes`)
await expect(other.getByText("No notes yet.")).toBeVisible()
await call(writer, "notes.note.create", { ...scope, title: "Not for others", body: "b" })
await expect(watcher.getByRole("row", { name: /Not for others/ })).toBeVisible()
await expect(other.getByText("No notes yet.")).toBeVisible()
await expect(other.getByRole("row", { name: /Not for others/ })).toHaveCount(0)
})