Skip to content
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

Open
2 tasks
brian-mann opened this issue May 11, 2016 · 472 comments
Open
2 tasks

Iframe support #136

brian-mann opened this issue May 11, 2016 · 472 comments
Assignees
Labels
Epic Requires breaking up into smaller issues existing workaround pkg/driver This is due to an issue in the packages/driver directory topic: iframes type: feature New feature that does not currently exist

Comments

@brian-mann
Copy link
Member

brian-mann commented May 11, 2016

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 its parent 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

  • How will we handle snapshotting? Currently we don't take snapshots for anything inside of an <iframe>.
  • How will we handle cross origin frames? It's possible to enable these with { chromeWebSecurity: false } (Chromium-based browsers only).
  • How will we show context switching in the Command Log? It should probably look / be colored differently than the 'normal' main commands

Examples of how we could do this

// switch to an iframe subject
cy
  .get('#iframe-foo').switchToIframe() 
  .get('#button').click() // executed in <iframe id='iframe-foo' />

// or pass in $iframe object in hand
cy
  .get('#iframe-foo').then(($iframe) => {
    cy.switchToIframe($iframe)
    cy.get('#button').click()
  })

// now switch back to the main frame
cy
  .switchToMain()
  .get(':checkbox').check() // issued on the main frame

Workaround

It's possible to run cy.* commands on iframe elements like below:

cy.get('iframe')
  .then(($iframe) => {
    const $body = $iframe.contents().find('body')

    cy.wrap($body)
      .find('input')
      .type('[email protected]')
})

⚠️ Updates

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

// ❗️ This is planned work and does not currently work
cy.visit('someshop.com')
// ... add stuff to cart
// ... get to payment page
cy.get('iframe').switchToFrame(() => {
  cy.get('#name').type('name')
  cy.get('#number').type('1234-5678...')
  cy.contains('Pay').click()
})
// go on with cypress commands in the main frame
cy.contains('Thanks for your order')

Same-origin iframe

Example where a site uses a same-domain iframe as a date-picker widget

// ❗️ This is planned work and does not currently work
cy.visit('https://date-picker.com')
cy.get('iframe').switchToFrame(() => {
  cy.get('.next-month').click()
  cy.contains('24').click()
})
// switch out of iframe context because callback is finished
cy.get('.date').should('have.text', '2/24/2021')

We also intend to support snapshots of iframes.

@brian-mann brian-mann added the type: feature New feature that does not currently exist label May 11, 2016
@Courey
Copy link

Courey commented May 31, 2016

commenting that I care because the docs said I should and we need this functionality for full test coverage.

@acusti
Copy link

acusti commented Jul 21, 2016

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.

@brian-mann
Copy link
Member Author

Is the iframe same domain or cross domain?

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.

@acusti
Copy link

acusti commented Jul 22, 2016

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!)

@maximilianschmid
Copy link

same domain iframe would allow us to test emails via e.g. mailinator.com

@brian-mann
Copy link
Member Author

Cypress actually injects to forcibly enable it to access same domain <iframes> and even sub-domain <iframes> but there is an artificial limitation in the driver code where it get's confused when elements are returned and they're not bound to the top frame of your application.

This is coming up on our radar and it will introduce a few more API's to enable switching in and out of <iframes>.

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
})

@harz87
Copy link

harz87 commented Oct 14, 2016

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.

@brian-mann
Copy link
Member Author

@harz87 the braintree iframes you're referring to are cross origin frames right?

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.

@rbone
Copy link

rbone commented Feb 6, 2017

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:

screen shot 2017-02-06 at 2 41 03 pm

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.

@oliver3
Copy link

oliver3 commented Feb 14, 2017

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.

@afohlmeister
Copy link

+1 We are also integrating external (cross-domain) payment methods and would like to test.

@jennifer-shehane jennifer-shehane added the pkg/driver This is due to an issue in the packages/driver directory label Mar 24, 2017
@mvandebunt
Copy link

+1 We are also integrating external (cross-domain) payment methods

@Alex0007
Copy link

Alex0007 commented Jun 16, 2017

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
})

image

But after click:
image

Manually filled form looks formatted
image

Anyone knows how to resolve this?

@brian-mann
Copy link
Member Author

brian-mann commented Jun 16, 2017

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')
})

@caerie4
Copy link

caerie4 commented Jun 22, 2017

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.

@caerie4
Copy link

caerie4 commented Jun 22, 2017

Are you still planning to add support for this later?

@brian-mann
Copy link
Member Author

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.

@AshMcConnell
Copy link

This would be great for us too, we are using CKeditor quite a bit (in an iFrame)

@bd82
Copy link

bd82 commented Aug 27, 2017

I'm also very interested in the ability to target elements in an iframe (same domain).
The group I'm part of are building a Web IDE and during testing the whole application "instance" runs in an iframe for isolation purposes.

This means that the root frame is only responsible for starting the inner frame application
and then issuing commands.

This works great for our unit and integration testing using karma.
I would very like to explore cypress as an alternative for our flaky E2E selenium tests.
But without iframe targeting 95% of our use case becomes irrelevant.

@brian-mann
Copy link
Member Author

brian-mann commented Sep 8, 2017

We are much closer to having this work. Here in 0.20.0 you are able to wrap <iframe> elements and at least use Cypress commands on them.

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 { force: true } to get the click to work.

Nevertheless its a big step forward because you can at least now fill out forms and interact with iframe elements, which prior to 0.20.0 did not work at all.

screen shot 2017-09-08 at 9 14 41 am

screen shot 2017-09-08 at 9 14 28 am

@paulfalgout
Copy link
Contributor

So this does work, but I find I have to add a wait(5000) prior to the then otherwise getting the iframe contents will be the about:blank while the frame is loading. since the iframe request is not XHR I suspect there's no way to route it with a wait alias? any other suggestions than an arbitrary wait time?

@brian-mann
Copy link
Member Author

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 document and window are current - we listen for load events and unload events to pause and resume command execution - all of that has to be implemented for frames.

The additional complexity is that we can't add those listeners until you've told us you want to "switch" into those frames.

@brian-mann
Copy link
Member Author

You'd likely need to write a custom command that takes this into account. It checks document and polls it until its ready

@paulfalgout
Copy link
Contributor

ok that was the direction I was headed 👍

@ioan-ghisoi
Copy link

ioan-ghisoi commented Nov 3, 2017

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

});

@paulfalgout
Copy link
Contributor

This is what I ended up doing. Seems to work rather well. You can alias the iframe() method, but if anything in the iframe loads another URL you have to do the original get().iframe() again.

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!');

@jennifer-shehane jennifer-shehane added the stage: needs investigating Someone from Cypress needs to look at this label Nov 7, 2017
@ali0311
Copy link

ali0311 commented Aug 29, 2023

Hello Everyone,
Seems like lots of things going on. Latest update regarding this issue is cypress doesn't support cross-origin iframe handling which is definitely deal breaker. Now as per doc, suggested item are:

  1. Make chromeWebSecurity as false - With this i am able to interact with elements inside iframe. However, this is not how we should mimic user/live testing. It's a security risk.
  2. Windows.postMessage() - This need to be done at source code level first then only we can deal it from cypress test which is also not feasible/realistic.

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,
Ali

@MarcosPereira1
Copy link

I found a solution that works without the need to set chromeWebSecurity to false.

I created this custom command:

Cypress.Commands.add('selectIframeByAlias', alias => { cy.get([title="${alias}"]).its('0.contentDocument').its('body').as('Iframe'); });

In the beforeEach of the test, I use:

cy.selectIframeByAlias('iframe selector name');

And in the test code, I use:

cy.get('@Iframe') .find('selector inside the iframe').click();

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.

@ali0311
Copy link

ali0311 commented Aug 30, 2023

Thanks @MarcosPereira1 for the input. Let me try that, did you tested above code against cross-origin iframe?
By the way i have tried similar logic:
Cypress.Commands.add('switchToIframe', (iframe) => {
return cy.get(iframe)
.its('0.contentDocument.body').should('not.be.empty')
.then(cy.wrap)
});

in test:
cy.switchToIframe('iframe[data-cy="the-frame"]').find('#run-button').should('have.text', 'Try it').click()

But this will not work for cross-origin iframes. Only work for same origin frames.
Cheers!

@ali0311
Copy link

ali0311 commented Aug 30, 2023

I did tried other workaround to establish communication flow between parent document and iframe document. Please find below sample code to understand more:
iframe-content.html:

<title>Embedded Content</title> <style> body { font-family: Arial, sans-serif; text-align: center; padding-top: 50px; } #message { display: none; color: green; } </style> Send Message
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");
  // Display the success message
  messageDiv.style.display = "block";
});
</script>

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;
  // Handle the message data here
  console.log("Received message from iframe:", event.data);
});
</script>

XHR in iframe

<iframe src="iframe-content.html" data-cy="the-frame"></iframe>

Cypress Test:
cy.switchToIframe('[data-cy="the-frame"]').find('#send-message-btn').should('have.text', 'Send Message').click()

Steps to do that:

  1. Host iframe-content.html and Index.html
  2. In iframe-content.html(child/iframe document) we are safely establish communicating with parent document by specifying its origin(index.html).
    This is my parent document: http://127.0.0.1:3000/index.html where i embedded iframe.
  3. In parent document(Index.html) i need to specify where we sending event and that will become my receiver origin.
  4. Once done, use normal iframe logic(as mentioned: Iframe support #136 (comment)) to deal with element.

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.
This is the feasible solution i'd like to submit, @brian-mann /Cypress Team: Please review and share your thoughts if any!
Thanks,
Ali

@jwedel
Copy link

jwedel commented Dec 6, 2023

@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.

@jennifer-shehane
Copy link
Member

We're not currently working on this. We suggest using one of the workarounds to test iframes within Cypress for the time being.

@DobQA
Copy link

DobQA commented Mar 7, 2024

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?

@jwedel
Copy link

jwedel commented Mar 19, 2024

We're not currently working on this. We suggest using one of the workarounds to test iframes within Cypress for the time being.

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.

@mirobo
Copy link
Contributor

mirobo commented Mar 19, 2024

We're not currently working on this. We suggest using one of the workarounds to test iframes within Cypress for the time being.

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?

@jwedel
Copy link

jwedel commented Mar 19, 2024

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.

@bikmax
Copy link

bikmax commented Mar 25, 2024

We're not currently working on this. We suggest using one of the workarounds to test iframes within Cypress for the time being.

well, time to switch to another framework I guess, because this is blocking me from E2E my project.

why won't you fix this??

@charliecook85
Copy link

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.
The 0.contentDocument.body solution from BrowserStack works for me.

cy.get('IFRAMELOCATOR')
      .its('0.contentDocument.body')
      .then((body) => {
        cy.wrap(body)
        .find("#ELEMENTINIFRAME")
        .should("be.visible")
      })

@jwedel
Copy link

jwedel commented Mar 25, 2024

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. The 0.contentDocument.body solution from BrowserStack works for me.

cy.get('IFRAMELOCATOR')
      .its('0.contentDocument.body')
      .then((body) => {
        cy.wrap(body)
        .find("#ELEMENTINIFRAME")
        .should("be.visible")
      })

I think this has been explained here already before, but doing its().then() will take a snapshot of the HTML at that time. So even if ELEMENTINIFRAME appears after 2 seconds and find() will retry for 4 seconds, the test will fail.

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 then() which in turn leads to horrible code.

So there is nothing that works with all the benefits Cypress usually offers, especially the automatic retry.

@sigerello
Copy link

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")

@tselmek
Copy link

tselmek commented Mar 25, 2024

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 🙌

@jwedel
Copy link

jwedel commented Mar 25, 2024

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")

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.

@samtsai
Copy link
Contributor

samtsai commented Mar 25, 2024

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")

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: cypress-recurse to achieve what you're looking for.

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

@jwedel
Copy link

jwedel commented Mar 25, 2024

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")

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: cypress-recurse to achieve what you're looking for.

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.

@bahmutov
Copy link
Contributor

@jwedel can you give me a small example of what is not loading or hard to check inside an iframe?

@jwedel
Copy link

jwedel commented Mar 25, 2024

@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.

@bahmutov
Copy link
Contributor

@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

@jwedel
Copy link

jwedel commented Mar 26, 2024

@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.

@bahmutov
Copy link
Contributor

bahmutov commented Mar 26, 2024 via email

@jwedel
Copy link

jwedel commented Mar 28, 2024

I still don’t understand. Why not grab the document and wrap it and use normal cypress commands?

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Epic Requires breaking up into smaller issues existing workaround pkg/driver This is due to an issue in the packages/driver directory topic: iframes type: feature New feature that does not currently exist
Projects
None yet
Development

No branches or pull requests