test(web): add 291 tests for parsers, hooks, and UI components

Lib utilities: scopeImportParser (31), status-styles (58),
planningEntryIds (10), uuid (11).

Hooks: useFilters (28), useRowOrder (18), usePermissions (30),
useViewPrefs (24).

Components: AnimatedModal (14), DateInput (22), InfoTooltip (13),
ProgressRing (19).

Web test suite: 75 → 87 files, 553 → 844 tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-10 17:14:11 +02:00
parent 98dca6126f
commit a3d75973ee
12 changed files with 2626 additions and 0 deletions
+118
View File
@@ -0,0 +1,118 @@
import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import { uuid } from "./uuid.js";
// RFC 4122 v4 UUID pattern
const UUID_V4_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
describe("uuid", () => {
// ---------------------------------------------------------------------------
// Shape and format
// ---------------------------------------------------------------------------
it("returns a string", () => {
expect(typeof uuid()).toBe("string");
});
it("matches the RFC 4122 v4 UUID format", () => {
expect(uuid()).toMatch(UUID_V4_REGEX);
});
it("has the correct length (36 characters)", () => {
expect(uuid()).toHaveLength(36);
});
it("contains hyphens at positions 8, 13, 18, and 23", () => {
const id = uuid();
expect(id[8]).toBe("-");
expect(id[13]).toBe("-");
expect(id[18]).toBe("-");
expect(id[23]).toBe("-");
});
it("has '4' as the version digit at position 14", () => {
expect(uuid()[14]).toBe("4");
});
it("has a valid variant digit at position 19 (8, 9, a, or b)", () => {
const variantChar = uuid()[19]!;
expect(["8", "9", "a", "b"]).toContain(variantChar.toLowerCase());
});
// ---------------------------------------------------------------------------
// Uniqueness
// ---------------------------------------------------------------------------
it("generates unique values on successive calls", () => {
const ids = new Set(Array.from({ length: 1000 }, () => uuid()));
expect(ids.size).toBe(1000);
});
// ---------------------------------------------------------------------------
// Fallback path (crypto.randomUUID not available)
// ---------------------------------------------------------------------------
describe("fallback when crypto.randomUUID is unavailable", () => {
let originalCrypto: Crypto;
beforeEach(() => {
originalCrypto = globalThis.crypto;
});
afterEach(() => {
Object.defineProperty(globalThis, "crypto", {
value: originalCrypto,
configurable: true,
writable: true,
});
});
it("falls back to Math.random-based generation when randomUUID is missing", () => {
// Stub crypto so that randomUUID is absent
Object.defineProperty(globalThis, "crypto", {
value: {},
configurable: true,
writable: true,
});
const id = uuid();
expect(id).toMatch(UUID_V4_REGEX);
});
it("fallback still produces unique values", () => {
Object.defineProperty(globalThis, "crypto", {
value: {},
configurable: true,
writable: true,
});
const ids = new Set(Array.from({ length: 500 }, () => uuid()));
expect(ids.size).toBe(500);
});
it("falls back when crypto is undefined", () => {
Object.defineProperty(globalThis, "crypto", {
value: undefined,
configurable: true,
writable: true,
});
const id = uuid();
expect(id).toMatch(UUID_V4_REGEX);
});
});
// ---------------------------------------------------------------------------
// Native path (crypto.randomUUID explicitly available)
// ---------------------------------------------------------------------------
describe("native crypto.randomUUID path", () => {
it("delegates to crypto.randomUUID when it is available", () => {
const spy = vi.spyOn(globalThis.crypto, "randomUUID");
uuid();
expect(spy).toHaveBeenCalledOnce();
spy.mockRestore();
});
});
});