// The components' markup, rendered on the server the way a page renders it: what a screen reader and // a keyboard user get is in the HTML, so the accessibility basics are asserted there -- a name for // every control, a caption and scoped headers for every table, a status that says what a colour would. import { buildRegistry } from "@conusai-kit/core" import { builtInCatalogues, createI18n, uiLabels } from "@conusai-kit/i18n" import { createDataClient, DataProvider } from "@conusai-kit/ui/data" import { FrameProvider, SidePanelProvider } from "@conusai-kit/ui/patterns" import { UiLabelsProvider } from "@conusai-kit/ui/primitives" import { renderToStaticMarkup as renderMarkup } from "react-dom/server" import { describe, expect, it } from "vitest" import { appModules } from "../../index.js" import type { NoteView } from "../contract/index.js" import { Attachments, attachmentLabels, DeleteNote, deleteLabels, NotesResource, resourceLabels } from "../ui/index.js" const catalogues = buildRegistry([...appModules]).catalogues const i18n = createI18n("en", { en: { notes: catalogues.en?.notes ?? {} } }) const t = (key: string, values?: Record) => i18n.message(`notes.${key}`, values) const slugs = { org: "acme", workspace: "ws", project: "p" } const kitStrings = uiLabels(createI18n("en", builtInCatalogues)) const renderToStaticMarkup = (node: React.ReactNode) => renderMarkup({node}) const inProvider = (node: React.ReactNode) => renderToStaticMarkup( new Response(null, { status: 501 }) })}> {node} , ) const note = (over: Partial = {}): NoteView => ({ noteId: "n1", title: "Kick-off", body: "Agenda", version: 3, createdBy: "u1", createdAt: "2026-04-01T08:00:00.000Z", updatedAt: "2026-04-02T08:00:00.000Z", attachmentCount: 2, ...over, }) describe("the notes resource", () => { const render = (notes: readonly NoteView[], permissions: readonly string[]) => inProvider( undefined, navigate: () => undefined, }} permissions={permissions} > , ) const writer = ["notes.note.read", "notes.note.write", "notes.note.delete"] const html = render([note(), note({ noteId: "n2", title: "Retro" })], writer) it("is a named table with a header for every column and one for each row", () => { expect(html).toContain('aria-label="note"') expect(html).toContain("Kick-off") expect(html).toContain("Retro") expect(html).toContain('data-guide="notes.list"') }) it("offers a new note to whoever may write one, and only to them", () => { expect(html).toContain("New note") expect(html).toContain('data-guide="notes.new"') expect(render([note()], ["notes.note.read"])).not.toContain("New note") }) it("says so in words when there is nothing to list", () => { expect(render([], writer)).toContain("No notes yet.") }) }) describe("the attachments", () => { const render = (enabled: boolean, seed: Parameters[0]["seed"]) => inProvider( , ) const rows = [ { attachmentId: "a1", fileId: "f1", filename: "minutes.txt", contentType: "text/plain", size: 2048, attachedAt: "2026-04-02T08:00:00.000Z", }, ] it("lists each file in a captioned table and names each remove button by its file", () => { const html = render(true, rows) expect(html).toContain('aria-label="Attachments"') expect(html).toContain("minutes.txt") expect(html).toContain("2 KB") expect(html).toContain('aria-label="Remove minutes.txt"') expect(html).toContain('href="/api/v1/files.file.download?org=acme&workspace=ws&project=p&fileId=f1"') expect(html).toContain(">Download minutes.txt") expect(html).toContain("1 attachment") }) it("labels its file input, describes it with the hint, and asks for the types it accepts", () => { const html = render(true, []) const id = /]*\bid="([^"]+)"[^>]*\bname="file"/.exec(html)?.[1] expect(id).toBeTruthy() expect(html).toMatch(new RegExp(``)) expect(html).toContain("aria-describedby=") expect(html).toContain('accept="text/plain,application/pdf"') expect(html).toContain("Nothing is attached.") }) it("offers no upload when the feature flag is off, and says why", () => { const html = render(false, []) expect(html).not.toContain('type="file"') expect(html).toContain("Attachments are switched off for this project.") }) }) describe("the delete button", () => { it("asks first: the destructive button is not on the page until the person has said yes to a question", () => { const html = inProvider( , ) expect(html).toContain("Delete note") expect(html).not.toContain("Yes, delete the note") }) })