Phaki
Phaki

Reputation: 303

Waiting for DOM to load: Cypress

I try to make and E2E test with Cypress. I have to visit multiple links from a table and make an assertion after visiting the page.

In the Cypress UI I see the page is loaded, also if I check I can see the elements in the DOM. But my Cypress assertion not working, it says:

Timed out retrying after 4000ms: cy.should() failed because this element is detached from the DOM.

<div id="content" class="css-1ddoub">...</div>

Cypress requires elements be attached in the DOM to interact with them.

The previous command that ran was:

> cy.wait()

This DOM element likely became detached somewhere between the previous and current command.

My test code:

cy.get('[id="content"').parent().within(()=>{
      cy.get('[data-cy="list-item"]').each(item => {
        const link = item.find('a').first();

        cy.visit(link.attr('href')!)
        cy.wait(5000)
        cy.findByText(/report herunterladen/i)
   })
})

Element exists in the DOM: enter image description here

Interesting because If I hardcode the url and not use .each, then all assertions works fine after .visit().

cy.visit('/de/banks/compare/?a=calculations.average&b=ins.PL.PKO') // Hardcoded url
cy.wait(1000)
cy.findByText(/report herunterladen/i)

I tried to use cy.wait() even with a large number like 30000 but it didn't work.

For me it looks like Cypress has a bug with .each() method. It doesn't want to load pages DOM elements within the each function.

Cypress: 10.7.0

Upvotes: 4

Views: 3030

Answers (4)

K.A.Plilkington
K.A.Plilkington

Reputation: 171

It looks like the cy.visit() command is over-writing the links on the page.

You should issue a go-back to restore them, but also re-query the link elements inside the loop.

cy.get('[id="content"').parent().within(() => {
  cy.get('[data-cy="list-item"]')
    .each((_, index) => {
      cy.get('[data-cy="list-item"]').eq(index)   // re-query after page changes
        .find('a').first().then(link => {
          cy.visit(link.attr('href')!)
          cy.wait(5000)
          cy.findByText(/report herunterladen/i)
          cy.go('back')
        })
    })
})

Upvotes: 3

Darko Riđić
Darko Riđić

Reputation: 569

Detached error happens when you successfully saved(e.g. using cy.get()) a DOM element into memory, and tried to access it but the page has unfortunately removed that element meanwhile. To be precise, the page has probably re-rendered(re-created the same elements).

It is OK to put cy.wait(), but before you actually try to access the elements.

// Change this number depending on your needs
cy.wait(2000) // Let the page do it's thing

// Now try to access
cy.get('[id="content"').parent().within(()=>{
      cy.get('[data-cy="list-item"]').each(item => {
        const link = item.find('a').first();

        cy.visit(link.attr('href')!)
        cy.wait(5000)
        cy.findByText(/report herunterladen/i)
   })
})

The above should work, but you should review this issue on the Front-end and look deeper into the re-rendering cause.

Detached DOM element error is actually a JS error.

Upvotes: 0

TesterDick
TesterDick

Reputation: 10535

Please see this answer Traverse through a list....

To avoid detached error, use .each() on list of urls not list of elements

cy.get('[id="content"')
  .parent()
  .within(() => {
    cy.get('[data-cy="list-item"] a')
      .then($els => [...$els].map((a) => a.href))   
      .each(link => {
        cy.visit(link)
        cy.wait(5000)
        cy.findByText(/report herunterladen/i)
   })
})

Upvotes: 10

Priya
Priya

Reputation: 1

Please try this!

 cy.get('[id="content"').parent().within(()=>{
              cy.get('[data-cy="list-item"]').each(item => {
                
             cy.wrap(item).find('a').first().invoke('attr','href').then((href)=>{
                cy.visit(href)
                cy.wait(2000)
                cy.findByText(/report herunterladen/i)
             })   
           })
        })

Upvotes: -1

Related Questions