简体   繁体   中英

Checking inside dynamically modified iframe with Cypress

I have a page that includes a third party script (Xsolla login). This script modifies elements on the page, one of the particular elements being iframe.

  • First a placeholder element is inserted

  • Then the iframe is deleted and new iframe is inserted with different dynamically loading content

  • Both iframes have the same id

How one can detect when the second, replaced, iframe is correctly loaded as Cypress cy.get() grabs the first iframe and then never detects newly changed content within the replaced iframe?

You can use cypress-wait-until plugin and then write a custom check function that inspects deep into the iframe.

/**
 * Because Xsolla does dynamic replacement of iframe element, we need to use this hacky wait.
 */
function checkLoadedXsollaIframe(doc: Document): bool {
  try {
    const iframe = doc.getElementById('XsollaLoginWidgetIframe') as any;
    if(!iframe) {
      return false;
    }
    // This element becomes available only when Xsolla has done its magic JS loading
    const usernameInput = iframe.contentDocument.querySelector('input[name="email"]');
    return usernameInput !== null;
  } catch(e) {
    return false;
  }
}


context('Login', () => {

  beforeEach(() => {
    cy.visit(frontendURL);
  });

  it('Should login with valid credentials', () => {

    // This injects Xsolla <script> tag and then
    // this third party script takes its own course of actions 
    cy.get('.login-btn').first().click();

    cy.waitUntil(() => cy.document().then(doc => checkLoadedXsollaIframe(doc)), {timeout: 5000 });

Below is the snippet that waits for the content inside the iframe to be loaded and HTMLElements be available & no timeouts required.


const iframeElement = (selector) => {
  const iframe = cy.get(selector);
  return iframe
    .should(($iframe) => // Make sure its not blank
      expect($iframe.attr('src')).not.to.include('about:blank')
    )
    .should(($iframe) => 
    expect($iframe.attr('src')).not.to.be.empty) // Make sure its not empty
    .then(($inner) => {
      const iWindow = $inner[0].contentWindow;
      return new Promise((resolve) => {
        resolve(iWindow);
      });
    })
    .then((iWindow) => {
      return new Promise((resolve) => {
        iWindow.onload = () => { // Listen to onLoad event
          resolve(iWindow.document);
        };
      });
    })
    .then((iDoc) => {
      return cy.wrap(iDoc.body); // Wrap the element to access Cypress API
    });
};

Now access the element inside the iframeDocument


iframeElement('#my-iframe') // Grab the iframe
  .find('h2')
  .should('have.text', 'My header text'); //Assert iframe header

Note: Don't attempt to access CORS websites. It might fail due to security reasons

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM