added testing

This commit is contained in:
2026-05-16 23:48:57 +02:00
parent 23c62fb1e6
commit f1d5c4a4fd
16 changed files with 1014 additions and 119 deletions
+72
View File
@@ -0,0 +1,72 @@
import { describe, expect, it } from 'vitest';
import { buildCybersigil, GLYPHS } from './cybersigil';
/** Deterministic LCG so a given seed reproduces a given sigil. */
function seeded(seed: number): () => number {
let s = seed >>> 0;
return () => {
s = (s * 1664525 + 1013904223) >>> 0;
return s / 4294967296;
};
}
describe('buildCybersigil', () => {
it('is deterministic for a fixed RNG', () => {
const a = buildCybersigil({ rng: seeded(42) });
const b = buildCybersigil({ rng: seeded(42) });
expect(a).toBe(b);
});
it('varies with the seed', () => {
expect(buildCybersigil({ rng: seeded(1) })).not.toBe(buildCybersigil({ rng: seeded(2) }));
});
it('emits a single well-formed root svg', () => {
const svg = buildCybersigil({ rng: seeded(7) });
expect(svg.startsWith('<svg')).toBe(true);
expect(svg.endsWith('</svg>')).toBe(true);
expect(svg.match(/<svg/g)).toHaveLength(1);
// no formatting holes leaked into the path data
expect(svg).not.toMatch(/NaN|undefined|Infinity/);
});
it('is vertically mirrored (two halves, one flipped about x=0)', () => {
const svg = buildCybersigil({ rng: seeded(7) });
expect(svg.match(/class="cs-sig-half"/g)).toHaveLength(2);
expect(svg).toContain('transform="scale(-1 1)"');
});
it('every stroke is pathLength-normalised for the carve animation', () => {
const svg = buildCybersigil({ rng: seeded(99) });
const paths = svg.match(/<path\b[^>]*>/g) ?? [];
expect(paths.length).toBeGreaterThan(0);
for (const p of paths) expect(p).toContain('pathLength="1"');
});
it('exposes a numeric, positive viewBox', () => {
const svg = buildCybersigil({ rng: seeded(7) });
const vb = svg.match(/viewBox="([^"]+)"/)?.[1] ?? '';
const nums = vb.split(/\s+/).map(Number);
expect(nums).toHaveLength(4);
expect(nums.every(Number.isFinite)).toBe(true);
expect(nums[2]).toBeGreaterThan(0); // width
expect(nums[3]).toBeGreaterThan(0); // height
});
it('honours the count option and stays bounded', () => {
const sparse = buildCybersigil({ count: 1, rng: seeded(5) });
const dense = buildCybersigil({ count: 9, rng: seeded(5) });
const n = (s: string) => (s.match(/<path/g) ?? []).length;
expect(n(dense)).toBeGreaterThan(n(sparse));
expect(n(dense)).toBeLessThanOrEqual(260); // MAX_PATHS(110) mirrored + ornaments
});
it('ships a non-empty glyph library with valid path data', () => {
expect(GLYPHS.length).toBeGreaterThan(0);
for (const g of GLYPHS) {
expect(g.w).toBeGreaterThan(0);
expect(g.h).toBeGreaterThan(0);
expect(g.d).toMatch(/^M/);
}
});
});