Search code examples
javascriptiframecypresscypress-iframe

Can't grab the select element inside iframe in cypress


I have a select element inside a iframe and need to select an option in a cypress test. But it doesn't find the element. Here is the html:

<iframe id="KBasePopUp_frame_iframe" onload="KBasePopUp_frame.onIFrameLoaded(this)" scrolling="auto" width="550" height="230" frameborder="0" src="https://bmw-rsa-asd-int.kcenter.usu.com/knowledgebase/docCreate.do?ctrl.returnPage=closeInfoBoard.nav&amp;showMenu=false&amp;ctrl.nested=true&amp;appAreaId=f0d159c8-19e5-4a2a-825f-19fe4f553686&amp;query.docTypeClass=*&amp;categorySelection=on" style="height: 230px;" bis_size="{&quot;x&quot;:710,&quot;y&quot;:550,&quot;w&quot;:550,&quot;h&quot;:230,&quot;abs_x&quot;:710,&quot;abs_y&quot;:550}"></iframe>

and the select element is somewhere down below:

<select name="query.docTypeId" onchange="markDataChanged();" class="largeSelectBoxWarn" title="Mandatory field, please insert data!">
    <option value="4002" title="Automatic question">Automatic question</option><option value="3001" title="Privacy Policy">Privacy Policy</option>
    <option value="1" title="Problem">Problem</option>
    <option value="12" title="Solution">Solution</option>
</select>

I don't have access to the developer's code and can only inspect with web tools.


Solution

  • As @spender mentioned, interacting with an iframe is described in Cypress's blog.

    You have to first retrieve the iframe document, and then its body, and then query from there.

    const getIframeDocument = () => {
      return cy
      .get('#KBasePopUp_frame_iframe')
      // Cypress yields jQuery element, which has the real
      // DOM element under property "0".
      // From the real DOM iframe element we can get
      // the "document" element, it is stored in "contentDocument" property
      // Cypress "its" command can access deep properties using dot notation
      // https://on.cypress.io/its
      .its('0.contentDocument').should('exist')
    }
    
    const getIframeBody = () => {
      // get the document
      return getIframeDocument()
      // automatically retries until body is loaded
      .its('body').should('not.be.undefined')
      // wraps "body" DOM element to allow
      // chaining more Cypress commands, like ".find(...)"
      .then(cy.wrap)
    }
    
    it('clicks the select option', () => {
        getIframeBody().find("select.largeSelectBoxWarn option[value=1]").click();
    });
    

    Making it more flexible

    The above is hard coded to your specific iframe since it's just supposed to teach you a concept. However, you could make it more flexible by accepting the query to the iframe as a parameter to getIframeBody('#KBasePopUp_frame_iframe');

    const getIframeDocument = (iframeQuery) => {
      return cy
      .get(iframeQuery)
      // Cypress yields jQuery element, which has the real
      // DOM element under property "0".
      // From the real DOM iframe element we can get
      // the "document" element, it is stored in "contentDocument" property
      // Cypress "its" command can access deep properties using dot notation
      // https://on.cypress.io/its
      .its('0.contentDocument').should('exist')
    }
    
    const getIframeBody = (iframeQuery) => {
      // get the document
      return getIframeDocument(iframeQuery)
      // automatically retries until body is loaded
      .its('body').should('not.be.undefined')
      // wraps "body" DOM element to allow
      // chaining more Cypress commands, like ".find(...)"
      .then(cy.wrap)
    }
    
    it("clicks the select option n KBasePopUp iframe", () => {
      getIframeBody("KBasePopUp_frame_iframe").find("select.largeSelectBoxWarn option[value=1]").click();
    });