a3d75973ee
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>
119 lines
3.5 KiB
TypeScript
119 lines
3.5 KiB
TypeScript
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();
|
|
});
|
|
});
|
|
});
|