7f0d4bdbb7
Co-authored-by: Kandev Agent <agent@kandev.dev>
104 lines
3.7 KiB
TypeScript
104 lines
3.7 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
import {
|
|
getTaskColor,
|
|
setTaskColor,
|
|
TASK_COLORS,
|
|
TASK_COLOR_BAR_CLASS,
|
|
TASK_COLOR_LABEL_KEYS,
|
|
TASK_COLORS_CHANGED_EVENT,
|
|
TASK_COLORS_STORAGE_KEY,
|
|
} from "./task-colors";
|
|
import { i18n, t } from "@/lib/i18n";
|
|
|
|
describe("task colors storage", () => {
|
|
beforeEach(() => {
|
|
window.localStorage.clear();
|
|
// Synthesize a cross-tab storage event so the module-level cache invalidates,
|
|
// matching how a real browser would notify other tabs after localStorage.clear().
|
|
window.dispatchEvent(new StorageEvent("storage", { key: null }));
|
|
});
|
|
|
|
it("returns null when no color is stored", () => {
|
|
expect(getTaskColor("task-1")).toBeNull();
|
|
});
|
|
|
|
it("stores and reads a color", () => {
|
|
setTaskColor("task-1", "blue");
|
|
expect(getTaskColor("task-1")).toBe("blue");
|
|
});
|
|
|
|
it("removes a color when set to null", () => {
|
|
setTaskColor("task-1", "blue");
|
|
setTaskColor("task-1", null);
|
|
expect(getTaskColor("task-1")).toBeNull();
|
|
});
|
|
|
|
it("ignores invalid colors loaded from storage", () => {
|
|
window.localStorage.setItem(
|
|
TASK_COLORS_STORAGE_KEY,
|
|
JSON.stringify({ "task-1": "fuchsia", "task-2": "red" }),
|
|
);
|
|
expect(getTaskColor("task-1")).toBeNull();
|
|
expect(getTaskColor("task-2")).toBe("red");
|
|
});
|
|
|
|
it("returns null on malformed storage", () => {
|
|
window.localStorage.setItem(TASK_COLORS_STORAGE_KEY, "{not json");
|
|
expect(getTaskColor("task-1")).toBeNull();
|
|
});
|
|
|
|
it("dispatches a change event when a color is set", () => {
|
|
const listener = vi.fn();
|
|
window.addEventListener(TASK_COLORS_CHANGED_EVENT, listener);
|
|
setTaskColor("task-1", "green");
|
|
expect(listener).toHaveBeenCalledTimes(1);
|
|
window.removeEventListener(TASK_COLORS_CHANGED_EVENT, listener);
|
|
});
|
|
|
|
it("does not dispatch when setting the same color twice", () => {
|
|
setTaskColor("task-1", "green");
|
|
const listener = vi.fn();
|
|
window.addEventListener(TASK_COLORS_CHANGED_EVENT, listener);
|
|
setTaskColor("task-1", "green");
|
|
expect(listener).not.toHaveBeenCalled();
|
|
window.removeEventListener(TASK_COLORS_CHANGED_EVENT, listener);
|
|
});
|
|
|
|
it("does not dispatch when clearing a non-existent color", () => {
|
|
const listener = vi.fn();
|
|
window.addEventListener(TASK_COLORS_CHANGED_EVENT, listener);
|
|
setTaskColor("task-1", null);
|
|
expect(listener).not.toHaveBeenCalled();
|
|
window.removeEventListener(TASK_COLORS_CHANGED_EVENT, listener);
|
|
});
|
|
});
|
|
|
|
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);
|
|
}
|
|
});
|
|
});
|