117 lines
4.4 KiB
TypeScript
117 lines
4.4 KiB
TypeScript
// Every string a page of the module shows is a message in English and in Lithuanian (spec §10.4), the
|
|
// Lithuanian plural forms are real ones, and no component of the module carries a string of its own.
|
|
|
|
import { readdirSync, readFileSync } from "node:fs"
|
|
import { join } from "node:path"
|
|
import { buildRegistry } from "@conusai-kit/core"
|
|
import { createI18n, lintUiStrings } from "@conusai-kit/i18n"
|
|
import { describe, expect, it } from "vitest"
|
|
import { appModules } from "../../index.js"
|
|
import { notificationLabels } from "../adapters/notifications.js"
|
|
import { feedbackLabels } from "../guides/feedback-labels.js" // kit:guides
|
|
import { attachmentLabels, deleteLabels, resourceLabels } from "../ui/labels.js"
|
|
|
|
// The messages as the app has them: from the manifest, flattened and merged by the kit.
|
|
const catalogues = buildRegistry([...appModules]).catalogues
|
|
const en = catalogues.en?.notes ?? {}
|
|
const lt = catalogues.lt?.notes ?? {}
|
|
|
|
/** The keys a builder asks for, recorded by running it with a lookup that notes each one. */
|
|
function keysUsed(): string[] {
|
|
const asked: string[] = []
|
|
const t = (key: string) => {
|
|
asked.push(key)
|
|
return key
|
|
}
|
|
resourceLabels(t)
|
|
attachmentLabels(t)
|
|
deleteLabels(t)
|
|
notificationLabels(t, "Launch")
|
|
feedbackLabels(t) // kit:guides
|
|
return asked
|
|
}
|
|
|
|
describe("the notes catalogues", () => {
|
|
it("hold the same keys in English and in Lithuanian", () => {
|
|
expect(Object.keys(lt).sort()).toEqual(Object.keys(en).sort())
|
|
})
|
|
|
|
it("hold no empty message", () => {
|
|
for (const [key, text] of [...Object.entries(en), ...Object.entries(lt)]) expect(text.trim(), key).not.toBe("")
|
|
})
|
|
|
|
it("hold every message the components' labels ask for, in both languages", () => {
|
|
const asked = keysUsed()
|
|
expect(asked.length).toBeGreaterThan(25)
|
|
for (const key of asked) {
|
|
expect(en[key as keyof typeof en], `en is missing notes.${key}`).toBeTruthy()
|
|
expect(lt[key as keyof typeof lt], `lt is missing notes.${key}`).toBeTruthy()
|
|
}
|
|
})
|
|
|
|
it("hold no message nothing asks for, so a renamed label does not leave a dead string behind", () => {
|
|
// The pages and the navigation ask for these directly, not through a label builder.
|
|
const direct = new Set([
|
|
"workspace.title",
|
|
"workspace.intro",
|
|
"workspace.filter",
|
|
"workspace.apply",
|
|
"workspace.clear",
|
|
"workspace.noProjects",
|
|
"workspace.empty",
|
|
"workspace.selected",
|
|
"title",
|
|
"intro",
|
|
"detail.back",
|
|
"detail.changed",
|
|
"detail.edit",
|
|
])
|
|
const asked = new Set([...keysUsed(), ...direct])
|
|
expect(Object.keys(en).filter((key) => !asked.has(key))).toEqual([])
|
|
})
|
|
|
|
it("answer the navigation label the manifest names", () => {
|
|
const nav = appModules.find((module) => module.id === "notes")?.ui?.nav ?? []
|
|
expect(nav.map((item) => item.label)).toEqual(["notes.title", "notes.workspace.title"])
|
|
expect(en.title).toBeTruthy()
|
|
expect(lt.title).toBeTruthy()
|
|
})
|
|
})
|
|
|
|
describe("the Lithuanian plural forms", () => {
|
|
const count = (locale: "en" | "lt", n: number) =>
|
|
createI18n(locale, { en: { notes: en }, lt: { notes: lt } }).message("notes.attachments.count", { count: n })
|
|
|
|
it("select one, few and other the way Lithuanian counts", () => {
|
|
expect(count("lt", 1)).toBe("1 priedas")
|
|
expect(count("lt", 2)).toBe("2 priedai")
|
|
expect(count("lt", 9)).toBe("9 priedai")
|
|
expect(count("lt", 10)).toBe("10 priedų")
|
|
expect(count("lt", 21)).toBe("21 priedas")
|
|
})
|
|
|
|
it("keep English to its two forms", () => {
|
|
expect(count("en", 1)).toBe("1 attachment")
|
|
expect(count("en", 2)).toBe("2 attachments")
|
|
expect(lt["attachments.count"]).toContain("few {")
|
|
expect(en["attachments.count"]).not.toContain("few {")
|
|
})
|
|
})
|
|
|
|
describe("the components", () => {
|
|
const dir = new URL("../ui/", import.meta.url).pathname
|
|
const sources = readdirSync(dir)
|
|
.filter((name) => name.endsWith(".tsx"))
|
|
.map((name) => ({ name, text: readFileSync(join(dir, name), "utf8") }))
|
|
|
|
it("carry no hard-coded user-facing string", () => {
|
|
expect(sources.length).toBeGreaterThanOrEqual(4)
|
|
for (const { name, text } of sources) expect(lintUiStrings(text, name), name).toEqual([])
|
|
})
|
|
|
|
it("carry no colour literal: the brand tokens are the only source of colour", () => {
|
|
for (const { name, text } of sources)
|
|
expect(text, name).not.toMatch(/#[0-9a-fA-F]{3,8}\b|\brgb\(|\bhsl\(|\boklch\(/)
|
|
})
|
|
})
|