Element Detaches

Application detaches an element

Imagine the element is removed from the DOM after some time. We can confirm this happens by using the should satisfy assertion with the built-in Cypress predicate function Cypress.dom.isDetached

<div id="name">Santo</div>
<script>
  setTimeout(() => {
    document.getElementById('name').remove()
  }, 1000)
</script>
cy.contains('#name', 'Santo').should(
  'satisfy',
  Cypress.dom.isDetached,
)

There is also Cypress.dom.isAttached that we can use

cy.contains('#name', 'Santo').should(
  'not.satisfy',
  Cypress.dom.isAttached,
)

Tip: there are lots of small utility functions inside Cypress.dom object.

Cypress action leads to detached element

Let's say we want to click on a button, which will cause the element to be refreshed. The old element will be removed. How can we check it from our test?

<div id="main-section">
  <div id="name">Santo</div>
</div>
<button id="click">Click to process</button>
<script>
  document
    .getElementById('click')
    .addEventListener('click', () => {
      setTimeout(() => {
        document.getElementById('name').remove()
        document.getElementById('main-section').innerHTML =
          '<div id="name">Anna</div>'
      }, 1000)
    })
</script>

We cannot grab the element after the click. It might be too late

// 🚨 INCORRECT, JUST FOR DEMO
cy.contains('Click to process').click()
cy.get('#name').should('satisfy', Cypress.dom.isDetached)

Instead, we need to prepare the element reference before we click. Then we click and retry checking the element using .should(callback) syntax.

// grab the initial element to prepare
cy.get('#name').then(($el) => {
  cy.contains('Click to process').click()
  // confirm the old element is gone
  cy.wrap(null).should(() => {
    expect($el[0], 'element is gone').to.satisfy(
      Cypress.dom.isDetached,
    )
  })
  // the new element should be quickly there
  cy.contains('#name', 'Anna', { timeout: 0 })
})