feat(speed): switch Playback Speed to 0x–4x forward (drop reverse)

Real-browser eyeball: below-0x reverse jittered because the base loop
clips aren't all-intra (the flagged risk). Narrow the slider to 0x–4x
forward (still continuous, step=any); 0x still freezes; OS reduced-motion
behavior unchanged. Removes the manual rAF reverse loop and the
HEFScrub.reverseStep helper + its unit tests.

Tests: node unit 10/10; a11y e2e 12/12 (Task 5 now 3 slider tests).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-06-30 12:48:14 -07:00
parent df8b245af3
commit e064a2e684
6 changed files with 36 additions and 115 deletions
+7 -22
View File
@@ -52,23 +52,25 @@ test.describe("Task 5 — Playback speed slider (replaces Reduce motion)", () =>
.toBe(false); // experience running → loop plays
}
test("the Reduce motion toggle is gone, replaced by a speed slider", async ({ page }) => {
test("the Reduce motion toggle is gone, replaced by a 04x speed slider", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "no-preference" });
await page.goto("/");
await expect(page.locator("#reduce-motion")).toHaveCount(0);
const slider = page.locator("#play-speed");
await expect(slider).toBeVisible();
await expect(slider).toHaveValue("1"); // default = normal speed
await expect(slider).toHaveValue("1"); // default = normal speed
await expect(slider).toHaveAttribute("min", "0");
await expect(slider).toHaveAttribute("max", "4");
});
test("forward speed sets the loop video's playbackRate", async ({ page }) => {
test("forward speed sets the loop video's playbackRate (up to 4x)", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "no-preference" });
await page.goto("/");
await startExperience(page);
await page.locator("#play-speed").fill("2");
await page.locator("#play-speed").fill("4");
await expect
.poll(() => page.locator("#vid-loop").evaluate((v: HTMLVideoElement) => v.playbackRate))
.toBe(2);
.toBe(4);
});
test("0x freezes the loop video", async ({ page }) => {
@@ -81,23 +83,6 @@ test.describe("Task 5 — Playback speed slider (replaces Reduce motion)", () =>
.toBe(true);
});
test("negative speed plays the loop in reverse (manual frame-stepping)", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "no-preference" });
await page.goto("/");
await startExperience(page);
await page.locator("#play-speed").fill("-1.5");
await page.waitForTimeout(150);
const a = await page.locator("#vid-loop").evaluate((v: HTMLVideoElement) => v.currentTime);
const paused = await page.locator("#vid-loop").evaluate((v: HTMLVideoElement) => v.paused);
await page.waitForTimeout(250);
const b = await page.locator("#vid-loop").evaluate((v: HTMLVideoElement) => v.currentTime);
// The reverse loop pauses the element and drives currentTime by hand — so the
// element reads paused, yet currentTime keeps moving (≠ forward play, ≠ freeze).
// (It may wrap loopStart→tail, so we assert movement, not a strict decrease.)
expect(paused).toBe(true);
expect(b).not.toBe(a);
});
test("OS reduced-motion disables the slider and freezes the loop", async ({ page }) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await page.goto("/");