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&showMenu=false&ctrl.nested=true&appAreaId=f0d159c8-19e5-4a2a-825f-19fe4f553686&query.docTypeClass=*&categorySelection=on" style="height: 230px;" bis_size="{"x":710,"y":550,"w":550,"h":230,"abs_x":710,"abs_y":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.
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();
});
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();
});