Files
diegosouzapw--omniroute/tests/unit/ui/compression-replay-reducer.test.ts
2026-07-13 13:39:12 +08:00

90 lines
3.5 KiB
TypeScript

/**
* tests/unit/ui/compression-replay-reducer.test.ts
*
* F5.1 coverage gap (F3.2): the `useCompressionReplay` state machine was untested
* (only buildReplayFrames was covered). This pins the pure reducer + timing.
*/
import { describe, it } from "node:test";
import assert from "node:assert/strict";
import {
replayReducer,
INITIAL_STATE,
stepMs,
type ReplayState,
} from "../../../src/app/(dashboard)/dashboard/compression/studio/useCompressionReplay.ts";
describe("replayReducer — compression replay state machine (F3.2)", () => {
it("starts idle: frameIndex -1, not playing, speed 1", () => {
assert.deepEqual(INITIAL_STATE, { frameIndex: -1, isPlaying: false, speed: 1 });
});
it("PLAY without frameIndex starts playing and keeps the current frame", () => {
const s = replayReducer({ frameIndex: 2, isPlaying: false, speed: 1 }, { type: "PLAY" });
assert.equal(s.isPlaying, true);
assert.equal(s.frameIndex, 2);
});
it("PLAY with frameIndex restarts from that frame (used for replay-from-start)", () => {
const s = replayReducer(INITIAL_STATE, { type: "PLAY", frameIndex: 0 });
assert.equal(s.isPlaying, true);
assert.equal(s.frameIndex, 0);
});
it("PAUSE stops playing but keeps the frame", () => {
const s = replayReducer({ frameIndex: 1, isPlaying: true, speed: 1 }, { type: "PAUSE" });
assert.equal(s.isPlaying, false);
assert.equal(s.frameIndex, 1);
});
it("TICK advances one frame while below the last", () => {
const start: ReplayState = { frameIndex: -1, isPlaying: true, speed: 1 };
const a = replayReducer(start, { type: "TICK", totalFrames: 3 });
assert.equal(a.frameIndex, 0);
assert.equal(a.isPlaying, true);
const b = replayReducer(a, { type: "TICK", totalFrames: 3 });
assert.equal(b.frameIndex, 1);
assert.equal(b.isPlaying, true);
});
it("TICK stops at the last frame and clears isPlaying (auto-stop at end)", () => {
const atSecondLast: ReplayState = { frameIndex: 1, isPlaying: true, speed: 1 };
const done = replayReducer(atSecondLast, { type: "TICK", totalFrames: 3 });
assert.equal(done.frameIndex, 2, "lands on the last frame index (totalFrames-1)");
assert.equal(done.isPlaying, false, "playback stops once the last frame is reached");
});
it("RESET returns to idle (-1, not playing) but preserves speed", () => {
const s = replayReducer({ frameIndex: 2, isPlaying: true, speed: 3 }, { type: "RESET" });
assert.equal(s.frameIndex, -1);
assert.equal(s.isPlaying, false);
assert.equal(s.speed, 3, "speed must survive a reset");
});
it("SET_SPEED changes speed without touching frame/play state", () => {
const s = replayReducer({ frameIndex: 1, isPlaying: true, speed: 1 }, {
type: "SET_SPEED",
speed: 3,
});
assert.equal(s.speed, 3);
assert.equal(s.frameIndex, 1);
assert.equal(s.isPlaying, true);
});
it("unknown action is a no-op (returns the same state reference)", () => {
const state: ReplayState = { frameIndex: 0, isPlaying: false, speed: 1 };
// @ts-expect-error — exercising the default branch with an invalid action
assert.equal(replayReducer(state, { type: "NOPE" }), state);
});
});
describe("stepMs — frame pacing by speed (F3.2)", () => {
it("maps speed to a shorter interval as speed rises", () => {
assert.equal(stepMs(1), 400);
assert.equal(stepMs(3), 133); // round(400/3)
assert.equal(stepMs(0.3), 1333); // round(400/0.3)
assert.ok(stepMs(3) < stepMs(1) && stepMs(1) < stepMs(0.3), "monotonic: faster = shorter");
});
});