17

I have following code in my component

var rect = ReactDOM.findDOMNode(this).getBoundingClientRect();

I use d3js and render graph in the component. But when I run test there are any svg tags. I assume that it happens because all rect's fields equals 0.

Here is output for console.log(rect) in browser:

ClientRect {top: 89, right: 808, bottom: 689, left: 8, width: 800…}

and when I run test:

{ bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0 }

So is there a way to set size of the element?

Gleb
  • 1,312
  • 3
  • 18
  • 36

3 Answers3

50

My solution is to mock getBoundingClientRect (I'm currently using jest 16.0.1)

describe('Mock `getBoundingClientRect`', () => {
    beforeEach(() => {
        Element.prototype.getBoundingClientRect = jest.fn(() => {
            return {
                width: 120,
                height: 120,
                top: 0,
                left: 0,
                bottom: 0,
                right: 0,
            }
        });
    });
    it('should mock `getBoundingClientRect`', () => {
        const element = document.createElement('span');
        const rect = element.getBoundingClientRect();
        expect(rect.width).toEqual(120);
    });

});
alunyov
  • 943
  • 1
  • 10
  • 14
  • 1
    thanks for the answer. Just wondering, where is the "Element" came from? – Wayne Chiu Nov 07 '17 at 06:34
  • @WayneChiu AFAIK, jest uses jsdom to mock DOM classes (such as Element, etc) - https://github.com/tmpvar/jsdom – alunyov Nov 08 '17 at 00:08
  • 4
    N.B. If you have multiple describes that have specific cases for getBoundingClientRect, you'll need to set this mock again for that particular describe, since the global objects prototype is being mutated – Roland Jegorov Jul 30 '18 at 08:28
0

Don't forget to put the original value of getBoundingClientRect back, because changing it might affect other tests.

Also, no need to do it in beforeEach: beforeAll will do.

describe("Mock `getBoundingClientRect`", () => {
  let boundingClientRect;

  const originalGetBoundingClientRect = Element.prototype.getBoundingClientRect;

  beforeAll(() => {
    Element.prototype.getBoundingClientRect = () => boundingClientRect;
  });

  afterAll(() => {
    Element.prototype.getBoundingClientRect = originalGetBoundingClientRect;
  });

  it("should mock `getBoundingClientRect`", () => {
    const element = document.createElement("div");
    boundingClientRect = new DOMRect(0, 0, 120, 120);
    const rect = element.getBoundingClientRect();
    expect(rect.width).toEqual(120);
  });
});


I took the idea from this answer

Andrey Ozornin
  • 1,129
  • 1
  • 9
  • 24
0

The existing answers mock Element.prototype.getBoundingClientRect. This will affect the apparent size of all DOM elements in your tests.

If you know what element you care about in advance, you can mock getBoundingClientRect on a single element. This does not affect the apparent size of other elements. If you want two elements to have different sizes in the same test, this is the way you need to do it.

describe("Mock `getBoundingClientRect` on a single element", () => {
  it("should mock `getBoundingClientRect`", () => {
    const myElement = document.createElement("div");
    myElement.getBoundingClientRect = jest.fn(() => 
      new DOMRect(0, 0, 120, 120);
    );
    const rect = element.getBoundingClientRect();
    expect(rect.width).toEqual(120);
  });
});
Kalinda Pride
  • 315
  • 5
  • 9