The window.postMessage calls are often used by complex web applications to send messages between different frames. In this blog post, I will show how to confirm the message was sent when using Cypress end-to-end test runner.
The application
đ You can find the source code for this blog post in the repository bahmutov/cypress-window-message-example.
The top document loads its JavaScript and includes an inner iframe element.
1 | <html> |
1 | <html> |
The top window is listening to the messages sent by the inner frame
1 | window.addEventListener('message', (e) => { |
1 | console.log('in the inner script') |
We see the console messages when visiting the page in the regular browser

Let's confirm this communication works.
Make the application work
Let's visit the page from a Cypress test and see if it works
1 | /// <reference types="cypress" /> |
Open Cypress with npx cypress open ... and the message from the inner frame never shows up âšī¸

The problem happens because the inner frame communicates using window.top.postMessage method call. When Cypress visits the site, it embeds the site in an iframe. Thus the Cypress is the top window, and it receives the message from the inner frame.

Hmm, how do we tell the inner iframe to send the message to the correct application window? Luckily, Cypress can rewrite the window.top references on the fly to make sure the message gets to the original intended top window object. In the cypress.json enable the following flag:
1 | { |
Now we see the message sent during the test.

Ok, let's test it.
Spy on the console.log method call
The top window logs the received messages. Let's confirm one of the console.log calls prints the message from the inner frame with text "inner frame is ready". We can spy on the console.log method, see the Stubs, Spies, and Clocks guide.
1 | it('spies on console.log call', () => { |
Notice the placeholder Cypress.sinon.match.string used during the assertion - we are not interested in the second argument, we only want to find if the console.log was called with ("TOP:", some string, "inner frame is ready") parameters. For Sinon spying assertion examples see my Spies and stubs examples page. There are several console.log calls made by the application, but we confirm only the one that interests us.

Listening to the window.postMessage
What if the top application window does not log the message send by the inner frame? What if we are only interested in the message "inner frame is ready" sent by the inner frame to know that the application is ready to be tested? We can listen to that message from the spec file.
1 | it('receives the window messages', () => { |
The command cy.stub creates a function that we can call ourselves. In the test above, we call the stub function with the message data, which should be just the string "inner frame is ready"
1 | win.addEventListener('message', (e) => { |
By giving our stub an alias, we can conveniently load it again and confirm that at some point it gets called with the expected argument
1 | // confirm the inner frame sent the ready message |

Pretty sweet, isn't it