Files
kandev/apps/web/lib/task-colors.test.ts
2026-09-06 17:18:09 +01:00

93 lines
3.2 KiB
TypeScript

import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
clearLegacyTaskColors,
readLegacyTaskColors,
TASK_COLORS,
TASK_COLOR_BAR_CLASS,
TASK_COLOR_LABEL_KEYS,
TASK_COLORS_STORAGE_KEY,
parseSidebarTaskColors,
} from "./task-colors";
import { i18n, t } from "@/lib/i18n";
describe("legacy task colors migration input", () => {
beforeEach(() => {
window.localStorage.clear();
});
it("reads valid legacy colors without treating them as server values", () => {
window.localStorage.setItem(
TASK_COLORS_STORAGE_KEY,
JSON.stringify({
"task-1": "red",
"task-cleared": null,
"task-automatic-gray": "gray",
"task-2": "blue",
}),
);
expect(readLegacyTaskColors()).toEqual({ "task-1": "red", "task-2": "blue" });
});
it("returns no values for malformed legacy storage", () => {
window.localStorage.setItem(TASK_COLORS_STORAGE_KEY, "{not json");
expect(readLegacyTaskColors()).toEqual({});
});
it("removes the legacy key only when migration asks for removal", () => {
window.localStorage.setItem(TASK_COLORS_STORAGE_KEY, JSON.stringify({ "task-1": "red" }));
clearLegacyTaskColors();
expect(window.localStorage.getItem(TASK_COLORS_STORAGE_KEY)).toBeNull();
});
});
describe("TASK_COLOR_LABEL_KEYS", () => {
afterEach(async () => {
await i18n.changeLanguage("en");
});
it("covers every colour, keyed by the persisted wire value", () => {
expect(Object.keys(TASK_COLOR_LABEL_KEYS).sort()).toEqual([...TASK_COLORS].sort());
// The same wire values index the class table, so they are identity.
expect(Object.keys(TASK_COLOR_BAR_CLASS).sort()).toEqual([...TASK_COLORS].sort());
});
it("holds catalog keys that resolve to English copy", () => {
expect(TASK_COLOR_LABEL_KEYS.red).toBe("task:colorRed");
const labels = TASK_COLORS.map((c) => t(TASK_COLOR_LABEL_KEYS[c]));
expect(labels).toEqual(["Red", "Orange", "Yellow", "Green", "Blue", "Purple", "Pink"]);
});
it("stores keys rather than resolved copy, so a locale switch takes effect", async () => {
await i18n.changeLanguage("pseudo");
for (const color of TASK_COLORS) {
const label = t(TASK_COLOR_LABEL_KEYS[color]);
// A missing key would resolve to the key itself; frozen copy would stay
// English. Both are ruled out by an accented, non-key label.
expect(label).not.toBe(TASK_COLOR_LABEL_KEYS[color]);
expect(label).toMatch(/[^\p{ASCII}]/u);
}
});
});
describe("backend manual task-color map parsing", () => {
it("keeps valid colors and tombstones while dropping malformed entries", () => {
expect(
parseSidebarTaskColors({
"task-red": "red",
"task-cleared": null,
"task-automatic-gray": "gray",
"": "blue",
"task-object": {},
}),
).toEqual({ "task-red": "red", "task-cleared": null });
});
it("caps task IDs by UTF-8 byte length and entry count", () => {
expect(parseSidebarTaskColors({ ["é".repeat(65)]: "red" })).toEqual({});
const input = Object.fromEntries(
Array.from({ length: 10_001 }, (_, index) => [`task-${index}`, "red"]),
);
expect(Object.keys(parseSidebarTaskColors(input))).toHaveLength(10_000);
});
});