From 97850310d8055da0f2b5de352df5f932ea15843c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 23 May 2026 11:43:04 -0400 Subject: [PATCH] test(engine): add regression tests for video hold-last-frame fix Add getFrameAtTime regression test covering source shorter than data-duration: verifies last frame is returned (not null) when seeking past source end for non-looping videos. Strengthens the FrameLookupTable test to check at both 1.5s and 4.9s (near timeline end) to guard against future regressions. --- .../engine/src/services/videoFrameExtractor.test.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/packages/engine/src/services/videoFrameExtractor.test.ts b/packages/engine/src/services/videoFrameExtractor.test.ts index 34c1adcafe..c0e6b72c4a 100644 --- a/packages/engine/src/services/videoFrameExtractor.test.ts +++ b/packages/engine/src/services/videoFrameExtractor.test.ts @@ -17,6 +17,7 @@ import { parseImageElements, extractAllVideoFrames, createFrameLookupTable, + getFrameAtTime, resolveProjectRelativeSrc, codecMayHaveAlpha, decoderForCodec, @@ -259,6 +260,17 @@ describe("FrameLookupTable", () => { const pastEnd = table.getActiveFramePayloads(1.5); expect(pastEnd.has("hero")).toBe(true); expect(pastEnd.get("hero")!.frameIndex).toBe(29); + const nearTimelineEnd = table.getActiveFramePayloads(4.9); + expect(nearTimelineEnd.has("hero")).toBe(true); + expect(nearTimelineEnd.get("hero")!.frameIndex).toBe(29); + }); + + it("getFrameAtTime returns last frame for non-looping video past source end", () => { + const extracted = fakeExtracted(90, 30); // 3s @ 30fps + expect(getFrameAtTime(extracted, 1.0, 0, false, 0)).toBe("frame-30.jpg"); + expect(getFrameAtTime(extracted, 5.0, 0, false, 0)).toBe("frame-89.jpg"); + expect(getFrameAtTime(extracted, 10.0, 0, false, 0)).toBe("frame-89.jpg"); + expect(getFrameAtTime(extracted, -1.0, 0, false, 0)).toBeNull(); }); });