import { describe, expect, it } from "vitest"; import { SPEEDS, normalizeSpeed, stepSpeed } from "./audio"; describe("SPEEDS", () => { it("is the agreed list from the player plan (0.5 through 3)", () => { expect(SPEEDS).toEqual([0.5, 0.75, 1, 1.25, 1.5, 1.75, 2, 2.5, 3]); }); it("contains 1× exactly once and stays ascending", () => { expect(SPEEDS.filter((r) => r === 1)).toHaveLength(1); expect([...SPEEDS].sort((a, b) => a - b)).toEqual([...SPEEDS]); }); }); describe("normalizeSpeed", () => { it("passes through every listed rate", () => { for (const rate of SPEEDS) { expect(normalizeSpeed(String(rate))).toBe(rate); } }); it("falls back to 1× for null, garbage and out-of-list values", () => { expect(normalizeSpeed(null)).toBe(1); expect(normalizeSpeed("")).toBe(1); expect(normalizeSpeed("not-a-number")).toBe(1); expect(normalizeSpeed("0")).toBe(1); expect(normalizeSpeed("2.2")).toBe(1); expect(normalizeSpeed("10")).toBe(1); }); it("accepts a stale pre-plan entry of 0.75 or 2", () => { expect(normalizeSpeed("0.75")).toBe(0.75); expect(normalizeSpeed("2")).toBe(2); }); }); describe("stepSpeed", () => { it("steps forward and backward through the list", () => { expect(stepSpeed(1, 1)).toBe(1.25); expect(stepSpeed(1, -1)).toBe(0.75); expect(stepSpeed(0.5, 1)).toBe(0.75); expect(stepSpeed(2, 1)).toBe(2.5); }); it("clamps at both ends instead of wrapping", () => { expect(stepSpeed(0.5, -1)).toBe(0.5); expect(stepSpeed(3, 1)).toBe(3); }); it("anchors unknown currents at 1× before stepping", () => { expect(stepSpeed(2.2, 1)).toBe(1.25); expect(stepSpeed(2.2, -1)).toBe(0.75); }); });