import { easeToward, locateInReel, pickActiveCard, reelStarts, videoProgress, } from "./showcase-math"; const card = (id: string, visible: boolean, distance: number) => ({ id, visible, distance, }); describe("pickActiveCard", () => { it("plays the visible card closest to the middle of the screen", () => { expect( pickActiveCard( [card("a", true, 300), card("b", true, 80), card("c", false, 5)], null, ), ).toBe("b"); }); it("lets the hovered card win over a closer one, but only while it is visible", () => { const cards = [card("a", true, 10), card("b", true, 400)]; expect(pickActiveCard(cards, "b")).toBe("b"); expect( pickActiveCard([card("a", true, 10), card("b", false, 0)], "b"), ).toBe("a"); }); it("keeps the first card on a tie, so the left one of a row wins", () => { expect( pickActiveCard( [card("left", true, 120), card("right", true, 120.3)], null, ), ).toBe("left"); }); it("plays nothing when no card is on screen", () => { expect(pickActiveCard([card("a", false, 0)], null)).toBeNull(); expect(pickActiveCard([], null)).toBeNull(); }); }); describe("locateInReel", () => { const weights = [1, 2, 1]; it("maps overall progress to a project and a position inside it", () => { expect(locateInReel(weights, 0)).toEqual({ index: 0, local: 0 }); expect(locateInReel(weights, 0.125)).toEqual({ index: 0, local: 0.5 }); const middle = locateInReel(weights, 0.5); expect(middle.index).toBe(1); expect(middle.local).toBeCloseTo(0.5); expect(locateInReel(weights, 1)).toEqual({ index: 2, local: 1 }); }); it("clamps progress outside the pinned range", () => { expect(locateInReel(weights, -3)).toEqual({ index: 0, local: 0 }); expect(locateInReel(weights, 7)).toEqual({ index: 2, local: 1 }); }); it("copes with an empty reel", () => { expect(locateInReel([], 0.4)).toEqual({ index: 0, local: 0 }); }); it("gives each project the share of the scroll that its weight asks for", () => { expect(reelStarts(weights)).toEqual([0, 0.25, 0.75]); }); }); describe("videoProgress", () => { it("holds still at the start and at the end of each project's stretch", () => { expect(videoProgress(0)).toBe(0); expect(videoProgress(0.1)).toBe(0); expect(videoProgress(0.5)).toBeCloseTo(0.5); expect(videoProgress(0.9)).toBe(1); expect(videoProgress(1)).toBe(1); }); }); describe("easeToward", () => { it("moves toward the target and never overshoots", () => { const first = easeToward(0, 1, 1 / 60); expect(first).toBeGreaterThan(0); expect(first).toBeLessThan(1); expect(easeToward(0.5, 0.5, 1)).toBe(0.5); expect(easeToward(0, 1, 10)).toBeCloseTo(1, 5); }); it("does not depend on the frame rate", () => { let sixty = 0; for (let i = 0; i < 60; i += 1) sixty = easeToward(sixty, 1, 1 / 60); let thirty = 0; for (let i = 0; i < 30; i += 1) thirty = easeToward(thirty, 1, 1 / 30); expect(sixty).toBeCloseTo(thirty, 6); }); it("stands still when no time has passed", () => { expect(easeToward(0.2, 1, 0)).toBe(0.2); }); });