question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Odd behavior with `cy.get` with promises and jQuery DOM elements

See original GitHub issue
  • Operating System: Windows 10
  • Cypress Version: 2.1.0 Beta
  • Browser Version: Chrome 64

Is this a Feature or Bug?

I assume it is a bug? Unless my understanding of JS promises completely fails me.

Current behavior:

I’m trying to use cypress and have run into an issue which I could not explain. I’m using cy.get().contains() and working on the result with chained promises (using a simple .then()), e.g.

cy.get('some selector').contains('some text').then(el => console.log(el));

So far so good, the element gets printed to the console, or rather the jQuery wrapper around it. Now I need to access the ownerDocument property of the element. Since this is a jQuery wrapper around a single element, I need to access its first element through array subscript 0 (even though the Cypress docs state that contains() returns a single DOM element – ».contains() yields the new DOM element it found.«):

cy.get('some selector').contains('some text').then(el => console.log(el[0].ownerDocument));

Still dandy (el.ownerDocument would not work).

But since we are all striving to have reusable and composable code it would make perfect sense to move the array access out of the actual logic:

cy.get('some selector').contains('some text')
  .then(els => els[0])
  .then(el => console.log(el.ownerDocument));

But this does not work! el.ownerDocument is undefined (el[0].ownerDocument is defined though). Logging el alone (console.log(el)) will log the jQuery wrapper, not the DOM element itself as I would expect. What gives? Why is this behaving so strangely? It looks like the result of my first then is ignored (if I comment the line, I get no change in behavior).

Desired behavior:

Results from promises should be fed as input into chained promises.

How to reproduce:

Test code:

    cy.get('some selector').contains('some text')
      .then(els => els[0])
      .then(el => {
        console.log(el);
        console.log(el[0]);
      });

edited replaced TS code with JS code

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8 (4 by maintainers)

github_iconTop GitHub Comments

2reactions
kucebcommented, Jan 24, 2019

As for cypress yielding a jquery instance after a .eq(..) this is normal and inline with how the rest of our apis work in relation to jquery. (in jquery, .eq(0) still returns you a jquery instance).

@efdknittlfrank @raduflp however, you are rightfully confused about chaining .then’s resulting in the subject being re-wrapped. This is unfortunately expected behavior, albiet not optimal for composability and predictability for users looking to extend cypress.

Basically, a cypress .then is just like a promise, but checks the subject right before yielding it to you, and if it’s a dom element, it wraps it in jquery

the reason we do this is so that users can use cy.wrap with regular dom elements, and cypress commands, expecting a jquery instance, can use them

this will likely change in a future major version of cypress, as we understand this is confusing behavior

for now, you’ll have to do what @efdknittlfrank mentioned above

   cy.get('some selector').contains('some text')
      .then(els => els[0]) // <--- this effectively does nothing
      .then(el => {
        console.log(el); // jquery
        console.log(el[0]); // dom element
      });

or you could try the following, that will use a real promise:

   cy.get('some selector').contains('some text')
      .then($el => Cypress.Promise.resolve($el)
		.then($el => $el[0])
		.then(el => {
			console.log(el) // dom element
			return el.ownerDocument
		})
	  )
	  .then(console.log) // logs the document

tl;dr: Basically, a cypress .then is just like a promise, but checks the subject right before yielding it to you, and if it’s a dom element, it wraps it in jquery

0reactions
raduflpcommented, Jan 30, 2019

@efdknittlfrank did you ever managed to find a solution?

@Bkucera it doesn’t look like its related to TS or a specific selectors. It looks like regardless of what it is returned from eq, first, then etc, it will always wrap the returned value in jQuery before passing it on.

I set up a new project with this minimal test to confirm the behavior

it("test get eq yields", () => {
    cy.visit('https://example.cypress.io/commands/actions')
    cy.get("a").eq(0).should(el => {
      console.log(el) // jQuery wrapper
      console.log(el[0]) // DOM element
    })
    cy.get("a").eq(0).then(el => {
      console.log(el) // jQuery wrapper
      return el[0] // return DOM element
    }).then(el2 => {
      console.log("then2");
      console.log(el2) // jQuery wrapper
      console.log(el2[0]) // DOM element
    })
    cy.get("a").first().then(el => {
      console.log(el) // jQuery wrapper
    })
  })

so basically even if you chain eq(0) later you still have to do el[0].

In case of eq this behavior is consistent with the TS typing : Chainable<JQuery<E>> but it is not consistent with the docs

.eq() yields the new DOM element(s) it found.

could you confirm that this is the expected behavior?

Read more comments on GitHub >

github_iconTop Results From Across the Web

How does jQuery and Cypress access DOM elements with ...
As we know that Cypress uses the get() method to search for a web element in DOM and jQuery uses the $() method...
Read more >
eq | Cypress Documentation
Get A DOM element at a specific index in an array of elements. The querying behavior of this command matches exactly how .eq()...
Read more >
cy.each examples | Cypress examples (v9.7.0) - Gleb Bahmutov
cy.each can iterate over any items from a list / jQuery object ... greaterThan', 2) .each(($price, k) => { // get the text...
Read more >
Resolve promises one after another (i.e. in sequence)?
Is there a simpler way that this code can be re-written so that I don't have to use my weird readSequential function? Originally...
Read more >
Promise - JavaScript - MDN Web Docs
The Promise object represents the eventual completion (or failure) of an asynchronous operation and its resulting value.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found