| 1 | // @vitest-environment jsdom |
| 2 | |
| 3 | import { describe, expect, it, vi } from "vite-plus/test"; |
| 4 | |
| 5 | import { forwardWheelToTimeline } from "./timelineMinimapWheel"; |
| 6 | |
| 7 | function setup() { |
| 8 | const timeline = document.createElement("div"); |
| 9 | const minimap = document.createElement("div"); |
| 10 | Object.defineProperty(timeline, "clientHeight", { value: 500 }); |
| 11 | const scrollBy = vi.fn(); |
| 12 | timeline.scrollBy = scrollBy as typeof timeline.scrollBy; |
| 13 | const seen: WheelEvent[] = []; |
| 14 | timeline.addEventListener("wheel", (event) => seen.push(event)); |
| 15 | minimap.addEventListener("wheel", (event) => forwardWheelToTimeline(event, timeline)); |
| 16 | document.body.append(timeline, minimap); |
| 17 | const wheel = (init: WheelEventInit) => { |
| 18 | const event = new WheelEvent("wheel", { bubbles: true, cancelable: true, ...init }); |
| 19 | minimap.dispatchEvent(event); |
| 20 | return event; |
| 21 | }; |
| 22 | return { timeline, scrollBy, seen, wheel }; |
| 23 | } |
| 24 | |
| 25 | describe("forwardWheelToTimeline", () => { |
| 26 | it("replays the wheel on the timeline and scrolls it instead of the minimap", () => { |
| 27 | const { timeline, scrollBy, seen, wheel } = setup(); |
| 28 | const original = wheel({ deltaY: -120, clientX: 20, clientY: 300 }); |
| 29 | |
| 30 | expect(original.defaultPrevented).toBe(true); |
| 31 | expect(seen).toHaveLength(1); |
| 32 | expect(seen[0]?.target).toBe(timeline); |
| 33 | expect(seen[0]?.deltaY).toBe(-120); |
| 34 | expect(seen[0]?.clientY).toBe(300); |
| 35 | expect(scrollBy).toHaveBeenCalledWith({ top: -120 }); |
| 36 | }); |
| 37 | |
| 38 | it.each([ |
| 39 | { deltaMode: WheelEvent.DOM_DELTA_LINE, top: 48 }, |
| 40 | { deltaMode: WheelEvent.DOM_DELTA_PAGE, top: 1500 }, |
| 41 | ])("converts delta mode $deltaMode to pixels", ({ deltaMode, top }) => { |
| 42 | const { scrollBy, wheel } = setup(); |
| 43 | wheel({ deltaY: 3, deltaMode }); |
| 44 | expect(scrollBy).toHaveBeenCalledWith({ top }); |
| 45 | }); |
| 46 | |
| 47 | it("lets a timeline listener cancel the scroll", () => { |
| 48 | const { timeline, scrollBy, wheel } = setup(); |
| 49 | timeline.addEventListener("wheel", (event) => event.preventDefault()); |
| 50 | wheel({ deltaY: 40 }); |
| 51 | expect(scrollBy).not.toHaveBeenCalled(); |
| 52 | }); |
| 53 | |
| 54 | it.each([{ deltaY: 40, ctrlKey: true }, { deltaX: 40 }])( |
| 55 | "leaves pinch zoom and horizontal wheels alone: %j", |
| 56 | (init) => { |
| 57 | const { scrollBy, seen, wheel } = setup(); |
| 58 | expect(wheel(init).defaultPrevented).toBe(false); |
| 59 | expect(seen).toHaveLength(0); |
| 60 | expect(scrollBy).not.toHaveBeenCalled(); |
| 61 | }, |
| 62 | ); |
| 63 | }); |