24

I'm trying to test a component that has a child component that includes react-intersection-observer but I always get an error

I tried to import the library but it still fails. This the initial test

    beforeEach(() => {
      testContainer = document.createElement("div");
      document.body.appendChild(testContainer);
      subject = memoize(() =>
        mount(<FilterNav {...props} />, { attachTo: testContainer })
      );
    });

    afterEach(() => {
      testContainer.remove();
    });

    context("the main filter is shown initially", () => {
      it("sets focus on the primary filter", () => {
        subject();
        const input = testContainer.querySelector(".main-input");
        expect(document.activeElement).toEqual(input);
      });

I'm getting this error -> Uncaught [ReferenceError: IntersectionObserver is not defined]

Is there a way I can mock IntersectionObserver?

Thanks

Ben Smith
  • 19,589
  • 6
  • 65
  • 93
Rafael Simões
  • 347
  • 1
  • 4
  • 12

7 Answers7

27

you can do something like this in mock file(i called it intersectionObserverMock.js for exmple):

const intersectionObserverMock = () => ({
     observe: () => null
})
window.IntersectionObserver = jest.fn().mockImplementation(intersectionObserverMock);

and in the test file import the file directly like this:

import '../__mocks__/intersectionObserverMock';

this will solve youre "IntersectionObserver is not defined" issue

Erez Lieberman
  • 1,893
  • 23
  • 31
  • 1
    This worked for me in combination with declaring an Window interface like this: https://github.com/Microsoft/TypeScript/issues/16255#issuecomment-436935103 declare global { interface Window { IntersectionObserver: typeof IntersectionObserver } } – Maximilian Lindsey Oct 07 '20 at 14:02
  • Minor addition to this is to also add other functions that you are using on observer. In my case I had to add `disconnect: () => null` because I used `observer.disconnect()` in my code – metodribic May 11 '23 at 08:15
19

Having same issue with Component that using IntersectionObserver.

Update: we need to import intersection-observer directly on test file.

import 'intersection-observer';

omdjin
  • 359
  • 2
  • 8
13

The only thing that worked for me, create a file src\__mocks__\intersectionObserverMock.js

global.IntersectionObserver = class IntersectionObserver {
  constructor() {}

  observe() {
    return null;
  }

  disconnect() {
    return null;
  };

  unobserve() {
    return null;
  }
};

In your test import file:

import './__mocks__/intersectionObserverMock'
Teebu
  • 677
  • 7
  • 18
13

Create a mock in your setupTests file:

// Mock IntersectionObserver
class IntersectionObserver {
  observe = jest.fn()
  disconnect = jest.fn()
  unobserve = jest.fn()
}

Object.defineProperty(window, 'IntersectionObserver', {
  writable: true,
  configurable: true,
  value: IntersectionObserver,
})

Object.defineProperty(global, 'IntersectionObserver', {
  writable: true,
  configurable: true,
  value: IntersectionObserver,
})

This will sort it not being defined.

Sir.Nathan Stassen
  • 1,843
  • 4
  • 21
  • 25
Matthew Hill
  • 131
  • 1
  • 2
5

To fix this issue I'd recommend using mockAllIsIntersecting from test-utils.js in react-intersection-observer. This function mocks the IntersectionObserver.

e.g.

import { mockAllIsIntersecting } from 'react-intersection-observer/test-utils';

it("sets focus on the primary filter", () => {
    subject();
    mockAllIsIntersecting(true);

    const input = testContainer.querySelector(".main-input");
    expect(document.activeElement).toEqual(input);
  });
Ben Smith
  • 19,589
  • 6
  • 65
  • 93
3

I actually was able to solve this issue with a mock function and include it in the window object

const observe = jest.fn();

window.IntersectionObserver = jest.fn(function() {
  this.observe = observe;
});
Rafael Simões
  • 347
  • 1
  • 4
  • 12
1

I extended Teebu's solution to trigger a first intersecting element. My test then act as if the intersection would match.

global.IntersectionObserver = class IntersectionObserver {

  constructor(private func, private options) {}

  observe(element: HTMLElement) {
    this.func([{isIntersecting: true, target: element}]);
  }

  disconnect() {
    return null;
  };

  unobserve() {
    return null;
  }
};
David Dal Busco
  • 7,975
  • 15
  • 55
  • 96