jsdom-testing-mocks

A set of tools for emulating browser behavior in jsdom environment

Usage no npm install needed!

<script type="module">
  import jsdomTestingMocks from 'https://cdn.skypack.dev/jsdom-testing-mocks';
</script>

README

jsdom-testing-mocks

A set of tools for emulating browser behavior in jsdom environment

Build status version PRs Welcome downloads MIT License Code of Conduct

GitHub Repo stars Twitter URL

Installation

npm install --save-dev jsdom-testing-mocks

or

yarn add -D jsdom-testing-mocks

Mock viewport

Mocks matchMedia, allows testing of component's behavior depending on the viewport description (supports all of the Media Features). mockViewport must be called before rendering the component

Example, using React Testing Library:

import { mockViewport } from 'jsdom-testing-mocks';

it('shows the right lines on desktop and mobile', () => {
  const viewport = mockViewport({ width: '320px', height: '568px' });

  render(<TestComponent />);

  expect(
    screen.getByText('Content visible only on small screens')
  ).toBeInTheDocument();

  expect(
    screen.queryByText('Content visible only on large screens')
  ).not.toBeInTheDocument();

  act(() => {
    viewport.set({ width: '1440px', height: '900px' });
  });

  expect(
    screen.queryByText('Content visible only on small screens')
  ).not.toBeInTheDocument();

  expect(
    screen.getByText('Content visible only on large screens')
  ).toBeInTheDocument();

  viewport.cleanup();
});

Also, you can mock the viewport for a group of tests, using mockViewportForTestGroup:

import { mockViewportForTestGroup } from 'jsdom-testing-mocks'

describe('Desktop specific tests', () => {
  mockViewportForTestGroup({ width: '1440px', height: '900px' })

  test('this', () = {
    // ...
  })

  test('that', () = {
    // ...
  })
})

Mock IntersectionObserver

Provides a way of triggering intersection observer events

Example, using React Testing Library:

import { mockIntersectionObserver } from 'jsdom-testing-mocks';

const intersectionObserver = mockIntersectionObserver();

it('loads the image when the component is in the viewport', () => {
  const { container } = render(<TestComponent />);

  expect(screen.queryByAltText('alt text')).not.toBeInTheDocument();

  // when the component's observed node is in the viewport - show the image
  act(() => {
    intersectionObserver.enterNode(container.firstChild);
  });

  expect(screen.getByAltText('alt text')).toBeInTheDocument();
});

API

mockIntersectionObserver returns an object, that has several useful methods:

.enterNode(node, desc) and .leaveNode(node, desc)

Triggers the intersection observer callback with only one node and isIntersected set to true (for enterNode) or false (for leaveNode). Other IntersectionObserverEntry params can be passed as desc argument

.enterAll(desc) and .leaveAll(desc)

Triggers the intersection observer callback for all of the observed nodes and isIntersected set to true (for enterAll) or false (for leaveAll). Other IntersectionObserverEntry params can be passed as desc argument

Mock ResizeObserver

Provides a way of triggering resize observer events. It's up to you to mock elements' sizes. If your component uses contentRect provided by the callback, you must mock element's getBoundingClientRect (for exemple using a helper function mockElementBoundingClientRect provided by the lib)

Currently the mock doesn't take into account multi-column layouts, so borderBoxSize and contentBoxSize will contain only one full-sized item

Example, using React Testing Library:

import {
  mockResizeObserver,
  mockElementBoundingClientRect,
} from 'jsdom-testing-mocks';

const DivWithSize = () => {
  const [size, setSize] = useState({ width: 0, height: 0 });
  const ref = useRef(null);

  useEffect(() => {
    const observer = new ResizeObserver(entries => {
      setSize({
        width: entries[0].contentRect.width,
        height: entries[0].contentRect.height,
      });
    });

    observer.observe(ref.current);

    return () => {
      observer.disconnect();
    };
  }, []);

  return (
    <div data-testid="theDiv" ref={ref}>
      {size.width} x {size.height}
    </div>
  );
};

const resizeObserver = mockResizeObserver();

it('prints the size of the div', () => {
  render(<DivWithSize />);

  const theDiv = screen.getByTestId('theDiv');

  expect(screen.getByText('0 x 0')).toBeInTheDocument();

  mockElementBoundingClientRect(theDiv, { width: 300, height: 200 });

  act(() => {
    resizeObserver.resize(theDiv);
  });

  expect(screen.getByText('300 x 200')).toBeInTheDocument();

  mockElementBoundingClientRect(theDiv, { width: 200, height: 500 });

  act(() => {
    resizeObserver.resize(theDiv);
  });

  expect(screen.getByText('200 x 500')).toBeInTheDocument();
});

API

mockResizeObserver returns an object, that has one method:

.resize(elements: HTMLElement | HTMLElement[])

Triggers all resize observer callbacks for all observers that observe the passed elements