diff options
| author | cashmere <cashmere@autistici.org> | 2026-07-27 01:21:02 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-07-27 01:21:02 +0200 |
| commit | 2bbada7d2c5c00c5f844b6916c08bc4a9ea8cb25 (patch) | |
| tree | 2fdd617e9d51f7293e56b0e9b947f00643aeb8cd /apps/web/src/actions/__tests__ | |
| parent | f8fdff7f0c1b8efdef9093c932e28e427799d6ef (diff) | |
| download | opencut-2bbada7d2c5c00c5f844b6916c08bc4a9ea8cb25.tar.gz | |
feat: freeze-duration prompt
Freeze frame now asks for the duration (decimal seconds, 0 < d <= 600)
before capture, via a promise-based zustand store mounted as an
editor-global dialog. Last-used duration persists and prefills. Cancel
aborts before capture with no commands.
Diffstat (limited to 'apps/web/src/actions/__tests__')
| -rw-r--r-- | apps/web/src/actions/__tests__/freeze-frame-store.test.ts | 98 |
1 files changed, 98 insertions, 0 deletions
diff --git a/apps/web/src/actions/__tests__/freeze-frame-store.test.ts b/apps/web/src/actions/__tests__/freeze-frame-store.test.ts new file mode 100644 index 00000000..94e1566e --- /dev/null +++ b/apps/web/src/actions/__tests__/freeze-frame-store.test.ts @@ -0,0 +1,98 @@ +import { beforeEach, describe, expect, test } from "bun:test"; +import { + DEFAULT_FREEZE_DURATION_SECONDS, + isValidFreezeDuration, + MAX_FREEZE_DURATION_SECONDS, + useFreezeFrameStore, +} from "../freeze-frame-store"; + +function resetStore() { + useFreezeFrameStore.setState({ + isOpen: false, + lastDurationSeconds: DEFAULT_FREEZE_DURATION_SECONDS, + resolver: null, + }); +} + +describe("freeze-frame store", () => { + beforeEach(() => { + resetStore(); + }); + + describe("isValidFreezeDuration", () => { + test("accepts positive durations up to the max, including decimals", () => { + expect(isValidFreezeDuration({ seconds: 3 })).toBe(true); + expect(isValidFreezeDuration({ seconds: 2.5 })).toBe(true); + expect(isValidFreezeDuration({ seconds: 0.1 })).toBe(true); + expect( + isValidFreezeDuration({ seconds: MAX_FREEZE_DURATION_SECONDS }), + ).toBe(true); + }); + + test("rejects non-positive, over-max, and non-finite durations", () => { + expect(isValidFreezeDuration({ seconds: 0 })).toBe(false); + expect(isValidFreezeDuration({ seconds: -1 })).toBe(false); + expect(isValidFreezeDuration({ seconds: 600.1 })).toBe(false); + expect(isValidFreezeDuration({ seconds: Number.NaN })).toBe(false); + expect(isValidFreezeDuration({ seconds: Number.POSITIVE_INFINITY })).toBe( + false, + ); + }); + }); + + describe("requestDuration", () => { + test("opens the dialog and resolves with the confirmed seconds", async () => { + const promise = useFreezeFrameStore.getState().requestDuration(); + expect(useFreezeFrameStore.getState().isOpen).toBe(true); + + useFreezeFrameStore.getState().confirmDuration({ seconds: 5 }); + + await expect(promise).resolves.toBe(5); + expect(useFreezeFrameStore.getState().isOpen).toBe(false); + }); + + test("remembers the confirmed duration as the last-used value", async () => { + const promise = useFreezeFrameStore.getState().requestDuration(); + useFreezeFrameStore.getState().confirmDuration({ seconds: 5 }); + await promise; + + expect(useFreezeFrameStore.getState().lastDurationSeconds).toBe(5); + }); + + test("resolves null on cancel and keeps the last-used duration", async () => { + useFreezeFrameStore.setState({ lastDurationSeconds: 7 }); + + const promise = useFreezeFrameStore.getState().requestDuration(); + expect(useFreezeFrameStore.getState().isOpen).toBe(true); + + useFreezeFrameStore.getState().cancelDuration(); + + await expect(promise).resolves.toBeNull(); + expect(useFreezeFrameStore.getState().isOpen).toBe(false); + expect(useFreezeFrameStore.getState().lastDurationSeconds).toBe(7); + }); + + test("ignores an invalid confirm and leaves the dialog open", () => { + useFreezeFrameStore.getState().requestDuration(); + + useFreezeFrameStore.getState().confirmDuration({ seconds: 0 }); + useFreezeFrameStore.getState().confirmDuration({ seconds: 601 }); + + expect(useFreezeFrameStore.getState().isOpen).toBe(true); + expect(useFreezeFrameStore.getState().lastDurationSeconds).toBe( + DEFAULT_FREEZE_DURATION_SECONDS, + ); + }); + + test("a new request supersedes a pending one by cancelling it", async () => { + const first = useFreezeFrameStore.getState().requestDuration(); + const second = useFreezeFrameStore.getState().requestDuration(); + + await expect(first).resolves.toBeNull(); + expect(useFreezeFrameStore.getState().isOpen).toBe(true); + + useFreezeFrameStore.getState().confirmDuration({ seconds: 2 }); + await expect(second).resolves.toBe(2); + }); + }); +}); |
