JEST | Assert a function was called inside addEventListener callback

I have the following function:

Code to test

export default function main() {
    const createAndAppendPTag = () => {
        const p = document.createElement('p');
        document.body.appendChild(p);
    };

    window.document.addEventListener('click', () => {
        createAndAppendPTag();
    });
}

The question is: How can I assert using Jest that createAndAppendPTag was called upon a document click event ?



Jest

This is what I tried, but can’t seem to make the test pass:

import main from './main'

window.document.addEventListener = jest.fn();
const createAndAppendPTag = jest.fn();

describe('Main', () => {
    const documentClickEvent = new Event('click');

    test('appends p tag to the document', () => {
        // dispatching event before and after invoking `main` to be sure
        window.document.dispatchEvent(documentClickEvent);

        main();

        window.document.dispatchEvent(documentClickEvent);

        expect(window.document.addEventListener).toHaveBeenNthCalledWith(1, 'click', () => {});
        expect(createAndAppendPTag).toHaveBeenCalledTimes(1);
    });
});

Terminal

This results in the following:

🔴  Main › appends p tag to the document
    
expect(jest.fn()).toHaveBeenNthCalledWith(n, ...expected)
    
n: 1
Expected: "click", [Function anonymous]
    
Number of calls: 0
    
5   | main();
6   | window.document.dispatchEvent(documentClickEvent);
> 7 | expect(window.document.addEventListener).toHaveBeenNthCalledWith(1, 'click', () => {});
*   |                                          ^

Thanks in advance.

Answer

I ran this simplified test to check for the side effect (p element was appended to body):

main.js

export default function main() {
  const createAndAppendPTag = () => {
    const p = document.createElement('p');
    document.body.appendChild(p);
  };

  window.document.addEventListener('click', () => {
    createAndAppendPTag();
  });
}

main.test.js

import main from `../main.js`;

it('"main" listener appends "P" to body upon click', () => {
  // add listener
  main();

  // clear body contents
  document.body.innerHTML = "";

  // dispatch click event to listener
  const addEvt = new Event('click');
  document.dispatchEvent(addEvt);

  // check for existence of "P" element
  const bodyEl = document.body.firstChild;
  expect(bodyEl).not.toEqual(null);
  expect(bodyEl.tagName).toBe('P');
  document.body.innerHTML = "";
});

It passed:

  ✓ "main" listener appends "P" to body upon click (2 ms)