Trouble with updating a variable within a loop in Cypress

During my experience with writing Cypress tests, I came across an issue that is preventing me from updating a specific variable. The goal of my test is to run a loop and update the questionId variable within each iteration for making API queries. However, the problem lies in the fact that the variable retains its initial value of 0 and fails to update while inside the loop. Despite researching various articles on Cypress' async/sync behavior, I have not been able to find a solution.

Below is a snippet of the test:

it('Should pass', function () {
            cy.visit(`${Cypress.env('appUrl')}/url`)
        
            let questionId: number = 0
            for (let index = 0; index < 9; index++) {
                cy.intercept({ method: 'GET', path: `${questionId}` }).as('questionData')
    
                    cy.log('next question id: ' + questionId)
    
                    cy.intercept({ method: 'POST', path: 'answers' }).as('answers')
                    cy.contains('button', 'Submit answer').click()
    
                    cy.wait('@answers')
                        .then((xhr: any) => {
                            expect(xhr.response.statusCode).to.eq(200)
    
                            questionId = xhr.response.body.data.next_question_id
                            cy.log('new question id: ' + questionId)
                            cy.contains('span', 'You are correct!').should('be.visible')
                            cy.contains('button', 'view solution').click()
                            cy.contains('button', 'continue').click()
                        })
            }
    
        })

Answer №1

When working with Cypress, it can exhibit peculiar behavior when using traditional for loops. A better alternative is to utilize Cypress lodash's times function. (You can refer to an article on Lodash's times function, as Cypress._ is essentially a wrapper around Lodash.)

...
let questionId = 0;
Cypress._.times(9, () => {
  cy.intercept({ method: 'GET', path: `${questionId}` }).as('questionData')
    
  cy.log('next question id: ' + questionId)
    
  cy.intercept({ method: 'POST', path: 'answers' }).as('answers')
  cy.contains('button', 'Submit answer').click()
    
  cy.wait('@answers')
    .then((xhr: any) => {
       expect(xhr.response.statusCode).to.eq(200)
    
        questionId = xhr.response.body.data.next_question_id
        cy.log('new question id: ' + questionId)
        cy.contains('span', 'You are correct!').should('be.visible')
        cy.contains('button', 'view solution').click()
        cy.contains('button', 'continue').click()
  })
});

Answer №2

The issue lies in synchronous loops such as

for (let index = 0; index < 9; index++) {...}

which must finish executing before any Cypress commands can begin.

A similar constraint applies to

Cypress._.times(9, () => {...})
.

To address this, one solution is implementing a recursive function.

This approach ensures that asynchronous operations complete at each stage before advancing to the next step (this sequence is crucial as subsequent steps depend on previous outcomes)

const handleQuestion = (questionId, iteration=0) => {

  if (iteration === 9) return    // finished, exit

  cy.intercept({ method: 'GET', path: `${questionId}` }).as('questionData')
  // What is the purpose of the above intercept? 
  // Is it necessary to wait for it, and what triggers the GET?
  

  cy.log('next question id: ' + questionId)
  cy.intercept({ method: 'POST', path: 'answers' }).as('answers')
  cy.contains('button', 'Submit answer').click()
    
  cy.wait('@answers').then((xhr: any) => {
    expect(xhr.response.statusCode).to.eq(200)
    const nextQuestionId = xhr.response.body.data.next_question_id
    cy.log('new question id: ' + nextQuestionId)
    cy.contains('span', 'You are correct!').should('be.visible')
    cy.contains('button', 'view solution').click()
    cy.contains('button', 'continue').click()
      .then(() => {
        cy.contains('button', 'Submit answer')    // Ensure submit button
          .should('be.visible')                   // is ready for next question
          .and('be.enabled')
        handleQuestion(nextQuestionId, ++iteration)  // Proceed to next question
      })
  })
}

handleQuestion(0)  // Begin with question #0

The validation prior to the next iteration may require adjustments

cy.contains('button', 'Submit answer')    
  .should('be.visible')                   
  .and('be.enabled')

This segment is designed to await the readiness of the page for the forthcoming question.

You might also verify certain text indicating the prepared state like

cy.contains('Please submit your answer')

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

Assign a value to a variable using a function in Typescript

Is there a way in typescript to explicitly indicate that a function is responsible for assigning value to a variable? UPDATED CODE Here, the code has been simplified. While getText() ensures that it will never be undefined, this may not hold true in subs ...

There seems to be an issue with my React application that was built using Webpack 5 and compiled with TypeScript. The @tailwind directive is not functioning properly in the browser, and

As I embark on creating a fresh react application using Webpack 5, Tailwind CSS, and Typescript, I find myself at a crossroads. Despite piecing together various tutorials, I am struggling to configure the postcss-loader for Tailwind. While traditional .css ...

How can I adjust the font size property for Material UI Icons through typing?

I put together the code snippet below, and I'm trying to define a specific type for the custom iconFontSize prop. Can someone guide me on how to achieve this? import { SvgIconComponent } from '@mui/icons-material' import { Typography, Typogr ...

How to anticipate an error being thrown by an observable in RxJS

Within my TypeScript application, there exists a method that produces an rxjs Observable. Under certain conditions, this method may use the throwError function: import { throwError } from 'rxjs'; // ... getSomeData(inputValue): Observable<s ...

Is there a method to retrieve Mui state classes easily?

One thing I really appreciate is the way to style mui-components with their class names. I'm curious if there's a method to access state classes like Mui-checked using a variable. Let me delve deeper into this: I have a styled component that lo ...

What is the best way to attach a jQuery UI event handler to a button that has been dynamically generated?

Currently, I have a jquery ui modal dialog box form that inserts items into a table. A specific column in the table contains a button which serves as a link to edit the particular row. My goal is to attach an event handler to this button so that when a use ...

Can Angular be used to dynamically filter a JSON object to display only the fields that match a specified filter text?

Sorry if this question has already been asked; I couldn't find the solution. Here is my issue: In my Angular app, I am retrieving a complex JSON object from a web service. I then present this JSON object to the user in tree format using ngx json vie ...

Precisely outline the function type that operates on an object, makes changes to its values, and then outputs the object in TypeScript

Can anyone help me create a function that generates a new object with the same keys as the input object, but with all values set to null? Here's the existing code: function nullify(arg) { var returnObj = {} for (var key in arg) { returnObj[ ...

Transferring data between jQuery and other global JavaScript variables

I'm facing a challenge when trying to make functions I created in jQuery access JavaScript values defined elsewhere. For instance, I have a function set within my jQuery code. var parentImg = ''; //global variable. $(document).change(funct ...

Utilize Node.js to proxy Angular requests to a service hosted on Azurewebsites

I am trying to set up a proxy post request in my Node.js server and receive a response from the target of this request. Below is an excerpt from my server.js file code where I have implemented the proxy, but I am facing a issue with not receiving any respo ...

The transformation in the resulting array is evident when a nested array is altered after being concatenated using Array.concat

MDN explains concat as follows: The concat() function is utilized to combine two or more arrays without altering the original arrays. Instead, it produces a new array. Let's examine the code snippet below: Example 1 const array1 = [['a& ...

If a specific class is identified, add a border to the div when clicked using JavaScript

Is there a way to use javascript/jquery to add a border to a selected div? I have multiple rows with columns, and I want only one column per row to be highlighted with a border when clicked. Each row should have one column with a border, so it's clear ...

Incorporating unique symbols into a RegExp object during its creation using a variable

As a beginner, I am currently working on a small function that will allow users to pick up objects and add them to an inventory by entering text in a box. In my setup, there is a text box with the id "commandBox" and a button that triggers the 'pickU ...

Asynchronous waterfall call in Node.js to call the method before

Is it possible to invoke a previous method within async.waterfall from a subsequent method? async.waterfall([ function (callback) { }, function (reservationStatus, callback) { }, function (reservationStatusList, f ...

What causes fs to produce an error when routing to a new page, yet refreshing the page resolves the issue?

Concern: I have developed a NextJs application with 4 input fields, each connected to a predefined options list read in as a json file within the project. The user can select two fields and then proceed to a search page by clicking a button. From the sear ...

Find the length of time in Typescript (measured in hours, minutes, and seconds)

Trying to calculate the duration between two dates in TypeScript (Angular): 2021-11-19 21:59:59 and 2021-11-19 22:00:18 let startDate: Date = new Date(start); let endDate: Date = new Date(end); if(end != null) { let duration = new Date(endDate.getT ...

The div height adjustment peculiarities in IE7 and IE8 are causing quite a stir

I recently encountered a problem with my HTML/JS code that I thought was simple. The code is designed to expand the size of a div on mouseover and then collapse it back on mouseout. Here's how the code looks: CSS: .sign-in-up { position: absolut ...

The specified '<<custom component name>>' argument does not match the 'Type<<custom component name>>' parameter

I'm currently facing an error that indicates a type parameters mismatch, but I can't pinpoint where in the process it's happening. Argument of type 'ModalUserInfoComponent' is not assignable to parameter of type 'Type<Mo ...

Utilizing PUG for Iterating Through Multiple Items in Express Framework using JSON Data

I'm currently working on a small application using Express and PUG, aiming to achieve the following: https://i.stack.imgur.com/ZDyTK.png index.pug ul#restaurants-list li img.restaurant-img(alt='Mission Chinese Food', sr ...

A guide on how to automatically preselect a RadioGroup option in Material-UI

When a user selects an option from the MCQ Select using RadioGroup in my code and submits it, they should be able to return later and see the option they selected highlighted, similar to how Google Forms allows users to review their selections. Below is t ...