Iframe support
See original GitHub issueUpdated Feb 9, 2021 - see note below or https://github.com/cypress-io/cypress/issues/136#issuecomment-773765339
Currently the Test Runner does not support selecting or accessing elements from within an iframe.
What users want
Users of Cypress need to access elements in an <iframe>
and additionally access an API to “switch into” and switch back out of different iframes. Currently the Test Runner thinks the element has been detached from the DOM (because its parent
document is not the expected one).
What we need to do
-
Add new
cy
commands to switch into iframes and then also switch back to the “main” frame. -
Cypress must inject itself into iframes so things like XHR’s work just like the main frame. This will ideally use something like
Mutation Observers
to be notified when new iframes are being pushed into the DOM. -
Add API to navigate between frames
-
Update the Driver to take into account element document references to known frames
Things to consider
- How will we handle snapshotting? Currently we don’t take snapshots for anything inside of an
<iframe>
. - How will we handle cross origin frames? It’s possible to enable these with
{ chromeWebSecurity: false }
(Chromium-based browsers only). - How will we show context switching in the Command Log? It should probably look / be colored differently than the ‘normal’ main commands
Examples of how we could do this
// switch to an iframe subject
cy
.get('#iframe-foo').switchToIframe()
.get('#button').click() // executed in <iframe id='iframe-foo' />
// or pass in $iframe object in hand
cy
.get('#iframe-foo').then(($iframe) => {
cy.switchToIframe($iframe)
cy.get('#button').click()
})
// now switch back to the main frame
cy
.switchToMain()
.get(':checkbox').check() // issued on the main frame
Workaround
It’s possible to run cy.*
commands on iframe elements like below:
cy.get('iframe')
.then(($iframe) => {
const $body = $iframe.contents().find('body')
cy.wrap($body)
.find('input')
.type('fake@email.com')
})
⚠️ Updates
Updates as of Feb 9, 2021
Pasting some snippets from our technical brief on iframe support that we are currently planning. As always, things can change as we move forward with implementation, but this is what we are currently planning.
If there’s any feedback/criticism on these specific proposed APIs, we’d be happy to hear it.
.switchToFrame([…args], callback)
Switches into an iframe and evals callback in the iframe. Doesn’t matter whether the iframe is same-origin or cross-origin.
Stripe payment example
// ❗️ This is planned work and does not currently work
cy.visit('someshop.com')
// ... add stuff to cart
// ... get to payment page
cy.get('iframe').switchToFrame(() => {
cy.get('#name').type('name')
cy.get('#number').type('1234-5678...')
cy.contains('Pay').click()
})
// go on with cypress commands in the main frame
cy.contains('Thanks for your order')
Same-origin iframe
Example where a site uses a same-domain iframe as a date-picker widget
// ❗️ This is planned work and does not currently work
cy.visit('https://date-picker.com')
cy.get('iframe').switchToFrame(() => {
cy.get('.next-month').click()
cy.contains('24').click()
})
// switch out of iframe context because callback is finished
cy.get('.date').should('have.text', '2/24/2021')
We also intend to support snapshots of iframes.
Issue Analytics
- State:
- Created 7 years ago
- Reactions:974
- Comments:433 (46 by maintainers)
Top GitHub Comments
commenting that I care because the docs said I should and we need this functionality for full test coverage.
This is what I ended up doing. Seems to work rather well. You can alias the
iframe()
method, but if anything in the iframe loads another URL you have to do the originalget().iframe()
again.