'window.dispatchEvent from test code does not trigger window.addEventListener
I have below listener added for which I am trying to write test using Jest. However, it seems as though the event I'm dispatching doesn't reach my code.
window.addEventListener('message', (event) => {
    if (event.data.type === 'abc') {
      console.log(event.data.payload);
    }
});
I have tried below 2 approaches and both of them don't seem to work. I'm unable to verify the call using the spy object I'm creating. Please refer to the code below:
- 
const listenerSpy = jest.spyOn(window, 'addEventListener'); const data = { type: 'abc', payload: '', }; const messageEvent = new MessageEvent('message', {data}); window.dispatchEvent(messageEvent); expect(listenerSpy).toHaveBeenCalledTimes(1);
- 
const listenerSpy = jest.spyOn(window, 'addEventListener'); const data = { type: 'abc', payload: '', }; window.postMessage(data, '*'); expect(listenerSpy).toHaveBeenCalledTimes(1);
For the 1st approach, have also tried using 'new Event('message')'.
With above 2 approaches, I get the error as below:
expect(jest.fn()).toHaveBeenCalledTimes(expected)
Expected number of calls: 1
Received number of calls: 0
  102 |     window.dispatchEvent(messageEvent);
  103 |
> 104 |     expect(listenerSpy).toHaveBeenCalledTimes(1);
      |                         ^
I have also tried to follow different websites including below: https://medium.com/@DavideRama/testing-global-event-listener-within-a-react-component-b9d661e59953 https://github.com/enzymejs/enzyme/issues/426
But no luck there as with typescript, I cannot follow the solution given. I have also tried to find answers on stackoverflow, but the none of solutions suggested seem to work for me.
I am new to react and got stuck with this. Any pointers on this would help.
Solution 1:[1]
Have you tried taking a look at this discussion? It seems to have a similar requirement. Dispatch a Custom Event and test if it was correctly triggered (React TypeScript, Jest)
Solution 2:[2]
jsdom fire the message event inside a setTimeout, see this
setTimeout(() => {
  fireAnEvent("message", this, MessageEvent, { data: message });
}, 0);
For more info, see issue
So, it's asynchronous and you need to wait for the macro task scheduled by setTimeout to be finished before the test case ends.
index.ts:
export function main() {
  window.addEventListener('message', (event) => {
    if (event.data.type === 'abc') {
      console.log(event.data.payload);
    }
  });
}
index.test.ts:
import { main } from './';
function flushMessageQueue(ms = 10) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
describe('71912032', () => {
  test('should pass', async () => {
    const logSpy = jest.spyOn(console, 'log');
    main();
    const data = { type: 'abc', payload: 'xyz' };
    window.postMessage(data, '*');
    await flushMessageQueue();
    expect(logSpy).toBeCalledWith('xyz');
  });
});
Test result:
 PASS  stackoverflow/71912032/index.test.ts
  71912032
    ? should pass (41 ms)
  console.log
    xyz
      at console.<anonymous> (node_modules/jest-mock/build/index.js:845:25)
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        4.01 s, estimated 13 s
Also, take a look at this question React Jest: trigger 'addEventListener' 'message' from tests
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | lynx | 
| Solution 2 | slideshowp2 | 
