ConusAI vendor v0.1.0-beta.114
ci / gate (push) Waiting to run

This commit is contained in:
ConusAI vendor v0.1.0-beta.114
2026-11-14 09:56:45 +00:00
commit 1ce8d23c35
867 changed files with 122104 additions and 0 deletions
+142
View File
@@ -0,0 +1,142 @@
// 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<string, string | number>) => 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(<UiLabelsProvider labels={kitStrings}>{node}</UiLabelsProvider>)
const inProvider = (node: React.ReactNode) =>
renderToStaticMarkup(
<DataProvider client={createDataClient({ fetch: async () => new Response(null, { status: 501 }) })}>
{node}
</DataProvider>,
)
const note = (over: Partial<NoteView> = {}): 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(
<FrameProvider
router={{
pathname: "/acme/ws/p/notes",
search: new URLSearchParams(),
refresh: async () => undefined,
navigate: () => undefined,
}}
permissions={permissions}
>
<SidePanelProvider>
<NotesResource slugs={slugs} seed={notes} labels={resourceLabels(t)} intro="Short notes" locale="en" />
</SidePanelProvider>
</FrameProvider>,
)
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<typeof Attachments>[0]["seed"]) =>
inProvider(
<Attachments
slugs={slugs}
noteId="n1"
seed={seed}
enabled={enabled}
accept={["text/plain", "application/pdf"]}
labels={attachmentLabels(t)}
locale="en"
/>,
)
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&amp;workspace=ws&amp;project=p&amp;fileId=f1"')
expect(html).toContain(">Download minutes.txt</a>")
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 = /<input [^>]*\bid="([^"]+)"[^>]*\bname="file"/.exec(html)?.[1]
expect(id).toBeTruthy()
expect(html).toMatch(new RegExp(`<label [^>]*for="${id}"[^>]*>Attach a file</label>`))
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(
<DeleteNote slugs={slugs} noteId="n1" listHref="/acme/ws/p/notes" labels={deleteLabels(t)} />,
)
expect(html).toContain("Delete note")
expect(html).not.toContain("Yes, delete the note")
})
})