We recently wrote an angular component, which wraps around Popper.js for our core front end team. The documentation and functionality of popper.js were very straightforward and easy to follow. We did hit some bumps when trying to write Jest for the component.

First we followed the instruction on popper.js on How to use Popper.js in Jest, which provides two ways of mock the library. Either way we could see the file gets loaded, but ends up in Typescript error:

popper_js_1.default is not a constructor

That tells us that it was actually loading the real library with import Popper from 'popper.js'; instead of mock. After dug a little more into it, it seems that popper.js index.d.ts is incompatible with the umd version. So Jest isn't loading the one with default export, thus that error message. We change the import to specify which version component actually loads in by doing:

import Popper from 'popper.js/dist/esm/popper.js'

Then in our jest.base.config.ts file, we also need to add transform ignore pattern:

transformIgnorePatterns: [
  '<rootDir>/node_modules/(popper)',
],

Now if run the tests, you'd get

TypeError: document.createRange is not a function

This is Jest actually loads up the real popper.js library. popper.js uses createRange to know the order of two given DOM elements in the DOM. But JSDOM does not have createRange defined. To solve that, we could add the definition in jest-setup.ts

global.window.document.createRange = function createRange() {
  return {
    setEnd: () => {},
    setStart: () => {},
    getBoundingClientRect: () => {
      return {right: 0};
    },
    getClientRects: () => [],
    commonAncestorContainer: document.createElement('div'),
  };
};

At this point, all the specs have correct popper.js loaded in and ready for testing!