I'm trying to test this custom hook but I don't know how to send the useRef as a parameter ElementRef is using useRef
import { MutableRefObject, useEffect, useState } from "react";
export default function useNearScreen(elementRef: MutableRefObject<HTMLDivElement>, margin = 80) {
const [show, setShow] = useState(false);
useEffect(() => {
const onChange = (entries: IntersectionObserverEntry[]) => {
const el: IntersectionObserverEntry = entries[0];
if (el.isIntersecting) {
setShow(true)
observer.disconnect();
}
}
const observer = new IntersectionObserver(onChange, {
rootMargin: `${margin}px`
})
observer.observe(elementRef.current as Element);
return () => observer.disconnect();
})
return show;
}
CodePudding user response:
First of all, jest use jsdom as its test environment by default. jsdom doesn't support IntersectionObserver
, see issue#2032. So we need to mock it and trigger the callback manually.
I will use @testing-library/react-hooks
package to test react custom hook.
E.g.
useNearScreen.ts
:
import { MutableRefObject, useEffect, useState } from 'react';
export default function useNearScreen(elementRef: MutableRefObject<HTMLDivElement>, margin = 80) {
const [show, setShow] = useState(false);
useEffect(() => {
const onChange = (entries: IntersectionObserverEntry[], observer: IntersectionObserver) => {
const el: IntersectionObserverEntry = entries[0];
if (el.isIntersecting) {
setShow(true);
observer.disconnect();
}
};
const observer = new IntersectionObserver(onChange, {
rootMargin: `${margin}px`,
});
observer.observe(elementRef.current as Element);
return () => observer.disconnect();
});
return show;
}
useNearScreen.test.ts
:
import { renderHook } from '@testing-library/react-hooks';
import { MutableRefObject } from 'react';
import { useRef } from 'react';
import useNearScreen from './useNearScreen';
describe('useNearScreen', () => {
test('should pass', () => {
const mObserver = {
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
};
const mIntersectionObserver = jest.fn();
mIntersectionObserver.mockImplementation((callback, options) => {
callback([{ isIntersecting: true }], mObserver);
return mObserver;
});
window.IntersectionObserver = mIntersectionObserver;
const mHTMLDivElement = document.createElement('div');
const { result } = renderHook(() => {
const elementRef = useRef<HTMLDivElement>(mHTMLDivElement);
return useNearScreen(elementRef as MutableRefObject<HTMLDivElement>);
});
expect(result.current).toBe(true);
expect(mIntersectionObserver).toBeCalledWith(expect.any(Function), { rootMargin: '80px' });
expect(mObserver.observe).toBeCalledWith(mHTMLDivElement);
expect(mObserver.disconnect).toBeCalled();
});
});
Test result:
PASS stackoverflow/71118856/useNearScreen.test.ts (9.656 s)
useNearScreen
✓ should pass (16 ms)
------------------|---------|----------|---------|---------|-------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------|---------|----------|---------|---------|-------------------
All files | 100 | 66.67 | 100 | 100 |
useNearScreen.ts | 100 | 66.67 | 100 | 100 | 9
------------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 10.581 s