Spy On window.postMessage Calls

Cypress tests run in the browser and make it simple to spy or subscribe to the window events.

Cypress test runs in the browser in an iframe next to the application's iframe. This makes it extremely simple to spy, stub, and subscribe to the events sent to the application's window object by other windows or by workers via window.postMessage method calls.

Spy on the method calls

The simplest way to confirm some code calls window.postMessage is by spying on this method using the cy.spy command. You just need the reference to the window object and the method's name "postMessage" and let the included Sinon library do the rest. Let's say this is the application's code:

index.html
1
2
3
4
5
6
<script>
setTimeout(() => {
window.postMessage('one')
window.postMessage('two')
}, 1000)
</script>

🎁 You can find the source code and the tests shown in the blog post in the repo bahmutov/cypress-post-message.

Let's spy on those calls and confirm they were made. We need to register the spy before any of the application's own code runs, otherwise we might lose calls. We can use cy.visit command's onBeforeLoad callback to register our spy before the actual page loads.

cypress/e2e/spec.cy.js
1
2
3
4
5
6
7
8
9
10
11
it('listens to the window.postMessage events', () => {
cy.visit('index.html', {
onBeforeLoad(win) {
cy.spy(win, 'postMessage').as('postMessage')
},
})
cy.get('@postMessage')
.should('have.been.calledTwice')
.and('have.been.calledWithExactly', 'one')
.and('have.been.calledWithExactly', 'two')
})

The cy.get('@postMessage').should('have.been.calledTwice') command and assertion combination retries until the application code really makes two calls.

The spy on postMessage test

Tip: you can find a lot of spy and stub assertions in my examples page glebbahmutov.com/cypress-examples/commands/spies-stubs-clocks.html.

You can watch the above test in action and my explanation in the video "Spy On The Window PostMessage Calls" embedded below:

Listen to the window events

What if there are several sources of window.postMessage calls? We don't have to spy on every caller, instead we could simply subscribe to the window events from the test itself and confirm the expected events were delivered. There is one trick we can employ: we can "strip" the actual event object and only keep track of the "event.data" property

The entire event object contains the "data" property we are interested in

The caller uses window.postMessage('one') while the listener callback gets the entire event object, with data: "one". We want to keep track of the data property from each event only. In our new app, the parent page includes the child iframe.

parent.html
1
2
3
4
5
6
7
8
9
<body>
<h1>Parent window</h1>
<iframe src="child.html"></iframe>
<script>
window.addEventListener('message', (e) => {
console.log('got message', e)
})
</script>
</body>
child.html
1
2
3
4
5
6
7
8
9
<body>
<h1>Child window</h1>
<script>
setTimeout(() => {
window.parent.postMessage('one')
window.parent.postMessage('two')
}, 1000)
</script>
</body>

Let's subscribe to the window message events from the spec. We will create a single stub function and we will call that function from the subscription event listener callback.

cypress/e2e/parent-spec.cy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
it('listens to the window.parent.postMessage events', () => {
const postMessageStub = cy.stub().as('postMessage')

cy.visit('parent.html', {
onBeforeLoad(win) {
win.addEventListener('message', (e) => {
// we are only interested in the argument itself
postMessageStub(e.data)
})
},
})
cy.get('@postMessage')
.should('have.been.calledTwice')
.and('have.been.calledWithExactly', 'one')
.and('have.been.calledWithExactly', 'two')
})

Beautiful - the expected calls were delivered, and we only calls our stub function with the arguments like "one" and "two".

Subscribed to the message events and called the stub function

You can watch this example in action in the video "Subscribe To The Message Events From The Test" below:

For more examples like these, subscribe to my YouTube channel and check out the playlist Cypress Tips & Tricks. Tip: you can find videos by searching the descriptions from cypress.tips/search page.

See also