3

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

Phaki
  • 210
  • 4
  • 18

4 Answers4

3

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

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)
   })
})
TesterDick
  • 3,830
  • 5
  • 18
  • Thank you @TesterDick your answer was the closest to solve my problem. It does not solved 100% because when I map through the hrefs I have to store them in an array with cy.task('setHrefs', href) then I can get the hrefs also with cy.task('getHrefs) and visit them one-by-one and it works. – Phaki Oct 03 '22 at 09:58
0

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.

Darko Riđić
  • 459
  • 3
  • 18
-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)
             })   
           })
        })
Priya
  • 1
  • 1
  • I tried it. Had to add .should() after the last row: ```cy.findByText(/report herunterladen/i).should('exist')```. It throws an error that the element is not visible. Without the should('exist') it works but then it doesn't check that the element is rendered. – Phaki Sep 20 '22 at 08:47
  • Make sure that there is only one result with text matching 'report herunterladen' in the dom. cypress may be interacting with the hidden element with the matching text 'report herunterladen'. – Priya Sep 20 '22 at 09:28
  • There is no result unfortunately. If there would be more results then it would throw an error because of the multiple same elements. – Phaki Sep 20 '22 at 19:20