| 1 | import { afterEach, beforeEach, describe, expect, it, vi } from "vite-plus/test"; |
| 2 | |
| 3 | import { createReviewDiffHighlightScheduler } from "./reviewDiffHighlightScheduler"; |
| 4 | |
| 5 | describe("review diff highlighting while scrolling", () => { |
| 6 | beforeEach(() => vi.useFakeTimers()); |
| 7 | afterEach(() => vi.useRealTimers()); |
| 8 | |
| 9 | it("keeps requesting new rows during gradual scrolling through a large diff", () => { |
| 10 | const request = vi.fn(); |
| 11 | const scheduler = createReviewDiffHighlightScheduler(request); |
| 12 | for (let firstRowIndex = 1; firstRowIndex <= 1_674; firstRowIndex++) { |
| 13 | scheduler.update({ firstRowIndex, lastRowIndex: firstRowIndex + 80 }); |
| 14 | vi.advanceTimersByTime(16); |
| 15 | } |
| 16 | expect(request.mock.calls.length).toBeGreaterThan(100); |
| 17 | vi.advanceTimersByTime(150); |
| 18 | expect(request).toHaveBeenLastCalledWith({ firstRowIndex: 1_674, lastRowIndex: 1_754 }); |
| 19 | }); |
| 20 | |
| 21 | it("highlights the settled viewport even below the movement threshold", () => { |
| 22 | const request = vi.fn(); |
| 23 | const scheduler = createReviewDiffHighlightScheduler(request); |
| 24 | scheduler.update({ firstRowIndex: 2, lastRowIndex: 82 }); |
| 25 | vi.advanceTimersByTime(100); |
| 26 | scheduler.update({ firstRowIndex: 3, lastRowIndex: 83 }); |
| 27 | vi.advanceTimersByTime(100); |
| 28 | expect(request).not.toHaveBeenCalled(); |
| 29 | vi.advanceTimersByTime(50); |
| 30 | expect(request).toHaveBeenCalledExactlyOnceWith({ firstRowIndex: 3, lastRowIndex: 83 }); |
| 31 | }); |
| 32 | |
| 33 | it("does not let repeated draw events starve the settled refresh", () => { |
| 34 | const request = vi.fn(); |
| 35 | const scheduler = createReviewDiffHighlightScheduler(request); |
| 36 | for (let i = 0; i < 10; i++) { |
| 37 | scheduler.update({ firstRowIndex: 1, lastRowIndex: 81 }); |
| 38 | vi.advanceTimersByTime(30); |
| 39 | } |
| 40 | expect(request).toHaveBeenCalledExactlyOnceWith({ firstRowIndex: 1, lastRowIndex: 81 }); |
| 41 | }); |
| 42 | |
| 43 | it("requests large jumps and reverse scrolling immediately without stale timers", () => { |
| 44 | const request = vi.fn(); |
| 45 | const scheduler = createReviewDiffHighlightScheduler(request); |
| 46 | scheduler.update({ firstRowIndex: 1, lastRowIndex: 81 }); |
| 47 | scheduler.update({ firstRowIndex: 1_000, lastRowIndex: 1_080 }); |
| 48 | scheduler.update({ firstRowIndex: 0, lastRowIndex: 80 }); |
| 49 | vi.runAllTimers(); |
| 50 | expect(request.mock.calls).toEqual([ |
| 51 | [{ firstRowIndex: 1_000, lastRowIndex: 1_080 }], |
| 52 | [{ firstRowIndex: 0, lastRowIndex: 80 }], |
| 53 | ]); |
| 54 | }); |
| 55 | |
| 56 | it("cancels pending work on disposal and resets the range for a new diff", () => { |
| 57 | const request = vi.fn(); |
| 58 | const scheduler = createReviewDiffHighlightScheduler(request); |
| 59 | scheduler.update({ firstRowIndex: 1, lastRowIndex: 81 }); |
| 60 | scheduler.cancel(); |
| 61 | vi.runAllTimers(); |
| 62 | expect(request).not.toHaveBeenCalled(); |
| 63 | scheduler.update({ firstRowIndex: 1_000, lastRowIndex: 1_080 }); |
| 64 | request.mockClear(); |
| 65 | scheduler.update({ firstRowIndex: 1_001, lastRowIndex: 1_081 }); |
| 66 | scheduler.reset(); |
| 67 | vi.runAllTimers(); |
| 68 | expect(request).not.toHaveBeenCalled(); |
| 69 | scheduler.update({ firstRowIndex: 1, lastRowIndex: 81 }); |
| 70 | expect(request).not.toHaveBeenCalled(); |
| 71 | vi.advanceTimersByTime(150); |
| 72 | expect(request).toHaveBeenCalledExactlyOnceWith({ firstRowIndex: 1, lastRowIndex: 81 }); |
| 73 | }); |
| 74 | }); |