67 lines
3.6 KiB
TypeScript
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)
|
|
})
|