-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Iframe support #136
Comments
commenting that I care because the docs said I should and we need this functionality for full test coverage. |
We also would need this functionality to be able to really use Cypress. Our app relies on rendering into an iframe for a significant part of it‘s functionality (it’s an editor, and the iframe is used to sandbox the thing being edited), and being able to target elements in that iframe is pretty essential to our testing. |
Is the We can pretty easily add same domain iframe support. Cross domain will take a lot more work. The difference will likely be days of work vs 1-2 weeks. |
Same domain. The iframe is really just a sandboxed canvas that we render into, rather than a 3rd party resource that we are loading into the app. (And thanks for the quick reply!) |
same domain iframe would allow us to test emails via e.g. mailinator.com |
Cypress actually injects to forcibly enable it to access same domain This is coming up on our radar and it will introduce a few more API's to enable switching in and out of As a workaround today you can actually still target these elements and potentially perform actions on them by you cannot return them or use them in cypress commands. So this actually works: cy.get("iframe").then(function($iframe){
// query into the iframe
var b = $iframe.contents().find("body")
// you can work with this element here but it cannot
// be returned
var evt = new Event(...)
b.dispatchEvent(evt)
return null
}) |
I would need that feature too. To really test all use cases of our credit card from which is implemented by using braintree hosted fields api. They inject iframes into div and do the complete validation. However to test that our visuals and the submission to the server works I would need to be able to access those iframes. |
@harz87 the If that's the case you'll need to disable web security before being able to access them. After you do that you can at least manually interact with them and force their values to be set - although you won't be able to use Cypress API's until we add support for switching to and from iframes. |
This is something that'd be pretty important for my company. We've currently got a working ruby+selenium testing setup, however not all of our company is able to take advantage of those tools as we have some PHP and Go codebases, each of which is rapidly accumulating more and more JS heavy interfaces we'd like to be able to test. We're looking at cypress as a possible candidate to standardise on, but iframe support is currently a blocker. The specific test case we're evaluating is our payments flow, which loads a modal containing an iframe on the same domain with all of the controls for inputting payment details and submitting requests to pay to the server. Here's a screenshot to give you a better idea: All of the content you can see is actually embedded in an iframe. Our tests go through the various payment methods we offer, filling in details like credit card information, billing address, etc. before clicking pay and then asserting that an appropriate message is displayed such as "Payment successful" or an appropriate error message. Unfortunately we can't just test the content within the iframe directly as the payment modal is designed to be embedded into a page, and it communicates necessary information between the page containing the iframe and the page within the iframe. We can use the workaround posted above, however as we're aiming to replace an already functional test system it makes it harder to justify using the workaround when it greatly reduces the benefits of using cypress. Let me know if there's anymore information you need. |
We would like to have this feature in cypress, because we are using CKEditor for wysiwyg input in our application, and it uses an iframe. |
+1 We are also integrating external (cross-domain) payment methods and would like to test. |
+1 We are also integrating external (cross-domain) payment methods |
People writing about payment methods, so do i. I'm trying to pass Stripe checkout iframe
cypress.json
{
"chromeWebSecurity": false
} .get('iframe.stripe_checkout_app').should('exist')
.then(function ($iframe) {
const iframe = $iframe.contents()
const cardNumInput = iframe.find('input:eq(0)')
const cardValidInput = iframe.find('input:eq(1)')
const cvcInput = iframe.find('input:eq(2)')
cardNumInput.val('4242424242424242')
cardValidInput.val('1222')
cvcInput.val('123')
setTimeout(() => {
iframe.find('button').click()
}, 1000)
return null
}) Anyone knows how to resolve this? |
EDITED: to show using proper Cypress commands. .get('iframe.stripe_checkout_app')
.then(function ($iframe) {
const $body = $iframe.contents().find('body')
cy
.wrap($body)
.find('input:eq(0)')
.type('4242424242424242')
cy
.wrap($body)
.find('input:eq(1)')
.type('1222')
cy
.wrap($body)
.find('input:eq(2)')
.type('123')
}) |
We use Iframe to insert user made forms into a webpage. We have to look at whats entered in the fields. The forms don't even show up in the cypress browser. They are just replaced with "<iframe> placeholder for". It hows up fine in a plain chrome browser though. |
Are you still planning to add support for this later? |
We don't show content in iframes when reverting to a snapshot (nor will we ever do that). However we will eventually support targeting DOM elements inside of iframes. |
This would be great for us too, we are using CKeditor quite a bit (in an iFrame) |
I'm also very interested in the ability to target elements in an iframe (same domain). This means that the root frame is only responsible for starting the inner frame application This works great for our unit and integration testing using karma. |
We are much closer to having this work. Here in We'll still need to build in API's that enable you to switch the document context to the iframe, so you can use the querying methods of Cypress the same as you do on the outer document. Also things like verifying actionability still need work - notice I need to pass Nevertheless its a big step forward because you can at least now fill out forms and interact with iframe elements, which prior to |
So this does work, but I find I have to add a |
No, this is all part of the complexity of support iframes. Essentially all of the same safeguards we've added to the main frame have to be replicated on iframes. We do a ton of work to ensure The additional complexity is that we can't add those listeners until you've told us you want to "switch" into those frames. |
You'd likely need to write a custom command that takes this into account. It checks |
ok that was the direction I was headed 👍 |
This is an option typing in an input field situated in the iframe: cy.get(iframe_selector).then($iframe => {
const iframe = $iframe.contents();
const myInput = iframe.find("your input selector like #myElement");
cy.wrap(myInput).type("example");
//you don't need to trigger events like keyup or change
}); |
This is what I ended up doing. Seems to work rather well. You can alias the Cypress.Commands.add('iframe', { prevSubject: 'element' }, $iframe => {
return new Cypress.Promise(resolve => {
$iframe.on('load', () => {
resolve($iframe.contents().find('body'));
});
});
}); // for <iframe id="foo" src="bar.html"></iframe>
cy.get('#foo').iframe().find('.bar').should('contain', 'Success!'); |
Hello Everyone,
Correct me if i missed any thing here. Could cypress team gives any expected timeline if this feature is going to be available or any current update would be very helpful. Thanks, |
I found a solution that works without the need to set chromeWebSecurity to false. I created this custom command:
In the beforeEach of the test, I use:
And in the test code, I use:
It's a simple solution and didn't cause any issues using chromeWebSecurity set to true, but I'm still in favor of Cypress helping us address this problem; it will greatly assist us. |
Thanks @MarcosPereira1 for the input. Let me try that, did you tested above code against cross-origin iframe? in test: But this will not work for cross-origin iframes. Only work for same origin frames. |
I did tried other workaround to establish communication flow between parent document and iframe document. Please find below sample code to understand more: Button clicked successfully!
<script>
const messageDiv = document.getElementById("message");
document.getElementById("send-message-btn").addEventListener("click", () => {
const message = { type: "dataUpdate", data: "Hello from iframe!" };
parent.postMessage(message, "http://127.0.0.1:3000/index.html");
Index.html: <script> // Listen for messages from the iframe window.addEventListener("message", (event) => { // Make sure the message is from a trusted source if (event.origin !== "http://127.0.0.1:3002/IframeTest/iframe-content.html") return;
XHR in iframe<iframe src="iframe-content.html" data-cy="the-frame"></iframe>Cypress Test: Steps to do that:
This is just a example and depends on how we are embedding cross-origin iframe. This looks safe way to deal with iframe but it may increase complexity in frontend as we need to main these logics on both segment of documents. In case if embedded iframe(iframe-content.html in our case) don't support(which most of the case won't as long it's not open source or same team managing) then it become show stopper for cypress users. |
@jennifer-shehane could you please give an update on this feature request? What does removing the "stage: backlog" label mean? Ist currently being implemented or is it kicked out from the potential features list (aka backlog)? Any response is highly appreciated. |
We're not currently working on this. We suggest using one of the workarounds to test iframes within Cypress for the time being. |
Thank you for confirming. I'm relieved to know you're not actively working on this, which means I'll need to explore alternative tools or workarounds. In the meantime, could you address the issue where Cypress crashes upon clicking a "redirect button" within an iFrame, intended to close the iFrame and navigate, for example, to the homepage of the site hosting the iFrame? |
This is extremely disappointing. Our whole code base is filled with RegExes on HTML sources because we don't get a proper retry inside iframes and we are fighting flaky tests because of this. There is nothing that works. I read everything here and tried all the "work-arounds". Maybe we are in a niche by using iframes for microfrontends and the Cypress team does not want to work on this. At least we got confirmation that is it not worth hoping and waiting. It will be very difficult to migrate thousands of lines to a different testing framework but I see no other option. |
We also use some "bigger" microfrontends but we just draw a line at how were testing our site. Even if iframes were very well supported, we would not use it extensively. We integrate 3rd-party frontends and they themselves are tested in isolation. So for the integration of the iframes we use some really basic tests and for extended tests we do it manually. If a site makes heavy use of iframes I'd simply consider another testing strategy. Or you can prevent a huge migration by adding Playwright solely for iframe-heavy tests to start with? |
Yes, we are having two different Cypress tests already. One project for the app that runs inside IFrames based on backend mocks and one project that does actual system tests with real backends and iframes. The latter is where we are having issues. We are trying to put as much as possible in the mocked tests but still, we need to properly test everything that does frontend / backend interaction again. We end up in a couple of hundreds of test cases. |
well, time to switch to another framework I guess, because this is blocking me from E2E my project. why won't you fix this?? |
Unfortunate that it's not going to be handled natively however at least I finally found a solution that worked for my tests after going through a number of workarounds that had been mentioned.
|
I think this has been explained here already before, but doing So you will get a lot of flaky tests with this „work-around“. The work-around for the work-around is to do RegEx checks on the raw HTML before calling So there is nothing that works with all the benefits Cypress usually offers, especially the automatic retry. |
What about the following approach? cy
.get('#IFRAMELOCATOR')
.its('0.contentDocument.body')
.should('not.be.empty')
// eslint-disable-next-line @typescript-eslint/unbound-method
.then(cy.wrap)
.find('#ELEMENTINIFRAME')
.should("be.visible") |
I was having issues targeting a third-party iframe and this method worked for me 🙌 |
It does exactly what it says. It checks that the content is not empty. But that does not help. If you have dynamic application, things are added to the DOM after the app is loaded, e.g. when backend requests are done. Long story short: It does not help for the problem I described. It just prevents the iFrame being completely empty. |
I think you could try combining the iframe helper and a plugin like: Also this is a great resource for finer details of above snippets: https://www.cypress.io/blog/2020/02/12/working-with-iframes-in-cypress |
I read that article already a couple of times and it’s eventually also leading to the same suggestion as mentioned before. I didn’t know about the recurse plugin and it might be useful in some situations. But we have hundreds of Dom queries, adding recurse everywhere would lead hardly readable code. I think our RegEx test is the least bad of all the bad work-arounds. |
@jwedel can you give me a small example of what is not loading or hard to check inside an iframe? |
Generally everything that’s dynamically loading. We have a big Angular app running inside an iFrame. There is lots of elements being added and removed based on user interaction. Now let’s say you click on a button which loads something from the backend that gets rendered later. Then you’ll do the get->its->find chain, find will timeout after 4 seconds (in fact you can use whatever timeout you want) when the element was not there before. Also using intercept does not work reliably as it does not guarantee that the resulting elements are actually in the DOM already. Our solution looks inside the raw HTML for the element using a RegEx which is automatically retried by Cypress. Once the RegEx succeeds, we can use find as we know the element is there. But this is all very ugly and still fragile and you cannot use jQuery selectors. |
@jwedel that is too fuzzy for me to help. I have created a simple iframe example showing flake due to loading and how I solve it, see https://glebbahmutov.com/blog/cypress-flaky-tests-exercises/ but if anyone has a realistic app they struggle with (and they can share publicly), I can take a look at it |
I watched your video. The last example is exactly the problem I was referring to. Where you use as we have a LOT of dynamic elements, we generalized it as a Cypress command that takes an optional RegEx where we could pass something like this works but it’s annoying and clutters the code with unnecessary duplications as you want to add another so we start without that and when it breaks or gets flaky, we’ll add the RegEx. it works but it feels like using assembler when you actually want to use some proper high level language. |
I still don’t understand. Why not grab the document and wrap it and use normal cypress commands?Sent from my iPhoneOn Mar 26, 2024, at 15:59, Jan Wedel ***@***.***> wrote:
@jwedel that is too fuzzy for me to help. I have created a simple iframe example showing flake due to loading and how I solve it, see https://glebbahmutov.com/blog/cypress-flaky-tests-exercises/ but if anyone has a realistic app they struggle with (and they can share publicly), I can take a look at it
I watched your video. The last example is exactly the problem I was referring to.
Where you use should.not.include we use a RegEx. We want Cypress to retry until a certain element is existing in the DOM.
as we have a LOT of dynamic elements, we generalized it as a Cypress command that takes an optional RegEx where we could pass something like new RegEx(/data-test=“my-elem”/).
this works but it’s annoying and clutters the code with unnecessary duplications as you want to add another find with the same element.
so we start without that and when it breaks or gets flaky, we’ll add the RegEx.
it works but it feels like using assembler when you actually want to use some proper high level language.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
We are doing that. As I said, we use almost the same code as you showed in the video. And as you already showed perfectly in the same video, any find after the wrap->then will not retry (actually Cypess does retry, but the underlying wrapped document snapshot does not change). So to retry for an element that takes time to appear in the DOM, we have to retry before the wrap happens. |
Updated Feb 9, 2021 - see note below or #136 (comment)
Currently the Test Runner does not support selecting or accessing elements from within an iframe.
What users want
Users of Cypress need to access elements in an
<iframe>
and additionally access an API to "switch into" and switch back out of different iframes. Currently the Test Runner thinks the element has been detached from the DOM (because itsparent
document is not the expected one).What we need to do
Add new
cy
commands to switch into iframes and then also switch back to the "main" frame.Cypress must inject itself into iframes so things like XHR's work just like the main frame. This will ideally use something like
Mutation Observers
to be notified when new iframes are being pushed into the DOM.Add API to navigate between frames
Update the Driver to take into account element document references to known frames
Things to consider
<iframe>
.{ chromeWebSecurity: false }
(Chromium-based browsers only).Examples of how we could do this
Workaround
It's possible to run
cy.*
commands on iframe elements like below:Updates as of Feb 9, 2021
Pasting some snippets from our technical brief on iframe support that we are currently planning. As always, things can change as we move forward with implementation, but this is what we are currently planning.
If there's any feedback/criticism on these specific proposed APIs, we'd be happy to hear it.
.switchToFrame([...args], callback)
Switches into an iframe and evals callback in the iframe. Doesn’t matter whether the iframe is same-origin or cross-origin.
Stripe payment example
Same-origin iframe
Example where a site uses a same-domain iframe as a date-picker widget
We also intend to support snapshots of iframes.
The text was updated successfully, but these errors were encountered: