cypresscross-domainrestriction

Cypress Cross-Origin Restriction – Accessing Elements Inside an iFrame


I'm facing an issue related to cross-origin restrictions. The website I'm testing contains an iframe with a src attribute pointing to a different origin than the main website's URL.

The iframe within the website has Access-Control-Allow-Origin: * set in its response headers.

The website's HTML structure is similar to the following

<html>
  <head></head>
  <body>
    <a data-test-id="skip-to-content">
      <span class="css-40g50j">Skip to main content</span>
    </a>
    <iframe title="Automation" sandbox="allow-same-origin" src="https://testDev.com/qa">
      #document (https://testDev.com/qa#test)
      <!DOCTYPE html>
      <html lang="en">
        <head></head>
        <body>
          <select id="test-selection">
            <option value="test1" data-test-id="test1">Test 1</option>
            <option value="test2" data-test-id="test2">Test 2</option>
          </select>
        </body>
      </html>
    </iframe>
  </body>
</html>

I've tried multiple approaches to access elements inside the iframe, but none have been successful. Some of the solutions I've attempted include:

Solution 1: I have set chromeWebSecurity: false in Cypress (cypress.config.ts), but the website requires web security to complete the login process.

Solution 2:

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

describe('Test iframe', () => {
  it('Test iframe:  ', () => {
    cy.visit("https://dev123.app.com");

    cy.get('iframe[title="Automation"]').should('be.visible');
    cy.getIframe('iframe[title="Automation"]').find('#test-selection').should('be.visible');
  });
});

It shows the error:

its.0.contentDocument.body
CypressError
Timed out retrying after 10000ms: cy.its() errored because the property: 0.contentDocument.body does not exist on your subject.

cy.its() waited for the specified property 0.contentDocument.body to exist, but it never did.

If you do not expect the property 0.contentDocument.body to exist, then add an assertion such as:

cy.wrap({ foo: 'bar' }).its('quux').should('not.exist')Learn more
_lib/support/commands.ts:20:1
18 | Cypress.Commands.add('getIframe', function (selector) {
19 | return cy.get(selector)
> 20 | .its('0.contentDocument.body')
| ^
21 | .should('not.be.empty')
22 | .then(cy.wrap);
23 | });

Solution 3:

Use cypress-iframe Plugin.

Install and import to project:

npm install -D cypress-iframe

import 'cypress-iframe';

it('Test iframe:  ', () => {
  cy.visit("https://dev123.app.com");

  cy.frameLoaded('iframe[title="Automation"]');
  cy.iframe().find('#test-selection').should('be.visible');
});

It shows the error:

25 then{timeout: 30000}, function(){}
SecurityError
Failed to read a named property 'toString' from 'Location': Blocked a frame with origin "https://dev123.app.com" from accessing a cross-origin frame.
node_modules/cypress-iframe/dist/index.js:97:41
   95 |                                 : (_a = fullOpts.url) === null || _a === void 0 ? void 0 : _a.test(contentWindow.location.toString());
   96 |                         }
>  97 |                         : function () { return contentWindow.location.toString() !== 'about:blank'; };
      |                                         ^
   98 |                     _c.label = 1;
   99 |                 case 1:
  100 |                     if (!!hasNavigated()) return [3, 3];

Solution 4:

it('Test iframe:  ', () => {
  cy.visit("https://dev123.app.com");

  cy.origin('https://testDev.com/qa', () => {
    cy.get('iframe[title="Automation"]').its('0.contentWindow.document').should('exist')
        .then((doc) => {
          cy.wrap(doc).find('#test-selection').should('be.visible');
        });
    });
});
It shows the error:

24 originhttps://testDev.com
25 getiframe[title="Automation"]
CypressError
Timed out retrying after 10000ms: The command was expected to run against origin https://testDev.com but the application is at origin https://dev123.app.com.

This commonly happens when you have either not navigated to the expected origin or have navigated away unexpectedly.

Using cy.origin() to wrap the commands run on https://dev123.app.com will likely fix this issue.

cy.origin('https://dev123.app.com', () => {
  <commands targeting https://dev123.app.com go here>
})Learn more
e2e/test.spec.ts:37:1
  35 |         // });
  36 |         // cy.getIframe('iframe[title="Automation"]').find('#test-selection').should('be.visible');
> 37 |         cy.origin('https://testDev.com', function () {
     | ^
  38 |             cy.get('iframe[title="Automation"]').its('0.contentWindow.document').should('exist')
  39 |                 .then(function (doc) {
  40 |                 cy.wrap(doc).find('#test-selection').should('be.visible'); 
View stack trace

Have you encountered this issue before? If so, how did you resolve it? Thank you so much!


Solution

  • Cypress tells us we cannot do this:- Cross-origin iframes

    Cross-origin iframes

    If your site embeds an <iframe> that is a cross-origin frame, Cypress won't be able to automate or communicate with this .

    Examples of uses for cross-origin iframes

    • Embedding a Vimeo or YouTube video.
    • Displaying a credit card form from Stripe or Braintree.
    • Displaying an embedded login form from Auth0.
    • Showing comments from Disqus.

    You could simply navigate directly to the inner frame source if that suits your testing requirements.