JS - Testing code that uses an IntersectionObserver

前端 未结 6 1653
死守一世寂寞
死守一世寂寞 2020-12-18 17:57

I have a (rather poorly written) javascript component in my application that handles infinite scroll pagination, and i\'m trying to rewrite it to use the IntersectionO

相关标签:
6条回答
  • 2020-12-18 18:35

    Same problem in 2019 this is how I solved it:

    import ....
    
    describe('IntersectionObserverMokTest', () => {
      ...
      const observeMock = {
        observe: () => null,
        disconnect: () => null // maybe not needed
      };
    
      beforeEach(async(() => {
        (<any> window).IntersectionObserver = () => observeMock;
    
        ....
      }));
    
    
      if(' should run the Test without throwing an error for the IntersectionObserver', () => {
        ...
      })
    });
    

    So I create a mock object, with the observe (and disconnect) method and overwrite the IntersectionObserver on the window object. Depending on your usage, you might have to overwrite other functions (see: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Browser_compatibility )

    The code is inspired by https://gist.github.com/ianmcnally/4b68c56900a20840b6ca840e2403771c but doesn't use jest

    0 讨论(0)
  • 2020-12-18 18:38

    In your jest.setup.js file, mock the IntersectionObserver with the following implementation:

    global.IntersectionObserver = class IntersectionObserver {
      constructor() {}
    
      disconnect() {
        return null;
      }
    
      observe() {
        return null;
      }
    
      takeRecords() {
        return null;
      }
    
      unobserve() {
        return null;
      }
    };
    

    Instead of using the Jest Setup File, you can do this mocking also directly in your tests or in your beforeAll,beforeEach blocks.

    0 讨论(0)
  • 2020-12-18 18:38

    Had a similar stack issue as @Kevin Brotcke, except using their solution resulted in a further TypeScript error:

    Function expression, which lacks return-type annotation, implicitly has an 'any' return type.

    Here's a tweaked solution that worked for me:

    beforeEach(() => {
        // IntersectionObserver isn't available in test environment
        const mockIntersectionObserver = jest.fn()
        mockIntersectionObserver.mockReturnValue({
          observe: jest.fn().mockReturnValue(null),
          unobserve: jest.fn().mockReturnValue(null),
          disconnect: jest.fn().mockReturnValue(null)
        })
        window.IntersectionObserver = mockIntersectionObserver
      })
    
    0 讨论(0)
  • 2020-12-18 18:51

    None of the posted answered worked for me because of our configuration of TypeScript and React (tsx) we're using. Here's what finally worked:

    beforeEach(() => {
      // IntersectionObserver isn't available in test environment
      const mockIntersectionObserver = jest.fn();
      mockIntersectionObserver.mockReturnValue({
        observe: () => null,
        unobserve: () => null,
        disconnect: () => null
      });
      window.IntersectionObserver = mockIntersectionObserver;
    });
    
    0 讨论(0)
  • 2020-12-18 18:57

    Here's another alternative based on previous answers, you can run it inside the beforeEach methods, or at the beginning of the .test.js file.

    You could also pass parameters to the setupIntersectionObserverMock to mock the observe and/or unobserve methods to spy on them with a jest.fn() mock function.

    /**
     * Utility function that mocks the `IntersectionObserver` API. Necessary for components that rely
     * on it, otherwise the tests will crash. Recommended to execute inside `beforeEach`.
     * @param intersectionObserverMock - Parameter that is sent to the `Object.defineProperty`
     * overwrite method. `jest.fn()` mock functions can be passed here if the goal is to not only
     * mock the intersection observer, but its methods.
     */
    export function setupIntersectionObserverMock({
      root = null,
      rootMargin = '',
      thresholds = [],
      disconnect = () => null,
      observe = () => null,
      takeRecords = () => null,
      unobserve = () => null,
    } = {}) {
      class MockIntersectionObserver {
        constructor() {
          this.root = root;
          this.rootMargin = rootMargin;
          this.thresholds = thresholds;
          this.disconnect = disconnect;
          this.observe = observe;
          this.takeRecords = takeRecords;
          this.unobserve = unobserve;
        }
      }
    
      Object.defineProperty(window, 'IntersectionObserver', {
        writable: true,
        configurable: true,
        value: MockIntersectionObserver
      });
    
      Object.defineProperty(global, 'IntersectionObserver', {
        writable: true,
        configurable: true,
        value: MockIntersectionObserver
      });
    }
    

    And for TypeScript:

    /**
     * Utility function that mocks the `IntersectionObserver` API. Necessary for components that rely
     * on it, otherwise the tests will crash. Recommended to execute inside `beforeEach`.
     * @param intersectionObserverMock - Parameter that is sent to the `Object.defineProperty`
     * overwrite method. `jest.fn()` mock functions can be passed here if the goal is to not only
     * mock the intersection observer, but its methods.
     */
    export function setupIntersectionObserverMock({
      root = null,
      rootMargin = '',
      thresholds = [],
      disconnect = () => null,
      observe = () => null,
      takeRecords = () => null,
      unobserve = () => null,
    } = {}): void {
      class MockIntersectionObserver implements IntersectionObserver {
        readonly root: Element | null = root;
        readonly rootMargin: string = rootMargin;
        readonly thresholds: ReadonlyArray < number > = thresholds;
        disconnect: () => void = disconnect;
        observe: (target: Element) => void = observe;
        takeRecords: () => IntersectionObserverEntry[] = takeRecords;
        unobserve: (target: Element) => void = unobserve;
      }
    
      Object.defineProperty(
        window,
        'IntersectionObserver', {
          writable: true,
          configurable: true,
          value: MockIntersectionObserver
        }
      );
    
      Object.defineProperty(
        global,
        'IntersectionObserver', {
          writable: true,
          configurable: true,
          value: MockIntersectionObserver
        }
      );
    }
    
    0 讨论(0)
  • 2020-12-18 18:57

    I had this problem with a setup based on vue-cli. I ended up using a mix of the answers I saw above:

       const mockIntersectionObserver = class {
        constructor() {}
        observe() {}
        unobserve() {}
        disconnect() {}
      };
    
      beforeEach(() => {
        window.IntersectionObserver = mockIntersectionObserver;
      });
    
    0 讨论(0)
提交回复
热议问题