admin管理员组

文章数量:1313962

I am writing a set of tests on a list of elements (HTML table).

Since the tests are still a work in progress, I'm getting a lot of junk data which I'd like to cleanup before each run.

So, I'm trying to delete all rows in before() hook. I imagine the process for deleting should be:

  1. Search products by name
  2. Click a delete icon in first row
  3. Wait for confirmation dialog
  4. Click "Yes"
  5. Repeat from step 2 until there are no rows left

This is the function I've got so far that I call after typing in the search input:

 function deleteAllFoundProducts() {
        cy.get('tbody tr').then(rows => {
            if (rows.length < 1) return // if no rows remain, terminate recursion
            cy.get('span[title="Delete"]', {timeout: 10000}).first().click()
            cy.get('.tBtn[title="Yes"]').click().then(() => {
                cy.get('.loader', {timeout: 10000}).should('not.exist') // wait for list to reappear
                deleteAllFoundProducts() // try again
            })
        })
    }

The problem is – the function continues to run even after deleting the final row, and errors out because there is no span[title="Delete"].

[EDIT]

On closer inspection I realized that an empty table still has one row – with No records found text. I thought it wasn't a row at all. So, the error is caused by having a row that doesn't contain a delete icon. I should have noticed that sooner.

This is relevant HTML with unnecessary Angular stuff removed:

<tbody class="p-element p-datatable-tbody" ng-reflect-value="" ng-reflect-template="[object Object]">
    <tr>
        <td colspan="6">
            <my-loader ng-reflect-loading="false">
            </my-loader>
            <div class="p-2">No matching records found. </div>
        </td>
    </tr>
</tbody>

The solution would, I guess, be to check if the row contains an element with text 'No records found', and perform delete only if it doesn't. But I'm not sure how to do it. Any help would still be appreciated.

I am writing a set of tests on a list of elements (HTML table).

Since the tests are still a work in progress, I'm getting a lot of junk data which I'd like to cleanup before each run.

So, I'm trying to delete all rows in before() hook. I imagine the process for deleting should be:

  1. Search products by name
  2. Click a delete icon in first row
  3. Wait for confirmation dialog
  4. Click "Yes"
  5. Repeat from step 2 until there are no rows left

This is the function I've got so far that I call after typing in the search input:

 function deleteAllFoundProducts() {
        cy.get('tbody tr').then(rows => {
            if (rows.length < 1) return // if no rows remain, terminate recursion
            cy.get('span[title="Delete"]', {timeout: 10000}).first().click()
            cy.get('.tBtn[title="Yes"]').click().then(() => {
                cy.get('.loader', {timeout: 10000}).should('not.exist') // wait for list to reappear
                deleteAllFoundProducts() // try again
            })
        })
    }

The problem is – the function continues to run even after deleting the final row, and errors out because there is no span[title="Delete"].

[EDIT]

On closer inspection I realized that an empty table still has one row – with No records found text. I thought it wasn't a row at all. So, the error is caused by having a row that doesn't contain a delete icon. I should have noticed that sooner.

This is relevant HTML with unnecessary Angular stuff removed:

<tbody class="p-element p-datatable-tbody" ng-reflect-value="" ng-reflect-template="[object Object]">
    <tr>
        <td colspan="6">
            <my-loader ng-reflect-loading="false">
            </my-loader>
            <div class="p-2">No matching records found. </div>
        </td>
    </tr>
</tbody>

The solution would, I guess, be to check if the row contains an element with text 'No records found', and perform delete only if it doesn't. But I'm not sure how to do it. Any help would still be appreciated.

Share Improve this question edited Jul 25, 2024 at 23:45 Ged.Delaney 1457 bronze badges asked Sep 28, 2021 at 10:19 XercesXerces 31 silver badge6 bronze badges 2
  • Can you share the HTML for your table ? – Alapan Das Commented Sep 28, 2021 at 10:33
  • Have you thought about deleting data from the backend instead (assuming you're using one)? i.e. by exposing an endpoint that deletes data, then calling that endpoint in your Cypress lifecycle hooks. That way you can keep your test cases relatively simple. – david-err Commented Sep 28, 2021 at 11:03
Add a ment  | 

3 Answers 3

Reset to default 6

With this recursive function you just need to pass a variable that decrements (or maybe increments to a max)

function deleteAllFoundProducts(rowCount) {
  if (rowCount < 1) return // if no rows remain, terminate recursion
  cy.get('span[title="Delete"]', {timeout: 10000}).first().click()
  cy.get('.tBtn[title="Yes"]').click().then(() => {
    cy.get('.loader', {timeout: 10000}).should('not.exist') // wait for list to reappear
    deleteAllFoundProducts(--rowCount) // try again
  })
}

cy.get('tbody tr')
  .then(rows => deleteAllFoundProducts(rows.length))  // first call here

This will still work when there's a "No matching records found" row appended after deleting all rows, since rowCount is obtained before deletions start.

Recursion is the safer way to iterate, particulary when the DOM is changed during the the loop or there is a conditional part to the test. Using .each() can give you "detached from DOM" errors.

There's a library https://github./bahmutov/cypress-recurse that makes it a bit easier.

You might apply it like ths

import { recurse } from 'cypress-recurse'

before(() => {
  recurse(
    () => {
      return cy.get('tbody tr').eq(0).then($row => {
        const $deleteButton = $row.find('button[title="Delete"]')
        if ($deleteButton.length) {
          $deleteButton[0].click()
        }
        return $deleteButton   // return this to signal end of iteration
      })
    },
    ($deleteButton) => $deleteButton.length === 0  // end if no delete button
  )
})

So this is an assumption, if this is not working then I would need to see the HTML table, So what I am assuming is that in your table you have different rows and in each row you have a Delete button and once you delete the button the row is removed. You can use an inbuilt cypress mand each().

cy.get('tbody tr').each(($ele) => {
  cy.wrap($ele).within(() => {
    cy.get('span[title="Delete"]', {timeout: 10000}).first().click()
  })
  cy.get('.tBtn[title="Yes"]').click()
  cy.get('.loader', {timeout: 10000}).should('not.exist')
})

Or, when there is 'No Records found' displayed, just exit the loop.

cy.get('tbody tr').each(($ele) => {
  if ($ele.find('div.p-2').text().trim().includes('No records found')) {
    return false
  } else {
    cy.wrap($ele).within(() => {
      cy.get('span[title="Delete"]', {timeout: 10000}).first().click()
    })
  cy.get('.tBtn[title="Yes"]').click()
  cy.get('.loader', {timeout: 10000}).should('not.exist')
  }
})

本文标签: javascriptCypressdelete all rows from tableStack Overflow