Spaces:
Running
Running
File size: 2,024 Bytes
1955a0f | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 | import { describe, it, expect } from "vitest";
import { mulberry32, hashModuleId, buildPieceSpecs, ASSEMBLY_EASE } from "./ModuleLaunchAnimation";
describe("launch animation randomness engine", () => {
it("mulberry32 is deterministic per seed and well-distributed", () => {
const a = mulberry32(12345);
const b = mulberry32(12345);
const seqA = [a(), a(), a(), a()];
const seqB = [b(), b(), b(), b()];
expect(seqA).toEqual(seqB);
seqA.forEach((v) => {
expect(v).toBeGreaterThanOrEqual(0);
expect(v).toBeLessThan(1);
});
});
it("different seeds produce different assemblies (re-seed per launch)", () => {
const specs1 = buildPieceSpecs(mulberry32(hashModuleId("e") ^ 1), 11);
const specs2 = buildPieceSpecs(mulberry32(hashModuleId("e") ^ 2), 11);
const sig1 = JSON.stringify(specs1.map((s) => [s.from.x, s.from.y, s.from.rotate, s.from.delay]));
const sig2 = JSON.stringify(specs2.map((s) => [s.from.x, s.from.y, s.from.rotate, s.from.delay]));
expect(sig1).not.toEqual(sig2);
});
it("piece transforms stay inside the mandated motion budget", () => {
const specs = buildPieceSpecs(mulberry32(42), 11);
expect(specs).toHaveLength(11);
specs.forEach((s) => {
const distance = Math.hypot(s.from.x, s.from.y);
expect(distance).toBeGreaterThanOrEqual(240);
expect(distance).toBeLessThanOrEqual(720);
expect(Math.abs(s.from.rotate)).toBeGreaterThanOrEqual(8);
expect(Math.abs(s.from.rotate)).toBeLessThanOrEqual(45);
expect(s.from.scale).toBeGreaterThanOrEqual(0.55);
expect(s.from.scale).toBeLessThanOrEqual(1.35);
// stagger: i*55 + 40..90ms
expect(s.from.delay).toBeGreaterThan(0);
// slow-motion duration 2.4–3.2s
expect(s.from.duration).toBeGreaterThanOrEqual(2.4);
expect(s.from.duration).toBeLessThanOrEqual(3.2);
});
});
it("uses the mandated slow-motion easing curve", () => {
expect(Array.from(ASSEMBLY_EASE)).toEqual([0.22, 1, 0.36, 1]);
});
});
|