harry
harry

Reputation: 33

Simulate window, document, and mouse click event properties in Angular Jest testing

I have a function attached to an even listener that checks if you've clicked off of a pop up or in it, and closes it if you've clicked off it. It uses various properties on the window and document object to check these things, as well as getBoundingClientRect() to get the popup's position.

The issue I'm running into is I can't figure out how to simulate all of that in a test. I've seen that in Jest you use the global object as the window object when mocking things?

Here's my function that checks the positioning & click:

popoverEscape = e => {
  const popover = this.panelEl.nativeElement.getBoundingClientRect();
  const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
  const left = popover.left + scrollLeft
  const right = popover.left + scrollLeft + popover.width
  const top = popover.top + scrollTop
  const bottom = popover.top + scrollTop + popover.height

  if(!this.isBetween(e.clientX, left, right) || !this.isBetween(e.clientY, top, bottom)){
  this.closePopover()


  }
};

isBetween(n, a, b) {
   return (n - a) * (n - b) <= 0
}

Upvotes: 3

Views: 2047

Answers (1)

Lin Du
Lin Du

Reputation: 102457

Here is the unit test solution:

index.ts:

export class SomeComponent {
  panelEl = {
    nativeElement: document.createElement('div'),
  };

  popoverEscape = (e) => {
    const popover = this.panelEl.nativeElement.getBoundingClientRect();
    const scrollLeft = window.pageXOffset || document.documentElement.scrollLeft;
    const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
    const left = popover.left + scrollLeft;
    const right = popover.left + scrollLeft + popover.width;
    const top = popover.top + scrollTop;
    const bottom = popover.top + scrollTop + popover.height;

    if (!this.isBetween(e.clientX, left, right) || !this.isBetween(e.clientY, top, bottom)) {
      this.closePopover();
    }
  };

  isBetween(n, a, b) {
    return (n - a) * (n - b) <= 0;
  }

  closePopover() {}
}

index.test.ts:

import { SomeComponent } from './';

describe('59737707', () => {
  afterEach(() => {
    jest.clearAllMocks();
    jest.restoreAllMocks();
  });
  describe('#popoverEscape', () => {
    it('should close popover if first isBetween return falsy', () => {
      Object.defineProperty(window, 'pageXOffset', { value: 100 });
      document.documentElement.scrollTop = 200;
      const comp = new SomeComponent();
      const rect = { left: 10, width: 20, top: 10, height: 50 };
      jest.spyOn(comp.panelEl.nativeElement, 'getBoundingClientRect').mockReturnValueOnce(rect as DOMRect);
      jest.spyOn(comp, 'isBetween').mockReturnValueOnce(false);
      comp.closePopover = jest.fn();
      const mEvent = { clientX: 100, clientY: 200 };
      comp.popoverEscape(mEvent);
      expect(comp.panelEl.nativeElement.getBoundingClientRect).toBeCalledTimes(1);
      expect(comp.closePopover).toBeCalledTimes(1);
      expect(comp.isBetween).toBeCalledTimes(1);
    });

    it('should close popover if second isBetween return falsy', () => {
      Object.defineProperty(window, 'pageXOffset', { value: 100 });
      document.documentElement.scrollTop = 200;
      const comp = new SomeComponent();
      const rect = { left: 10, width: 20, top: 10, height: 50 };
      jest.spyOn(comp.panelEl.nativeElement, 'getBoundingClientRect').mockReturnValueOnce(rect as DOMRect);
      jest
        .spyOn(comp, 'isBetween')
        .mockReturnValueOnce(true)
        .mockReturnValueOnce(false);
      comp.closePopover = jest.fn();
      const mEvent = { clientX: 100, clientY: 200 };
      comp.popoverEscape(mEvent);
      expect(comp.panelEl.nativeElement.getBoundingClientRect).toBeCalledTimes(1);
      expect(comp.closePopover).toBeCalledTimes(1);
      expect(comp.isBetween).toBeCalledTimes(2);
    });
  });
});

Unit test results with coverage report:

 PASS  src/stackoverflow/59737707/index.test.ts (14.308s)
  59737707
    #popoverEscape
      ✓ should close popover if first isBetween return falsy (12ms)
      ✓ should close popover if second isBetween return falsy (3ms)

----------|----------|----------|----------|----------|-------------------|
File      |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
----------|----------|----------|----------|----------|-------------------|
All files |    94.12 |       75 |       50 |    93.75 |                   |
 index.ts |    94.12 |       75 |       50 |    93.75 |                21 |
----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        16.602s

You can do the same way for the uncovered branch and function and improve the test coverage to 100%.

Source code: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/59737707

Upvotes: 1

Related Questions