// SPDX-License-Identifier: LicenseRef-ConusAI-Template-1.0 // The example module in a browser (plan item AT2.2): add a note, attach a file through the files // module's Blob upload, change and remove things, and delete the note -- with axe on every screen it // shows. Another organization's member sees none of it. import { readFileSync } from "node:fs" import { expect, type Page, test } from "@playwright/test" import { call, callRaw, makeOrganization, platformState, signIn, unique, violations } from "./support/app" const MINUTES = Buffer.from("Agreed on Friday: ship the pilot.\n") async function uploadText(page: Page, scope: Awaited>, name: string) { const begun = await call<{ fileId: string; parts: Array<{ number: number }> }>(page, "files.file.beginUpload", { ...scope, filename: name, contentType: "text/plain", size: 1, }) const csrf = await page.locator('meta[name="csrf-token"]').getAttribute("content") const part = begun.parts[0] if (!part) throw new Error("the one-byte upload did not return a part") const sent = await page.request.put( `/api/v1/files.file.putPart?${new URLSearchParams({ ...scope, fileId: begun.fileId, part: String(part.number) })}`, { data: Buffer.from("x"), headers: { "content-type": "application/octet-stream", "x-csrf-token": csrf ?? "", "sec-fetch-site": "same-origin", }, }, ) expect(sent.ok()).toBe(true) const receipt = (await sent.json()) as { etag: string } await call(page, "files.file.completeUpload", { ...scope, fileId: begun.fileId, parts: [{ number: part.number, etag: receipt.etag }], }) return begun.fileId } test("a note takes attachments, is edited and removed, and every screen passes axe", async ({ page }) => { await signIn(page, `${unique("author")}@example.test`) const { org, workspace, project } = await makeOrganization(page) const base = `/${org}/${workspace}/${project}/notes` // the organization's home links to the module from the manifest's navigation entry await page.goto(`/${org}`) await expect(page.getByRole("link", { name: "Notes", exact: true })).toBeVisible() await page.goto(base) await expect(page.getByRole("heading", { level: 1, name: "Notes" })).toBeVisible() await expect(page.getByText("No notes yet.")).toBeVisible() expect(await violations(page)).toEqual([]) await page.getByLabel("Title").fill("Kick-off") await page.getByLabel("Text").fill("Agenda and owners") await page.getByRole("button", { name: "Add note" }).click() // the note's own page await expect(page.getByRole("heading", { level: 1, name: "Kick-off" })).toBeVisible() await expect(page.getByText("Nothing is attached.")).toBeVisible() expect(await violations(page)).toEqual([]) // an upload through the files module (the platform's Blob store), then the link await page.getByLabel("Attach a file").setInputFiles({ name: "minutes.txt", mimeType: "text/plain", buffer: MINUTES }) await page.getByRole("button", { name: "Attach", exact: true }).click() await expect(page.getByRole("row", { name: /minutes\.txt.*text\/plain.*1 KB/ })).toBeVisible() await expect(page.getByText("1 attachment", { exact: true })).toBeVisible() expect(await violations(page)).toEqual([]) const download = page.waitForEvent("download") await page.getByRole("link", { name: "Download minutes.txt" }).click() const downloaded = await download expect(downloaded.suggestedFilename()).toBe("minutes.txt") const downloadedPath = await downloaded.path() expect(readFileSync(downloadedPath)).toEqual(MINUTES) // what the worker sent the platform: ONE audit event and ONE usage fact for the upload, one audit event // and one usage fact for the note, one audit event for the link const noteId = page.url().split("/").pop() ?? "" const { files } = await call<{ files: Array<{ fileId: string }> }>(page, "files.file.list", { org, workspace, project, }) expect(files).toHaveLength(1) const fileId = files[0]?.fileId ?? "" await expect(async () => { const state = await platformState() // The target carries the organization as `/` (src/server/audit.ts), so a // resource matches by its trailing segment. const audited = (id: string) => state.audit.filter((event) => event.resource_id?.endsWith(`/${id}`)).map((event) => event.action) expect(audited(fileId)).toEqual(["files.file_uploaded"]) expect(audited(noteId).sort()).toEqual(["notes.attachment_added", "notes.note_created"]) const metered = (key: string) => state.usage.filter((fact) => fact.idempotency_key === key) expect(metered(`files_bytes_stored.${fileId}`)).toEqual([ expect.objectContaining({ meter: "files_bytes_stored", quantity: String(MINUTES.length) }), ]) expect(metered(`notes_created.${noteId}`)).toEqual([ expect.objectContaining({ meter: "notes_created", quantity: "1" }), ]) // and the bytes are in the platform's Blob store, once, under the organization's own prefix expect( state.blobs.filter((blob) => blob.size === MINUTES.length && blob.pathname.includes(`/files/`)), ).toHaveLength(1) }).toPass({ timeout: 30_000 }) // edit: the form carries the version it was read at await page.getByLabel("Title").fill("Kick-off call") await page.getByRole("button", { name: "Save changes" }).click() await expect(page.getByRole("heading", { level: 1, name: "Kick-off call" })).toBeVisible() // the list shows the note with its attachment count await page.goto(base) await expect(page.getByRole("row", { name: /Kick-off call.*1/ })).toBeVisible() expect(await violations(page)).toEqual([]) // remove the attachment, then delete the note after being asked await page.getByRole("link", { name: "Open Kick-off call" }).click() await page.getByRole("button", { name: "Remove minutes.txt" }).click() await expect(page.getByText("Nothing is attached.")).toBeVisible() await page.getByRole("button", { name: "Delete note" }).click() expect(await violations(page)).toEqual([]) await page.getByRole("button", { name: "Yes, delete the note" }).click() await expect(page.getByText("No notes yet.")).toBeVisible() }) test("a second editor's stale save is refused and says so", async ({ page }) => { await signIn(page, `${unique("author")}@example.test`) const { org, workspace, project } = await makeOrganization(page) const base = `/${org}/${workspace}/${project}/notes` await page.goto(base) await page.getByLabel("Title").fill("Plan") await page.getByRole("button", { name: "Add note" }).click() await expect(page.getByRole("heading", { level: 1, name: "Plan" })).toBeVisible() // the same note opened in a second tab, saved there first const second = await page.context().newPage() await second.goto(page.url()) await second.getByLabel("Title").fill("Plan v2") await second.getByRole("button", { name: "Save changes" }).click() await expect(second.getByRole("heading", { level: 1, name: "Plan v2" })).toBeVisible() // the first tab still holds version 1 await page.getByLabel("Title").fill("Plan v3") await page.getByRole("button", { name: "Save changes" }).click() await expect(page.getByRole("alert")).toBeVisible() expect(await violations(page)).toEqual([]) await second.close() }) test("concurrent attachments cannot exceed the per-note cap", async ({ page }) => { await signIn(page, `${unique("quota-owner")}@example.test`) const scope = await makeOrganization(page) const note = await call<{ noteId: string }>(page, "notes.note.create", { ...scope, title: "Attachment quota", body: "", }) const existingFiles: string[] = [] for (let index = 0; index < 19; index += 1) { const fileId = await uploadText(page, scope, `quota-${index}.txt`) existingFiles.push(fileId) await call(page, "notes.attachment.add", { ...scope, noteId: note.noteId, fileId }) } const contenders = await Promise.all([ uploadText(page, scope, "quota-race-a.txt"), uploadText(page, scope, "quota-race-b.txt"), ]) const csrf = await page.locator('meta[name="csrf-token"]').getAttribute("content") const results = await Promise.all( contenders.map((fileId) => callRaw(page.request, csrf ?? "", "notes.attachment.add", { ...scope, noteId: note.noteId, fileId }), ), ) expect(results.map((result) => result.status()).sort()).toEqual([200, 409]) const detail = await call<{ attachments: unknown[] }>(page, "notes.note.get", { ...scope, noteId: note.noteId }) expect(detail.attachments).toHaveLength(20) expect(existingFiles).toHaveLength(19) }) test("another organization's member does not see the notes", async ({ page, browser }) => { await signIn(page, `${unique("owner")}@example.test`) const mine = await makeOrganization(page) const stranger = await browser.newPage() await signIn(stranger, `${unique("stranger")}@example.test`) const response = await stranger.goto(`/${mine.org}/${mine.workspace}/${mine.project}/notes`) expect(response?.status()).toBe(404) await expect(stranger.getByRole("heading", { level: 1, name: "Page not found" })).toBeVisible() await stranger.close() }) test("a path the module does not know is the not-found page", async ({ page }) => { await signIn(page, `${unique("author")}@example.test`) const { org, workspace, project } = await makeOrganization(page) const unknownModule = await page.goto(`/${org}/${workspace}/${project}/nothing-here`) expect(unknownModule?.status()).toBe(404) const unknownNote = await page.goto(`/${org}/${workspace}/${project}/notes/00000000-0000-0000-0000-000000000000`) expect(unknownNote?.status()).toBe(404) })