I'm trying to write a vitest test around a composable that reads and writes postMessage() for communication between iframes.
The Vue docs say that if the composable relies on lifecycle hooks to mount it in a test app. So I have added test-helper.js
import { createApp } from 'vue'
export function withSetup(composable) {
let result
const app = createApp({
setup() {
result = composable()
// suppress missing template warning
return () => {}
}
})
app.mount(document.createElement('div'))
// return the result and the app instance
// for testing provide/unmount
return [result, app]
}
The test creates a mock messageHandler which should be executed when the event listener is triggered.
Then I call the postMessageFromIframe() method in my composable and test, but I'm getting some unexpected behaviour.
import { usePostMessage } from '@/composables/usePostMessage.ts';
import { vi} from "vitest";
import { withSetup } from './test-helper'
import { nextTick } from 'vue';
describe('usePostMessage', () => {
it("should deliver a post message successfully", async () => {
//ARRANGE - create a mock
const expectedPayload = { action: 'testAction' };
const messageHandler = vi.fn().mockImplementation(event => {
// ASSERT within the event handler
console.log('I was run');
expect(event.data.from).toBe('usePostMessage');
expect(event.data.payload.action).toBe(expectedPayload.action);
});
//listener
window.addEventListener('message', messageHandler);
//ACT
//launch within app
const [result,app] = withSetup(() => {
const { postMessageFromIframe } = usePostMessage();
postMessageFromIframe({action:'testAction'});
return { postMessageFromIframe };
});
//ASSERT
// Wait for next tick to allow the event to be processed
await nextTick();
expect(messageHandler).toHaveBeenCalled();
// Cleanup
window.removeEventListener('message', messageHandler);
app.unmount();
});
});
If I comment out the last expect() then the test runs fine but messageHandler is never executed. However if I uncomment the last expect() the tests fail but the messageHandler is executed. It seems that the last expect() is actually executing messageHandler. I think I am misunderstanding something here...
So it seems that
nextTick()wasn't enough to wait for all the asynchronous tasks to finish.I tried adding:
but still no luck.
Final answer was to create a Promise with a 0ms timeout: