Skip to content

Commit

Permalink
feat: restore beforeEach in test utils
Browse files Browse the repository at this point in the history
- Add back `beforeEach` to trigger the automatic mocking.
- Keep the `beforeAll` hook
- Add a check to `setupIntersectionMocking` method, so it only runs if mocking hasn't been set up
- Introduce a `destroyIntersectionMocking` method to clear all mocking, and revert to the browser implementation
  • Loading branch information
thebuilder committed Jan 14, 2025
1 parent f3213dd commit 788dd47
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 6 deletions.
12 changes: 12 additions & 0 deletions src/__tests__/hooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { render, screen } from "@testing-library/react";
import React, { useCallback } from "react";
import { type IntersectionOptions, defaultFallbackInView } from "../index";
import {
destroyIntersectionMocking,
intersectionMockInstance,
mockAllIsIntersecting,
mockIsIntersecting,
Expand Down Expand Up @@ -342,6 +343,7 @@ test("should set intersection ratio as the largest threshold smaller than trigge
});

test("should handle fallback if unsupported", () => {
destroyIntersectionMocking();
// @ts-ignore
window.IntersectionObserver = undefined;
const { rerender } = render(
Expand All @@ -363,6 +365,7 @@ test("should handle fallback if unsupported", () => {
});

test("should handle defaultFallbackInView if unsupported", () => {
destroyIntersectionMocking();
// @ts-ignore
window.IntersectionObserver = undefined;
defaultFallbackInView(true);
Expand All @@ -383,3 +386,12 @@ test("should handle defaultFallbackInView if unsupported", () => {
`[TypeError: IntersectionObserver is not a constructor]`,
);
});

test("should restore the browser IntersectingObserver", () => {
expect(vi.isMockFunction(window.IntersectionObserver)).toBe(true);
destroyIntersectionMocking();

// This should restore the original IntersectionObserver
expect(window.IntersectionObserver).toBeDefined();
expect(vi.isMockFunction(window.IntersectionObserver)).toBe(false);
});
30 changes: 24 additions & 6 deletions src/test-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,26 +11,32 @@ let isMocking = false;

const observers = new Map<IntersectionObserver, Item>();

// Store a reference to the original `IntersectionObserver` so we can restore it later.
// This can be relevant if testing in a browser environment, where you actually have a native `IntersectionObserver`.
const originalIntersectionObserver =
typeof window !== "undefined" ? window.IntersectionObserver : undefined;

/*
** If we are running in a valid testing environment, we can automate mocking the IntersectionObserver.
*/
if (
typeof window !== "undefined" &&
typeof beforeAll !== "undefined" &&
typeof beforeEach !== "undefined" &&
typeof afterEach !== "undefined"
) {
beforeAll(() => {
// Use the exposed mock function. Currently, only supports Jest (`jest.fn`) and Vitest with globals (`vi.fn`).
const initMocking = () => {
// Use the exposed mock function. Currently, it supports Jest (`jest.fn`) and Vitest with globals (`vi.fn`).
// @ts-ignore
if (typeof jest !== "undefined") setupIntersectionMocking(jest.fn);
else if (typeof vi !== "undefined") {
setupIntersectionMocking(vi.fn);
}
});
};

afterEach(() => {
resetIntersectionMocking();
});
beforeAll(initMocking);
beforeEach(initMocking);
afterEach(resetIntersectionMocking);
}

function getActFn() {
Expand Down Expand Up @@ -76,6 +82,7 @@ afterEach(() => {
* @param mockFn The mock function to use. Defaults to `vi.fn`.
*/
export function setupIntersectionMocking(mockFn: typeof vi.fn) {
if (isMocking) return;
window.IntersectionObserver = mockFn((cb, options = {}) => {
const item = {
callback: cb,
Expand Down Expand Up @@ -122,6 +129,17 @@ export function resetIntersectionMocking() {
observers.clear();
}

/**
* Destroy the IntersectionObserver mock function, and restore the original browser implementation of `IntersectionObserver`.
* You can use this to opt of mocking in a specific test.
**/
export function destroyIntersectionMocking() {
resetIntersectionMocking();
// @ts-ignore
window.IntersectionObserver = originalIntersectionObserver;
isMocking = false;
}

function triggerIntersection(
elements: Element[],
trigger: boolean | number,
Expand Down

0 comments on commit 788dd47

Please sign in to comment.