Testing an app inside an Iframe using Cypress

Martin Džejky Jakubik
3 min readSep 12, 2018

What I’ve learned from using Cypress with iframes.

Why an iframe

My Angular application runs inside an iframe on another, completely unknown website. It is set up that way to make sure that it has a separate CSS and JS scope from the host and does not conflict with the page. However, this presents a unique challenge. How do you develop and test an application like this?

Initially, the application was set up so it does not use an iframe when running on localhost. But that meant that the iframe version was not tested, nor used when developing. Only in production.

Yep. So recently I made a change — the application always runs inside an iframe, both when I am working on it and in Cypress tests.

Cypress and iframes

There’s a long Github issue in the Cypress repo about iframes about how to handle them. I read the issue and figured that it should be completely possible to write Cypress tests for an iframe app. There are 2 issues that needed to be handled:

1. Target elements inside the iframe
2. Mock routes inside the iframe

Target elements

This issue is solved very well inside the linked Github issue. Basically, I have created a function like this:

export function runInEditor(callback: () => void) {
return cy
.log(‘runInEditor’)
.get(`#${EDITOR_CONTAINER_ID} > iframe`, { log: false })
.should(iframe => expect(iframe.contents().find(‘ef-app-root’)).to.exist)
.then(iframe => cy.wrap(iframe.contents().find(‘ef-app-root’), { log: false }))
.within({ log: false }, callback);
}

You can see that it does 2 things — it waits for the iframe and the editor inside it to load (handled by the .should() call) and it calls .within (documentation here) which makes sure that the commands run in the callback are executed in the context of the iframe. It works really well!

Mock routes

As you know, iframes have different JS scopes than the host. I mock a couple of routes with Cypress in the tests but Cypress apparently handles this by patching XMLHttpRequest in the host page. Mocking did not work in the iframe and my app would make real requests to the backend (and fail).

It turns out that the fix is very simple (and I was actually surprised that it worked that well). Somewhere in the test page, you have to do this:

iframe.contentWindow.XMLHttpRequest = XMLHttpRequest;
iframe.contentWindow.fetch = fetch;

The XMLHttpRequest line is probably enough but I added fetch just in case. It works as expected.

Downsides

The only downside I have encountered with this setup is that iframes are not supported in snapshots. That means that when you scroll through commands in the Cypress command log, you won’t see anything on the page. That sucks… See the small demonstration video below.

Thanks for reading! If you like the article, feel free to share it and clap for it. If you would like to read more about Angular, make sure to follow me or Bratislava Angular. Cheers!

--

--

Martin Džejky Jakubik

Frontend developer @ Exponea. Writing about things I learn along the way.