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.
This commit is contained in:
1 parent
408a972665
commit
db96ea91cd
4 files changed
+91
-5
No files matched your search
@@ -0,0 +1,55 @@
|
||||
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);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user