Handling Iframes in Cypress: Native Methods vs. cypress-iframe Plugin

Jace ReedJace Reed
6 min read

cypress iframes

In this article, we explore how to effectively handle iframes in Cypress, a powerful tool for end-to-end testing. Iframes present unique challenges in web testing due to their separate DOM context, but with the right strategies, you can efficiently interact with them. We’ll discuss best practices for iframe handling, and provide sample iframe application and respective scripts.

Iframe Overview

An inline frame (iFrame) is an element that loads another HTML element inside of a web page. They are commonly used to embed specific content like external ads, videos, tags, or other interactive elements into the page.

Interaction with iframes can be challenging and may cause failures because iframes have a separate DOM context, traditional methods of interaction can fail, necessitating specialized approaches using cypres.

Here’s a breakdown of the two primary approaches:

1. cypress-iframe Plugin

The cypress-iframe plugin provides simplified commands to manage iframe interactions:

  • Advantages:

    • Ease of Use: Commands like frameLoaded() and iframe() simplify iframe handling, making tests more readable and quicker to write.

    • Efficiency: Reduces boilerplate code, allowing testers to focus on business logic rather than technical intricacies.

  • Disadvantages:

    • Dependency Management: Adds a third-party dependency that requires monitoring for updates and compatibility.

    • Limited Control: Abstracts details, which can complicate debugging and handling of edge cases.

2. Native Cypress Methods

Gleb Bahmutov, a prominent Cypress expert and a well-known figure in the Cypress testing community, strongly advocates for using native Cypress commands due to their seamless integration and the performance benefits they offer.

Advantages:

  • No External Dependencies: Avoids relying on third-party plugins, reducing potential maintenance issues.

  • Greater Control: Provides complete control over iframe interactions, using commands like cy.get().its(‘0.contentDocument.body’) to access iframe content.

  • Custom Commands: Encourages creating reusable custom commands to handle iframes consistently across tests.

Example Custom Command:

 Cypress.Commands.add('getIframeBody', (iframeSelector) => {
  return cy.get(iframeSelector)
    .its('0.contentDocument.body')
    .should('not.be.empty')
    .then(cy.wrap());
});

Recommendation

  • Use cypress-iframe if you prioritize simplicity and rapid test development, especially in projects with frequent iframe use and simpler testing needs.

  • Adopt Native Methods if you require more control and stability, avoiding external dependencies, particularly in complex or long-term projects.

Best practices for testing iframes using native methods:

  • Ensure you are using Cypress version 10 or later. Currently, we are using version 10.11.0

  • Cypress does not automatically have access to the DOM inside iframes.

Test Application:

sample iframe test application cypress

-> Approach- cy.iframe(): Not able to access the ‘.button’ class and leading to timeout error.

describe('Localhost Application Test Suite', () => {
   beforeEach(() => {
       cy.visit('http://localhost:3000');
   });


   it('click a button inside the first iframe1 and verify text', () => {
       cy.iframe('[data-cy="the-frame-1"]').then($iframeBody => {
           cy.log('Checking iframe body:', $iframeBody.html());
           cy.wrap($iframeBody).find('.button').then($button => {
               cy.log('Button found:', $button);
           });
       });


   });


   it('click a button inside the first iframe2 and verify text', () => {
       cy.iframe('[data-cy="the-frame-2"]').then($iframeBody => {
           cy.log('Checking iframe body:', $iframeBody.html());
           cy.wrap($iframeBody).find('.button').then($button => {
               cy.log('Button found:', $button);
           });
       });


   });


});

Test Report:

test report cypress iframe

-> Approach 1: Using .get(0) to Access the document

This approach directly accesses the iframe’s document object, allowing you to manipulate it using Cypress methods:

describe('Iframe Test', () => {
   beforeEach(() => {
       cy.visit('http://localhost:3000');
   });

   it('should interact with elements inside an iframe', () => {
       // First, get the iframe element
       cy.get('[data-cy="the-frame-1"]').then($iframe => {
           // Then access the document of the iframe
           const doc = $iframe.contents().get(0);

           // Wrap the document to use Cypress commands on it
           cy.wrap(doc).then((doc) => {
               // Find elements inside the document
               const button = Cypress.$(doc).find('button');
               cy.wrap(button).click();
           });
       });
   });
})

.get(0): This method retrieves the document element from the jQuery object representing the iframe. It’s the equivalent of accessing iframe.contentDocument in plain JavaScript

-> Approach 2: Using .find(‘body’) to Access the Iframe Content

This method involves accessing the body of the iframe document and then using Cypress commands within that context:

describe('Iframe Test', () => {
   beforeEach(() => {
       cy.visit('http://localhost:3000');
   });

   it('should click the button inside Iframe 1', () => {
       cy.get('[data-cy="the-frame-1"]').then(($iframe) => {
           const iframeBody = $iframe.contents().find('body');
           cy.wrap(iframeBody).within(() => {
               cy.get('button').click();
           });
       });
   });

   it('should click the button inside Iframe 2', () => {
       cy.get('[data-cy="the-frame-2"]').then(($iframe) => {
           const iframeBody = $iframe.contents().find('body');
           cy.wrap(iframeBody).within(() => {
               cy.get('button').click();
           });
       });
   });
  });
})

.find(‘body’): This method finds the body element within the iframe’s document. It’s a more specific way of scoping the query to the body, which is useful if you want to ensure that you’re interacting within the visible area of the iframe.

->We can create a custom function to access iframes elements:

The clickButtonInIframe custom command assumes it is defined in cypress/support/commands.js

// Custom command to access iframe body and perform an action
Cypress.Commands.add('clickButtonInIframe', (iframeSelector, buttonSelector) => {
 cy.get(iframeSelector)
   .its('0.contentDocument.body')
   .should('not.be.empty')
   .then(cy.wrap)
   .find(buttonSelector)
   .should('be.visible')
   .click();
 return
});

->Test Script:

describe('Iframe Test', () => {
   beforeEach(() => {
       cy.visit('http://localhost:3000');
   });

   it('should click the button inside Iframe 1', () => {

       cy.get('[data-cy="the-frame-1"]').then(($iframe) => {
           const iframeBody = $iframe.contents().find('body');
           cy.wrap(iframeBody).within(() => {
               cy.get('button').click();
           });
       });
   });


   it('should click the button inside Iframe 2', () => {
       cy.get('[data-cy="the-frame-2"]').then(($iframe) => {
           const iframeBody = $iframe.contents().find('body');
           cy.wrap(iframeBody).within(() => {
               cy.get('button').click();
           });
       });
     });


   it('should interact with elements inside an iframe using direct document access', () => {


       cy.get('[data-cy="the-frame-1"]').then($iframe => {
           // Then access the document of the iframe
           const doc = $iframe.contents().get(0);


           // Wrap the document to use Cypress commands on it
           cy.wrap(doc).then((doc) => {
               // Find elements inside the document
               const button = Cypress.$(doc).find('button');
               cy.wrap(button).click();
           });
       });
   });


   it('using cypress command for clicking a button inside the iframe', () => {
       cy.clickButtonInIframe('[data-cy="the-frame-1"]', 'button'); 
   });


});

Test Reports:

sample test report cypress iframe

image1

 Cypress test specs and sample iframe application for testing iframe interactions.

Attempt to directly access the button inside iframes 1,2 without proper access.

->Test Script:

describe('Negative Iframe Test', () => {
   beforeEach(() => {
       cy.visit('http://localhost:3000');
   });


   it('should fail to click the button inside Iframe 1 without accessing its body', () => {
       // Attempt to directly click the button inside Iframe 1 without proper access
       cy.get('[data-cy="the-frame-1"]').then(($iframe) => {
           // Attempt to find the button in the iframe's body directly
           expect(() => {
               cy.wrap($iframe).find('button').click();
           }).to.throw();
       });
   });


   it('should fail to click the button inside Iframe 2 without accessing its body', () => {
       // Attempt to directly click the button inside Iframe 2 without proper access
       cy.get('[data-cy="the-frame-2"]').then(($iframe) => {
           // Attempt to find the button in the iframe's body directly
           expect(() => {
               cy.wrap($iframe).find('button').click();
           }).to.throw();
       });
   });
});

 Cypress test failure for iframe interaction, showing code and sample application side-by-side.


Wrapping Up

Mastering the handling of iFrames in Cypress is essential for any developer or tester working with complex web applications. By following the best practices and sample scripts outlined, you can ensure reliable and efficient test interactions with iframe content, enhancing the quality of your testing efforts. We welcome your feedback on these suggested approaches.

Source: This blog was originally published at https://testgrid.io/blog/handling-iframes-in-cypress/

0
Subscribe to my newsletter

Read articles from Jace Reed directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Jace Reed
Jace Reed

Senior Software Tester with 7+ years' experience. Expert in automation, API, and Agile. Boosted test coverage by 30%. Focused on delivering top-tier software.