import { describe, expect, it } from "vitest"; import { TOOLCRAFT_VIDEO_EXPORT_FRAMES_PER_SECOND, createToolcraftVideoFrameSchedule, } from "./video-frame-schedule"; describe("createToolcraftVideoFrameSchedule", () => { it("creates exact timeline timestamps without duplicating the endpoint", () => { const schedule = createToolcraftVideoFrameSchedule(4.8); expect(TOOLCRAFT_VIDEO_EXPORT_FRAMES_PER_SECOND).toBe(30); expect(schedule).toHaveLength(144); expect(schedule[0]).toEqual({ durationSeconds: 1 / 30, index: 0, timeSeconds: 0, }); expect(schedule.at(-1)?.timeSeconds).toBe(143 / 30); expect( schedule.reduce( (end, frame) => Math.max(end, frame.timeSeconds + frame.durationSeconds), 0, ), ).toBeCloseTo(4.8, 10); }); it("shortens only the final frame for a fractional duration", () => { const schedule = createToolcraftVideoFrameSchedule(1.01); expect(schedule).toHaveLength(31); expect(schedule.at(-1)).toEqual({ durationSeconds: 1.01 - 1, index: 30, timeSeconds: 1, }); }); it.each([0.1, 0.3, 4.8, 4.9, 60])( "avoids a floating-point phantom frame for %s seconds", (durationSeconds) => { const schedule = createToolcraftVideoFrameSchedule(durationSeconds); expect(schedule).toHaveLength(Math.round(durationSeconds * 30)); expect(schedule.at(-1)?.timeSeconds).toBeLessThan(durationSeconds); }, ); it.each([0, -1, Number.NaN, Number.POSITIVE_INFINITY])( "rejects invalid duration %s", (durationSeconds) => { expect(() => createToolcraftVideoFrameSchedule(durationSeconds)).toThrow( "positive finite duration", ); }, ); });