reactjsunit-testingjestjsreact-hooksreact-resize-observer

ResizeObserver API testing jest


I tried to test the hook, that used the ResizeObserver. Also, I need to check if the element was overflow or wasn't after resizing. So I wrote such a decision:

import { useLayoutEffect, useState } from 'react';
import ResizeObserver from 'resize-observer-polyfill';

export default function useOverflowCheck(ref) {
  const [isOverflowing, setIsOverflowing] = useState(false);

  const isOverflow = current => {
    if (current) {
      const hasOverflowX = current.offsetWidth < current.scrollWidth;
      const hasOverflowY = current.offsetHeight < current.scrollHeight;

      setIsOverflowing(hasOverflowX || hasOverflowY);
    }
  };

  // eslint-disable-next-line react-hooks/exhaustive-deps
  useLayoutEffect(() => {
    const element = ref.current;

    const resizeObserver = new ResizeObserver(entries => {
      entries && entries.length && isOverflow(entries[0].target);
    });

    if (element) {
      resizeObserver.observe(element);
      return () => {
        resizeObserver.disconnect();
      };
    }
  });

  return isOverflowing;
}

And I tried to unit test this code, but my tests didn't cover the resizeObserver callback. Test:

import { renderHook } from '@testing-library/react-hooks';
import useOverflowCheck from './index';

describe('useOverflowCheck', () => {
  it('should return true for an overflowing component', () => {
    const el: HTMLDivElement = document.createElement('div');
    Object.defineProperties(el, {
      offsetWidth: { value: 30, configurable: true },
      offsetHeight: { value: 30, configurable: true },
    });

    const ref = {
      current: el,
    };

    expect(renderHook(() => useOverflowCheck(ref)).result.current).toBeTruthy();
  });
});

Solution

  • Should define Resize Observer constructor and add a listener to test Resize Observer callback.

    let listener: ((rect: any) => void) | undefined = undefined;
        (global as any).ResizeObserver = class ResizeObserver {
          constructor(ls) {
            listener = ls;
          }
          observe() {}
          unobserve() {}
          disconnect() {}
        };
    

    Then should specify needed properties:

    act(() => {
          listener!([
            {
              target: {
                clientWidth: 100,
                scrollWidth: 200,
                clientHeight: 100,
                scrollHeight: 200,
              },
            },
          ]);
        });
    

    Inspired by https://github.com/streamich/react-use/blob/master/tests/useMeasure.test.ts