IntersectionObserverを使用したコードをvitestでテストする方法を検討しました。
考え方としては、スクロールをシミュレーションするのではなく、new IntersectionObserver で指定したコールバック関数を発火させることでテストします。
import { Mock } from "vitest";
import { act, render, screen } from "@testing-library/react";
describe("ImageList", () => {
let mocObserve: Mock;
let mocDisconnect: Mock;
let IntersectionObserverMock: Mock<
[cb: IntersectionObserverCallback],
{
disconnect: Mock;
observe: Mock;
trigger: (opt: { isIntersecting: boolean }[]) => void;
}
>;
beforeAll(() => {
IntersectionObserverMock = vi.fn((cb) => ({
disconnect: (mocDisconnect = vi.fn()),
observe: (mocObserve = vi.fn()),
trigger: (opt: { isIntersecting: boolean }[]) => {
cb(
opt as IntersectionObserverEntry[],
{
disconnect: (mocDisconnect = vi.fn()),
} as unknown as IntersectionObserver
);
},
}));
vi.stubGlobal("IntersectionObserver", IntersectionObserverMock);
});
afterEach(() => {
vi.clearAllMocks();
});
test("should get more images when scrolling to the bottom", async () => {
render(<SampleComponent />);
expect(mocObserve).toHaveBeenCalledOnce();
const obs = IntersectionObserverMock.mock.results[0];
act(() => {
obs.value.trigger([{ isIntersecting: true }]);
});
// some test
expect(mocDisconnect).toHaveBeenCalledOnce();
});
});