Files
bookhoard/web/src/audio.test.ts
T
John O'Keefe db96ea91cd refactor(audio-player): one source for the speed list (plan §5)
The rate list was duplicated — hardcoded in the templ x-for AND
implicitly in audio.ts — and phase 2's keyboard stepping would have
needed a third copy. SPEEDS is now the single exported const (the
agreed nine values, 0.5 through 3); the component exposes it as
'speeds' for the dropdown's x-for (templ cannot import TS), and
stepSpeed() walks the same array for the future audio-keys consumer.

normalizeSpeed() maps persisted values onto the list: garbage or
out-of-list localStorage entries fall back to 1x, and init() writes
the normalized value back so storage heals instead of staying
poisoned.

Pure logic unit-tested in audio.test.ts; live-verified with Playwright
on the KJV folder book: nine options render, pick/reload/persist and
the stale-value fallback all behave, button label and
audio.playbackRate agree in every case.
2026-10-21 21:08:27 -04:00

56 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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);
});
});