82 lines
3.3 KiB
TypeScript
82 lines
3.3 KiB
TypeScript
// The notes page on F10: the worked example. Its loader reads through the caller and returns plain data
|
|
// with every label translated; the browser receives that data as JSON, so it must survive the round trip.
|
|
|
|
import { notFound as notFoundError } from "@conusai-kit/core"
|
|
import { describe, expect, it, vi } from "vitest"
|
|
import { notesPage } from "../ui/pages.js"
|
|
|
|
const slugs = { org: "acme", workspace: "general", project: "demo" }
|
|
const i18n = {
|
|
locale: "lt",
|
|
message: (key: string, values?: Record<string, string | number>) =>
|
|
`${key}${values ? ` ${JSON.stringify(values)}` : ""}`,
|
|
date: (date: Date) => date.toISOString().slice(0, 10),
|
|
}
|
|
const load = (segments: readonly string[], call: (contract: unknown, input: unknown) => Promise<unknown>) =>
|
|
notesPage.load({
|
|
slugs,
|
|
segments,
|
|
search: {},
|
|
caller: { call } as never,
|
|
i18n: i18n as never,
|
|
container: {} as never,
|
|
})
|
|
|
|
const note = {
|
|
noteId: "note-1",
|
|
version: 3,
|
|
title: "Roles",
|
|
body: "text",
|
|
attachmentCount: 0,
|
|
createdAt: "2026-11-02T10:00:00.000Z",
|
|
updatedAt: "2026-11-02T10:00:00.000Z",
|
|
}
|
|
|
|
describe("the notes page's loader", () => {
|
|
it("returns the list, its first page and its labels as plain data", async () => {
|
|
const call = vi.fn(async () => ({ notes: [note] }))
|
|
const data = await load([], call)
|
|
expect(call.mock.calls[0]?.[1 as never]).toEqual(slugs)
|
|
expect(data).toMatchObject({ kind: "list", intro: "notes.intro", notes: [note] })
|
|
if (data.kind !== "list") throw new Error("not the list")
|
|
expect(data.labels.title).toBe("notes.title")
|
|
expect(JSON.parse(JSON.stringify(data))).toEqual(data)
|
|
})
|
|
|
|
it("returns one note with its attachments as the live list's seed and its date already formatted", async () => {
|
|
const call = vi.fn(async () => ({
|
|
note,
|
|
attachments: [
|
|
{ attachmentId: "a1", fileId: "f1", filename: "a.pdf", contentType: "application/pdf", size: 2048 },
|
|
],
|
|
attachmentsEnabled: true,
|
|
}))
|
|
const data = await load(["note-1"], call)
|
|
expect(call.mock.calls[0]?.[1 as never]).toEqual({ ...slugs, noteId: "note-1" })
|
|
if (data.kind !== "note") throw new Error("not the note")
|
|
expect(data.changed).toContain("2026-11-02")
|
|
// The rows are the procedure's own: the live list reads the same shape again on a change.
|
|
expect(data.attachments[0]).toMatchObject({ attachmentId: "a1", size: 2048 })
|
|
// The count and the size keep their placeholders for the browser, which knows the live numbers.
|
|
expect(data.attachmentLabels.count).toBe("notes.attachments.count")
|
|
expect(data.attachmentLabels.sizeKb).toBe("notes.attachments.sizeKb")
|
|
expect(data.note).toEqual({ noteId: "note-1", title: "Roles", body: "text" })
|
|
expect(JSON.parse(JSON.stringify(data))).toEqual(data)
|
|
})
|
|
|
|
it("answers a path it does not know as not found, without reading", async () => {
|
|
const call = vi.fn()
|
|
await expect(load(["note-1", "extra"], call)).rejects.toMatchObject({ status: 404 })
|
|
expect(call).not.toHaveBeenCalled()
|
|
expect(notFoundError("no such page").status).toBe(404)
|
|
})
|
|
|
|
it("hands a procedure's refusal on, for the shell to answer", async () => {
|
|
await expect(
|
|
load(["note-2"], async () => {
|
|
throw notFoundError("no such note")
|
|
}),
|
|
).rejects.toMatchObject({ status: 404 })
|
|
})
|
|
})
|