Home > Blockchain >  I create a new row with info in a table but I can' t get the last one (the new one) on Cypress
I create a new row with info in a table but I can' t get the last one (the new one) on Cypress

Time:10-26

I am using Cypress for e2e test in an app This particular behavior I want to test is:

  1. Enter info in a form.
  2. Press submit.
  3. New field is created and is placed at the bottom of the table.
  4. Check the last field have the values I entered in the previous form.

Everything is ok, but when testing I don't get the last value but the previous one, like if wasn't finish creating it.

cy.get("button").contains("Submit").click();
    cy.url().should("include", "/advertisers");

    cy.get("tr").last().should("contain.text", "New Advertiser");
    cy.get("tr").last().should("contain.text", "Lom Yolk");
    cy.get("tr").last().should("contain.text", "500");
    cy.get("tr").last().should("contain.text", "Prepaid");

and the error that pops is about it should match data with last row but instead what it detects is info from row above

is my internet speed, or should I place a .then somewhere and somehow?

thanks in advance

CodePudding user response:

If the Submit action adds one row, try checking the row count has increased by one.

The .should('have.length', initialLength 1) will keep rechecking until timeout occurs.

But maybe it's not timeout, instead background processing in the app. For this, also add cy.wait(0).

cy.get('tr').then($current => {

  const initialLength = $current.length;

  cy.get('button').contains('Submit').click();
  cy.wait(0);  // for background processing in the app

  cy.get('tr', {timeout: 10_000}).should('have.length', initialLength   1)

  cy.url().should("include", "/advertisers");

  cy.get("tr").last().should("contain.text", "New Advertiser");
  cy.get("tr").last().should("contain.text", "Lom Yolk");
  cy.get("tr").last().should("contain.text", "500");
  cy.get("tr").last().should("contain.text", "Prepaid");
})

Another variant that should work (based on example app below)

cy.contains()

Without checking the row count, use cy.contains() to check for your form data.

cy.get('button').contains('Submit').click();
cy.url().should("include", "/advertisers");
cy.contains("tr", "New Advertiser");
cy.contains("tr", "Lom Yolk");
cy.contains("tr", "500");
cy.contains("tr", "Prepaid");

Minimal reproducible example

This is a simple web page with a button that asynchronously adds a row to a table.

Checking the row count after pressing the button, the test passes.

App

<body>
  <table>
    <tbody>
      <tr><td>one</td></tr>
      <tr><td>two</td></tr>
    </tbody>
  </table>
  <button>Add row</button>

  <script>
    const button = document.querySelector('button')

    function addRow() {
      setTimeout(() => {
        const tbody = document.querySelector('tbody')
        const tr = document.createElement('tr')
        const td = document.createElement('td')
        td.innerText = 'three'
        tr.appendChild(td)
        tbody.appendChild(tr)
      }, 2000)
    }
    button.addEventListener('click', addRow)
  </script>
</body>

Test

cy.get('tr').then($tr => {
  const initialCount = $tr.length
  cy.get('button').click()
  cy.get('tr').should('have.length', initialCount   1)
  cy.get('tr').last().should('contain.text', 'three')
})

CodePudding user response:

I'm guessing the Submit issues an asynchronous request that relies on some server.

When you use cy.get("tr").last() it will execute right away because if you already have a last tr elements on the page and executes on that before vue updates the DOM.

you could check the length before proceeding (picked 7 randomly)

// this should wait until the 7th row is added
cy.get("tr").should("have.length", 7);

// then these can assume the DOM was already updated
cy.get("tr").last().should("contain.text", "New Advertiser");
cy.get("tr").last().should("contain.text", "Lom Yolk");
cy.get("tr").last().should("contain.text", "500");
cy.get("tr").last().should("contain.text", "Prepaid");

A slightly simpler option is to use cy.get(tr:last), because using only the get with a selector will re-test for DOM changes, whereas using cy.get("tr").last() doesn't.

cy.get("tr:last").should("contain.text", "New Advertiser");
cy.get("tr:last").should("contain.text", "Lom Yolk");
cy.get("tr:last").should("contain.text", "500");
cy.get("tr:last").should("contain.text", "Prepaid");

My recommendation though would be to also include the wait

// create the alias
cy.intercept('/my-api-call/*').as('myApiCall')

//...
cy.get("button").contains("Submit").click();

// wait for the alias to have been called
cy.wait('@myApiCall')

// then make the other assertions but assume still that the DOM may not have updated yet

Including the API waits (and especially mocking it too) can help reduce test flakiness. If you use numeric wait times or skip them altogether, your might might break randomly if you are relying on an external API.

  • Related