When adding a new row of information to a table during an e2e test on Cypress, I am encountering difficulty retrieving the data from the most recent row

Using Cypress for e2e testing in an application, the specific behavior being tested involves:

  1. Entering information into a form.
  2. Submitting the form.
  3. Expecting a new field to be created and placed at the bottom of a table.
  4. Checking that the last field contains the values entered in the previous form.

While everything seems to be working fine, there's an issue during testing where the last value is not detected properly, showing the previous value instead as if the creation process wasn't complete yet.

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");

An error message indicates that the data should match with the last row but seems to be picking up information from the row above. Could this be related to internet speed or do I need to use a .then statement somewhere in the code?

Any insights would be greatly appreciated. Thank you.

Answer №1

If you notice an additional row being added after the Submit action, it's recommended to verify that the row count has indeed increased by one.

Utilizing

.should('have.length', initialLength + 1)
will continuously recheck until a timeout is reached.

In some cases, the delay might not be due to timeout but could be related to background processing within the application. To accommodate this, consider including 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");
})

Alternatively, another approach based on the example app provided below is:

Using cy.contains()

You can directly check for your form data without explicitly verifying the row count using cy.contains().

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

Here is a basic web page scenario where clicking a button asynchronously adds a row to a table.

Upon pressing the button and checking the row count, the test should pass successfully.

Application Preview

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

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

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

Testing Approach

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

Answer №2

It appears that the Submit function triggers an asynchronous request that is dependent on a server response.

When utilizing cy.get("tr").last(), it will be executed immediately if there is already a last tr element on the page, occurring before any updates to the DOM by Vue.

One possible approach is to verify the length beforehand (for example, checking for 7 elements).

// ensuring that the 7th row has been added
cy.get("tr").should("have.length", 7);

// subsequent assertions assuming DOM update
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");

Alternatively, using cy.get(tr:last) can simplify the process, as solely employing get with a selector will re-evaluate for DOM alterations, while cy.get("tr").last() does not.

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");

Furthermore, it is advisable to incorporate the use of wait

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

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

// awaiting the completion of the alias
cy.wait('@myApiCall')

// proceed with additional assertions considering potential delays in DOM updates

Integrating API waits (and potentially mock responses) can enhance the stability of tests. Relying on precise timing or omitting wait commands entirely could lead to sporadic failures if your test hinges on an external API.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The middleware is causing disruptions in the configuration of redis and express

I've recently started using Redis and I'm facing an issue with my middleware 'cache' function that seems to be causing problems in my code. Everything works fine without it, the data displays correctly in the browser, and when I check f ...

Utilizing v-for in Vue with TypeScript to generate multiple checkboxes

My goal was to capture the values of checkboxes and store them in an array using v-model. However, I encountered an issue where the first time I toggle a checkbox, it doesn't register. Only after checking a second box and hitting submit does the secon ...

Achieve horizontal wrapping of div elements

Currently, I am developing a blog where search results for articles will be displayed within divs. The design of the website is completely horizontal, meaning that articles scroll horizontally. Creating a single line of divs is straightforward, but it&apo ...

Error encountered when trying to send form data through an AJAX request

Whenever a user updates their profile picture, I need to initiate an ajax call. The ajax call is functioning properly, but the issue lies in nothing being sent to the server. <form action="#" enctype='multipart/form-data' id="avatar-upload-fo ...

Analyzing an array through its sub arrays

Imagine we are dealing with an array of varying length and we need to process it in chunks of maximum size 100, aiming to use the fewest number of chunks. For example, if the array's length is 241, we would need to divide it into 3 subarrays: 41, 100, ...

"Getting Started with Respond.js: A Step-by-Step

I've been struggling to find clear instructions on how to properly set up respond.js. Should I just unzip it into the htdocs folder, or do I only need respond.min.js in there? Then, do I simply reference the file like this... <script src="respon ...

What is preventing me from hashing a password using Mongoose Virtual Setter?

I've been attempting to use a virtual method to hash my password, but for some reason, the password is not getting hashed. Instead, it keeps showing as undefined every time I try to access the virtual field. When I try to save the data without hashing ...

Developing maintenance logic in Angular to control subsequent API requests

In our Angular 9 application, we have various components, some of which have parent-child relationships while others are independent. We begin by making an initial API call that returns a true or false flag value. Depending on this value, we decide whether ...

Sequelize querying using the `WHERE NOT EXISTS()` condition

I am currently working with a many-to-many relationship setup in my database: var Genres = db.define('Movie', { name: { type: Sequelize.STRING(100), allowNull: false }, description: { type:Sequelize.STRING(), ...

Access the elements within arrays without using the square brackets

I am trying to access data from a list, but I am having trouble using square brackets []. The getTalonPaie function calls the get method from the HttpClient service and returns an observable with multiple values. However, when I try to store these values i ...

Utilizing custom filters to navigate through nested ng-repeats

My goal is to achieve a specific behavior by using custom filters. I am working on creating a unified search bar that will display entire group names with all failing students, as well as group names with only the matching students. If you want to see the ...

Assign Angular FromControl value to set the value of a select input

Seeking assistance on setting the initial value of the select dropdown below. I have attempted to do so through my formControl, but the value in the form is accurate while not reflecting in the view. HTML : <mat-form-field> <mat-select name="an ...

The CSS and Javascript files are failing to load

My web page is not displaying my CSS and JavaScript properly when I access it through a folder in the browser. Both files are located within multiple subfolders. I have attempted to rename the files to troubleshoot the issue. <head> <link rel=" ...

Managing multiple input fields with React Hooks

Over at the React documentation forms section, you'll find an example that utilizes class components like so: class Reservation extends React.Component { constructor(props) { super(props); this.state = { isGoing: true, numberOfGu ...

Is there a way to effectively eliminate an array of objects in JavaScript or TypeScript and alter the object structure simultaneously?

I am seeking solutions to restructure an object that has multiple arrays of objects so that I can access the object directly. console.log(value.data.summary[0].data) Instead of accessing arrays in this manner, I want to modify my data structure. Is there ...

Modify the background color of the disabled checkbox in Vuetify

Check out this example where I found a disabled checkbox. Is there a way to set a custom background color for the "on" and "off" states of a disabled checkbox? ...

make Vue acknowledge a component

I am currently facing a perplexing situation where a component is functioning correctly when called directly. However, when this same component is called within another component from a different project, it fails to work. <template> <vue-tel- ...

Creating a custom directive in AngularJS that utilizes an event listener

I am currently working on designing a custom directive that includes a text box. When a user clicks a button, the text box should expand and become focused. If the user then clicks away from the expanded text box, it should minimize, disappear, and display ...

Mastering keyframe animation with Three.js

Hello everyone, I am new to posting and I have a question that I haven't been able to find the answer to anywhere else. Just to clarify, I am not a professional programmer, more of a 3D graphic designer ;) I am interested in importing json models wit ...

Using jqWidgets: What is the best way to insert an HTML form into a jqxWindow?

In my Laravel project, I have created an HTML form inside a jqxWindow widget with the following code: <div id="provinceWindow"> <div id="provinceWindowHeader"></div> <div id="provinceWindowContent"> <form ...