|
| 1 | +import { describe, expect, it, vi } from "vite-plus/test"; |
| 2 | + |
| 3 | +import { render } from "../../../test/render"; |
| 4 | +import { DiffFileLoadingBoundary } from "./DiffFileLoadingBoundary"; |
| 5 | + |
| 6 | +// Like the real observer, every observe() reports the target's current state; this boundary |
| 7 | +// is always in range. |
| 8 | +class InRangeIntersectionObserver { |
| 9 | + static observeCount = 0; |
| 10 | + constructor(readonly callback: IntersectionObserverCallback) {} |
| 11 | + observe(target: Element) { |
| 12 | + InRangeIntersectionObserver.observeCount += 1; |
| 13 | + this.callback( |
| 14 | + [{ target, isIntersecting: true } as IntersectionObserverEntry], |
| 15 | + this as unknown as IntersectionObserver, |
| 16 | + ); |
| 17 | + } |
| 18 | + unobserve() {} |
| 19 | + disconnect() {} |
| 20 | +} |
| 21 | + |
| 22 | +function stubIntersectionObserver() { |
| 23 | + InRangeIntersectionObserver.observeCount = 0; |
| 24 | + vi.stubGlobal("IntersectionObserver", InRangeIntersectionObserver); |
| 25 | + return { [Symbol.dispose]: () => vi.unstubAllGlobals() }; |
| 26 | +} |
| 27 | + |
| 28 | +describe("DiffFileLoadingBoundary", () => { |
| 29 | + it("observes once and loads once while its parent re-renders with a new load", async () => { |
| 30 | + using _observer = stubIntersectionObserver(); |
| 31 | + const load = vi.fn(); |
| 32 | + // DiffPanel hands over a new loadNextFiles whenever any file patch changes. |
| 33 | + await using screen = await render(<DiffFileLoadingBoundary load={() => load()} count={8} />, { |
| 34 | + router: false, // the boundary does not touch routing |
| 35 | + }); |
| 36 | + await screen.rerender(<DiffFileLoadingBoundary load={() => load()} count={8} />); |
| 37 | + await screen.rerender(<DiffFileLoadingBoundary load={() => load()} count={8} />); |
| 38 | + await screen.rerender(<DiffFileLoadingBoundary load={() => load()} count={8} />); |
| 39 | + |
| 40 | + await expect.element(screen.getByRole("status", { name: "Loading diff…" })).toBeInTheDocument(); |
| 41 | + expect(InRangeIntersectionObserver.observeCount).toBe(1); |
| 42 | + expect(load).toHaveBeenCalledTimes(1); |
| 43 | + |
| 44 | + // A settled batch leaves the boundary in range, so it asks for the next one. |
| 45 | + await screen.rerender(<DiffFileLoadingBoundary load={() => load()} count={4} />); |
| 46 | + expect(InRangeIntersectionObserver.observeCount).toBe(1); |
| 47 | + expect(load).toHaveBeenCalledTimes(2); |
| 48 | + }); |
| 49 | +}); |
0 commit comments