import { AnimationManager, type HoverValues } from "../src/internal/data-grid/animation-manager.js"; import { vi, expect, describe, it, beforeEach, afterEach } from "vitest"; // const OG_RAF = window.requestAnimationFrame; // const OG_CAF = window.cancelAnimationFrame; describe("Animation manager", () => { beforeEach(() => { // Mock rAF with setTimeout. Not perfect by any means, but at least it mocks the asynchronicity. vi.useFakeTimers(); // window.requestAnimationFrame = f => { // const timeoutID = setTimeout(() => { // const timestamp = performance.now(); // f(timestamp); // }, 0); // return timeoutID as unknown as number; // }; // window.cancelAnimationFrame = (rafID: number) => { // clearTimeout(rafID); // }; }); afterEach(() => { vi.useRealTimers(); // window.requestAnimationFrame = OG_RAF; // window.cancelAnimationFrame = OG_CAF; }); it("hovers an element from 0 to 1", async () => { // As we call the handler, keep track of all the hover amounts we were called with. // We can't use spy.mock.calls cause we're passing the same reference with different values // so all the calls see the last state of the hover values. const hoverAmountsInCallingOrder: number[] = []; const spy = vi.fn((hoverValues: HoverValues) => { hoverAmountsInCallingOrder.push(hoverValues[0].hoverAmount); }); const manager = new AnimationManager(spy); manager.setHovered([1, 2]); expect(spy).not.toHaveBeenCalled(); vi.runAllTimers(); // Check that we called the handler with the right cell expect(spy).toHaveBeenCalledWith([{ item: [1, 2], hoverAmount: 1 }]); // Check that we called the handler many times. More than 3, idk. expect(hoverAmountsInCallingOrder.length).toBeGreaterThan(3); // Now check that each successive call yielded a greater hover amount. let previousHoverAmount = 0; for (const hoverAmount of hoverAmountsInCallingOrder) { expect(hoverAmount).toBeGreaterThanOrEqual(previousHoverAmount); previousHoverAmount = hoverAmount; } }); it("unhovers an element back to 0", async () => { // Don't track calls while setting up everything let trackCalls = false; const hoverAmountsInCallingOrder: number[] = []; const spy = vi.fn((hoverValues: HoverValues) => { if (trackCalls) { hoverAmountsInCallingOrder.push(hoverValues[0].hoverAmount); } }); const manager = new AnimationManager(spy); manager.setHovered([1, 2]); vi.runAllTimers(); // Make sure that the item is right, and that it hovered up to 1. expect(spy).toHaveBeenCalledWith([{ item: [1, 2], hoverAmount: 1 }]); trackCalls = true; manager.setHovered(undefined); vi.runAllTimers(); expect(hoverAmountsInCallingOrder.length).toBeGreaterThan(3); let previousHoverAmount = 1; for (const hoverAmount of hoverAmountsInCallingOrder) { // due to easing you might get thez expect(hoverAmount).toBeLessThanOrEqual(previousHoverAmount); previousHoverAmount = hoverAmount; } // Make sure the last call was done with 0 hover amount. We don't want to end up in a partially hovered state. const lastCallHoverAmount = hoverAmountsInCallingOrder[hoverAmountsInCallingOrder.length - 1]; expect(lastCallHoverAmount).toBe(0); }); it("Crossfades two items", async () => { type ItemsHoverAmount = { one: number; two: number }; let trackCalls = false; const hoverAmountsInCallingOrder: ItemsHoverAmount[] = []; const spy = vi.fn((hoverValues: HoverValues) => { if (trackCalls) { const hoverForOne = hoverValues.find(item => item.item[0] === 1)?.hoverAmount ?? 0; const hoverForTwo = hoverValues.find(item => item.item[0] === 2)?.hoverAmount ?? 0; hoverAmountsInCallingOrder.push({ one: hoverForOne, two: hoverForTwo, }); } }); const manager = new AnimationManager(spy); manager.setHovered([1, 1]); vi.runAllTimers(); // Make sure that the first item is fully hovered, to start with expect(spy).toHaveBeenCalledWith([{ item: [1, 1], hoverAmount: 1 }]); trackCalls = true; manager.setHovered([2, 2]); vi.runAllTimers(); // Check that we started with one > two const { one: startingOne, two: startingTwo } = hoverAmountsInCallingOrder[0]; expect(startingOne).toBeGreaterThan(startingTwo); // Check that we ended with one < one const { one: lastOne, two: lastTwo } = hoverAmountsInCallingOrder[hoverAmountsInCallingOrder.length - 1]; expect(lastOne).toBeLessThan(lastTwo); // Now check that both moved monotonically let previousOneHoverAmount = 1; let previousTwoHoverAmount = 0; for (const hoverAmount of hoverAmountsInCallingOrder) { const { one, two } = hoverAmount; expect(one).toBeLessThanOrEqual(previousOneHoverAmount); previousOneHoverAmount = one; expect(two).toBeGreaterThanOrEqual(previousTwoHoverAmount); previousTwoHoverAmount = two; } }); });