logo@krutoo/utils

IntersectionObserverMock

Mock-implementation of IntersectionObserver with ability of simulating intersections programmatically.

Basic usage

import { IntersectionObserverMock } from '@krutoo/utils/testing';

// creating like normal IntersectionObserver
const observer = new IntersectionObserverMock(callback, options);

// using like normal IntersectionObserver
observer.observe(element);
observer.unobserve(element);
observer.disconnect();

// ...somewhere in your tests
observer.simulateIntersection([
  // all entry properties except `target` is optional
  // properties that is not provided will be filled by mocks
  {
    target: document.querySelector('#my-widget'),
    isIntersecting: false,
  },
]);

Typical unit-test

Here is an example of unit test with IntersectionObserverMock. For implementation useIntersection is used:

import { IntersectionObserverContext, useIntersection } from '@krutoo/utils/react';
import { IntersectionObserverMock } from '@krutoo/utils/testing';
import { act, render } from '@testing-library/react';
import { describe, expect, mock, test } from '#test-utils';

function MyComponent({ onChange }) {
  const ref = useRef(null);

  useIntersection(ref, () => {
    onChange?.();
  });

  return (
    <div ref={ref} data-testid='my-component'>
      Hello from my component
    </div>
  );
}

describe('MyComponent', () => {
  test('should call onChange on intersection change', () => {
    const observer = new ResizeObserverMock(() => {});

    const context = {
      getObserver(callback: IntersectionObserverCallback) {
        return observer.addCallback(callback);
      },
    };

    const spy = mock.fn();

    const { getByTestId } = render(
      <IntersectionObserverContext value={context}>
        <MyComponent onChange={spy} />
      </IntersectionObserverContext>,
    );

    // no calls after render
    expect(spy.mock.callCount()).toBe(0);

    // one call after intersection simulated
    act(() => {
      observer.simulateIntersection([
        {
          target: getByTestId('my-component'),
          isIntersecting: true,
        },
      ]);
    });
    expect(spy.mock.callCount()).toBe(1);
  });
});

As you can see, mock includes chainable addCallback method. You can use it to provide one observer for multiple components during test.