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

cy.get().contains() does not accept new DOM elements #2640

Closed
aayvazyan-tgm opened this issue Oct 23, 2018 · 19 comments
Closed

cy.get().contains() does not accept new DOM elements #2640

aayvazyan-tgm opened this issue Oct 23, 2018 · 19 comments
Labels
stage: proposal 💡 No work has been done of this issue type: unexpected behavior User expected result, but got another

Comments

@aayvazyan-tgm
Copy link

aayvazyan-tgm commented Oct 23, 2018

Current behavior:

  • When cy.get().contains() is called, .contains() will only wait for the DOM elements that existed during cy.get(). Newly created DOM elements will not be checked against .contains().
  • .get().contains() works fine in when there is only one result for the .get(). But as soon as there is a list of generated items, .get().contains() is no longer working as previously and race conditions start to occur. The reason is not obvious but this is a reason for tests to become flaky over time.

Desired behavior:

  • cy.get() should accept new DOM elements while .contains() has not finished.
    I expect it to work like cy.get().should('contain', '...');.

  • Also the documentation misses the hint of this significant behavior difference.

Steps to reproduce:

HTML

<div class="myDiv">Div1</div>
<div class="myDiv">Div2</div>
<div class="replaceMe">Div3</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
    setTimeout(() => {
        $("div.replaceMe").replaceWith('<div class="myDiv">Expectation</div>');
    }, 4000);
</script>

Cypress Test

describe('test', () => {
    it('should work', () => {
        cy.visit('http://localhost:8080/cy.html');
        cy.get('.myDiv').contains('Expectation');
    });
});

Result:
image

@davidzambrana
Copy link

I read some time ago a post concerning this, and the author encouraged the use of cy.contains('element', 'text') for cases like yours.

Can you try with cy.contains('.myDiv','Expectation')?

@aayvazyan-tgm
Copy link
Author

aayvazyan-tgm commented Oct 23, 2018

cy.contains('.myDiv','Expectation') works as expected and succeeds.

I still see the current behavior as an issue, especially because .get().contains() works fine in most cases where there is only one result for the .get()

As soon as there is a list of generated items, .get().contains() is no longer working as expected and race conditions occur. Also the reason is not obvious.

@davidzambrana
Copy link

Yep I know, I got to know it through this post... Maybe it is good that this behavior gets documented somewhere in the Docs.

@Konstruktour
Copy link

Konstruktour commented Oct 23, 2018

for me it is quiet confusing that it works with .should but not with .contains ...

think the cypress docu is not right.. if u take a look at the .should command, it is described as it works "will continue to retry its specified assertions until it times out" ( https://docs.cypress.io/api/commands/should.html#Timeouts ) ...

also .contains is described simular ".contains() can time out waiting for the element(s) to exist in the DOM." (https://docs.cypress.io/api/commands/contains.html#Timeouts) which works as expected, when it is NOT chained to other commands.. which is imo very critical...

@egucciar
Copy link
Contributor

egucciar commented Oct 29, 2018

That's how .contains (and by that coin .find, .eq, and .its) work though. It's not the most obvious detail in the docs but once you run into this gotcha once it makes sense I think.

@pixelscript
Copy link

I'm coming across this problem a lot in a SPA when the data isn't ready yet. It means we can't use .eq reliably and have to resort to using :nth-child selectors.

@jennifer-shehane jennifer-shehane added stage: proposal 💡 No work has been done of this issue type: unexpected behavior User expected result, but got another difficulty: 3️⃣ labels Dec 28, 2018
@lukeapage
Copy link
Contributor

Just came across this - very weird how it doesn't retry with get->contains, but it does if you use contains on its own or get->should contain

@tomaszzielinski
Copy link

Just for the record - this behavior is documented here: https://docs.cypress.io/guides/core-concepts/retry-ability.html

@aayvazyan-tgm
Copy link
Author

aayvazyan-tgm commented Sep 27, 2021

This is a major reason for flaky tests as not every developer is fully aware of this behavior.
Is there an update on this issue? @jennifer-shehane

@haveaguess
Copy link

Just for the record - this behavior is documented here: https://docs.cypress.io/guides/core-concepts/retry-ability.html

But it says "Cypress only retries commands that query the DOM: cy.get()... " and yet we are all here expecting it to retry in the timeout window and it doesn't

@bahmutov
Copy link
Contributor

@haveaguess Cypress retries the last command with its assertions, not the entire chain. The Retry-ability guide talks a lot about how to merge commands or add an assertion in between to ensure each step finishes when you want it to.

@haveaguess
Copy link

haveaguess commented Sep 28, 2021

Thank you, you're absolutely right, and it's an interesting read:

https://docs.cypress.io/guides/core-concepts/retry-ability#Only-the-last-command-is-retried

@chriswait
Copy link

chriswait commented Sep 25, 2022

The usage docs for contains chains contains() onto a get()

Correct Usage
cy.get('.nav').contains('About') // Yield el in .nav containing 'About'

It's quite confusing for the documentation to suggest writing tests in a way that will become flakey for non-obvious reasons. I say non-obvious because Cypress's own documentation obviously anticipates the "retry last command only" behaviour confusing developers, saying things like "weird", "why isn't Cypress finding it? What is going on?", and "for a variety of implementation reasons"..

Additionally, other parts of the docs explicitly suggest not using contains as per the usage example above:

Tip: instead of ... cy.get(selector).contains(text) chain, we recommend using cy.contains(selector, text) which is retried automatically as a single command.

Would you be interested in a pull request that updates the documentation for contains(), such that:

  1. The usage example uses cy.contains(selector, text)

and/or

  1. There is a note that .get().contains() may cause flakiness (linking to the explanation that only the last command is retried).

Apologies if I've missed something obvious in the docs that makes this clear, but I think it should be mentioned on the contains page.

@feifei0914kwc
Copy link

I ran into similar situation and my solution is to set a buffer to check the amount of the element first and then use cy.get().contains() to get the element that I actually need.

cy.get(selector).should("have.length", 5) cy.get(selector).contains("target").click()

cy.get(selector).should("have.length", 5)will make sure that I got the correct amount of elements that I'm expecting and then I can get the element that contains "target" string.

@objarni
Copy link

objarni commented Oct 25, 2022

This struck me today. Took 2h's of reading docs, then finding this thread, to get it working. I consider this a weak spot of cypress, not being clear in documented behaviour being the most severe issue, the actual behaviour too being non-intuitive.

Expected behaviour (unless very clearly documented): .get should re-query the DOM on every retry, regardless of how many chained calls are postpended to the get call.

@andrii-lundiak
Copy link

andrii-lundiak commented Jan 5, 2023

In case if someone wondering, Jan-2023 :) issue still open, so I put a my "5 cents".
Cypress 9.7.0, and my e2e tests .

  • I started facing issues with .contains('My Text').should('be.visible') or even .contains('My Text') or even .get('.selector').contains('My text') and it failed. I then changed to .contains('.my-element', 'My Text') and it surprisingly works OK.
  • I have to say, that in many other places I still have regular .contains('My Other Text) and it works fine. But sometimes it also FAILs.
  • I also can state, that many of existed code with .get('.selector').contains('My text') ALSO worked and still works.
  • I also can state that this weird behavior is nearby when I use cy.visit() and it also fails EVEN when I use cy.visit().then(() => { HERE .contains()})
  • And the element I am searching (SPAN inside of A which is inside of DIV 99.99% exists in DOM - I clearly see it). But Cypress doesn't:

image

Yeah, maybe it's bad because of .should('be.visible'). I removed it it and relied ONLY on get/contains.

Yeah. maybe it's related with cy.visit(). But .contains() was OK (with no such weird behavior) with [email protected]

I dunno. Going to upgrade to v10, maybe there is regression bug (introduced by v9) fixed. I'm on my way to upgrade to Cypress v12... but it's hard journey to do when you are still on v6.

@karlhorky
Copy link
Contributor

karlhorky commented Jan 5, 2023

In our experience, using the cy.contains('.my-element', 'My Text') form is best:

  1. Most robust (does not fail like the other forms)
  2. Shorter code
  3. Has not produced any downsides for us

@BlueWinds
Copy link
Contributor

This should be improved or resolved completely in Cypress 12 with the introduction of queries and major changes in the way Cypress retries commands.

I'm going to close this as resolved; if you're still experiencing similar issues in Cy 12+, please open a new issue or leave a comment here telling me what I've missed!

@aayvazyan-tgm
Copy link
Author

aayvazyan-tgm commented Mar 2, 2023

@BlueWinds can you elaborate on how this should be fixed in cypress 12+? the changelog does not mention any related changes to the existing selector handling and it does not mention any new query api.
https://docs.cypress.io/guides/core-concepts/retry-ability has been reworked but misses the problem that I mentioned

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
stage: proposal 💡 No work has been done of this issue type: unexpected behavior User expected result, but got another
Projects
None yet
Development

No branches or pull requests