Cypress Working with iFrames
An iframe embeds one web page inside another web page. Payment forms and embedded videos commonly use this technique. Cypress requires a different approach for interacting with elements inside an iframe compared to the main page.
What an iFrame Looks Like
<iframe id="payment-frame" src="https://payment-provider.com/form"></iframe>The content inside this tag comes from a separate web page, often hosted on a different address entirely.
A Simple Way to Picture It
Think of an iframe as a television screen sitting inside a room. The room represents your main web page, and the television represents the iframe's content. Someone standing in the room cannot directly touch what happens inside the television without a special remote control.
Why iFrames Need Special Handling
Cypress commands like cy.get normally search only the main page's content. An iframe loads its content from a separate document, which sits outside this default search area. You need an extra step to reach inside the iframe.
Accessing iFrame Content
A common approach uses a plugin or custom command to reach inside the iframe body.
cy.get('#payment-frame')
.its('0.contentDocument.body')
.should('not.be.empty')
.then(cy.wrap)
.find('#card-number')
.type('4111111111111111')This chain accesses the iframe's inner document, wraps it as a Cypress subject, then searches inside it normally.
iFrame Access Flow Diagram
Main Page | v Find the iframe element | v Access contentDocument.body | v Wrap as a Cypress subject | v Search and interact with inner elements
Using a Custom Command for iFrames
Teams working with iframes frequently often build a reusable custom command to avoid repeating this pattern.
Cypress.Commands.add('getIframeBody', (selector) => {
return cy.get(selector)
.its('0.contentDocument.body')
.should('not.be.empty')
.then(cy.wrap)
})cy.getIframeBody('#payment-frame').find('#card-number').type('4111111111111111')Cross-Origin iFrame Limitations
Cypress historically struggled with iframes loaded from a different domain than the main page, due to browser security restrictions. Recent Cypress versions include experimental support for handling cross-origin content more smoothly, though some setups still require extra configuration.
Same-Origin vs Cross-Origin
Same-Origin iFrame - Loaded from the same domain as the main page - Easier to access directly Cross-Origin iFrame - Loaded from a different domain - Requires extra handling due to browser security rules
Testing Embedded Video Players
Embedded video players often load inside an iframe from a video hosting service. Testing playback usually focuses on confirming the iframe exists and loads correctly, rather than controlling the video itself.
cy.get('iframe.video-player').should('have.attr', 'src').and('include', 'videohost.com')When to Avoid Testing Inside iFrames
Some iframe content belongs to a third-party service outside your control, such as a payment gateway. Testing deep interactions inside such content adds fragility, since the third party can change their page without notice. Many teams limit iframe testing to confirming the frame loads correctly, leaving deeper checks to the third party's own testing.
Key Points
- An iframe embeds a separate web page inside your main page.
- Cypress requires accessing contentDocument.body to reach inside an iframe.
- Custom commands help avoid repeating iframe access code across tests.
- Cross-origin iframes need extra handling due to browser security rules.
- Testing third-party iframe content deeply can make tests fragile.
