Search code examples
jestjsjsdom

Inconsistent behaviour of mocking DOMPoint in Jest


I have a number of geometry functions that use DOMPoint and DOMRect, which I want to test using jest. The framework is configured to use jsdom as test environment. Unfortunately, jsdom does not have definitions for DOMPoint and DOMRect, hence I need to mock them.

In my setupUnitTests.ts file, which is set as setupFilesAfterEnv value in jest.config.ts I have this mock call:

Object.defineProperty(globalThis, "DOMPoint", {
    writable: true,
    enumerable: true,
    value: jest.fn().mockImplementation((x?: number, y?: number, z?: number, w?: number) => {
        return {
            x: x ?? 0,
            y: y ?? 0,
            z: z ?? 0,
            w: w ?? 0,
            matrixTransform: jest.fn(),
            toJSON: jest.fn(),
        };
    }),
});

const p = new DOMPoint(1, 2, 3, 4);

The call at the end returns a DOM point instance with the expected values, fine.

In my test spec, however, the DOMPoint instance is not initialised. The mock implementation function is not called, like it is when I create a DOM point in the setup file. My spec file is very simple:

import { inflateRect, pointInRect, rectsAreEqual } from "../../../utilities/graphics";

describe("Graphics Tests", () => {
    it("Rectangles", () => {
        const point1 = new DOMPoint(2, 1, 3, 4);
        const point2 = new DOMPoint(-95, 3, 4, 1);
        const rect1 = new DOMRect(0, 0, 0, 0);
        const rect2 = new DOMRect(-100, 0, 10, 10);
        const rect3 = new DOMRect(0, 0, 10, 10);
        const rect4 = new DOMRect(0, 0, 10, 10);
        const rect5 = new DOMRect(-1, -1, 12, 12);

        expect(pointInRect()).toBe(false);
        expect(pointInRect(point1)).toBe(false);
        expect(pointInRect(point1, rect1)).toBe(false);
        expect(pointInRect(point1, rect2)).toBe(false);
        expect(pointInRect(point2, rect2)).toBe(true);

        expect(rectsAreEqual(rect1, rect2)).toBe(false);
        expect(rectsAreEqual(rect3, rect4)).toBe(true);
        expect(rectsAreEqual(rect4, rect3)).toBe(true);

        expect(inflateRect(rect3, 1, 1, 1, 1)).toStrictEqual(rect5);
    });

});

What's missing here? Why is the function given to jest.fn().mockImplementation not called?

The mock itself is definitely used, because when I remove it I get the error about DOMPoint not being defined.

I then moved the mock to the spec file:

describe("Graphics Tests", () => {
    Object.defineProperty(global.self, "DOMPoint", {
        writable: true,
        enumerable: true,
        value: jest.fn().mockImplementation((x?: number, y?: number, z?: number, w?: number) => {
            return {
                x: x ?? 0,
                y: y ?? 0,
                z: z ?? 0,
                w: w ?? 0,
                matrixTransform: jest.fn(),
                toJSON: jest.fn(),
            };
        }),
    });

    it("Rectangles", () => {
        const point1 = new DOMPoint(2, 1, 3, 4);
        ...
    });

});

and found the mock function still not being called (but the mock defined). This is pretty confusing...


Solution

  • While debugging through the Jest code to compare the execution paths between the constructor invocations, I saw that in the spec file I had no mock config anymore (the default was used), which ultimately led me to the solution of my problem. In the jest config file I had set resetMocks: true, which caused the mocks to be removed. After setting that to false things started working as expected.