cypress - click iframe element while the body of the iframe change after few seconds

Viewed 556

I have a problem when using cy.getIframeBody().find('#some-button') that the #some-button element is not yet available, because the iframe is still loading, but the body element is not empty so the .find() is triggered.

This is the custom command to get the iframe body

Cypress.Commands.add('getIframeBody', ()=> {
    return cy.get('iframe.cy-external-link-container')
        .its('0.contentDocument.body')
        .should('not.empty')
        .then(cy.wrap)
});

How I can do it without using cy.wait()?

2 Answers

You can add random timeouts and .should() assertions, but if they work at all the test is likely to be flaky.

The key is to repeat the query of the body element (this line)

.its('0.contentDocument.body')

until the button shows up.

So not

.its('0.contentDocument.body')
.should('not.empty')

but something like

.its('0.contentDocument.body')
.should('have.child', '#some-button')  // force a retry on body query

.should() with callback will do this

Cypress.Commands.add('getIframeBodyWithSelector', (waitForSelector) => {
  return cy.get('iframe.cy-external-link-container')
    .its('0.contentDocument.body')
    .should(body => {
      expect(Cypress.$(body).has(waitForSelector).length).gt(0)
    })
    .then(cy.wrap)
})

it('finds some button', () => {
  cy.getIframeBodyWithSelector('#some-button')
    .find('#some-button')                      // passes
})

You can add an timeout and also add should('be.visible'). should assertion will make sure that till the timeout value is reached it rerties and make sure that the iframe is loaded successfully.

Cypress.Commands.add('getIframeBody', () => {
  return cy
    .get('iframe.cy-external-link-container', {timeout: 7000})
    .should('be.visible')
    .its('0.contentDocument.body')
    .should('not.empty')
    .then(cy.wrap)
})
Related