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

Can you use setValidators() in Angular to validate two patterns simultaneously?

Is there a way to validate both IP address and IP address range in a single control using Angular? I have tried using the following code snippet: controls["CapPoolVolExpolAldClientControl"].setValidators([Validators.required, Validators.pattern(/([0-9]){1 ...

What could be causing the returned promise value to not refresh?

I am currently facing an issue with my program. Upon clicking a button, the goal is to update the "likes" attribute of a MongoDB document that has been randomly fetched. Despite setting up the logic for this, the update does not occur as intended: MongoCli ...

Encountering an issue with finding the module `scheduler/tracing` in React Native

Encountering an error during the react-native run-android process: Error: Unable to resolve module `scheduler/tracing` from `/Users/miftahali/projects/react/appscustomec/node_modules/react-native/Libraries/Renderer/oss/ReactNativeRenderer-dev.js`: Module ...

Pressing a button to input a decimal in a calculator

I am encountering an issue with inputting decimals in my JavaScript. I have a function that checks if the output is Not a Number (NaN). If it is, I want it to output a decimal instead. I attempted to add another conditional statement like this: var opera ...

Accessing a PDF document from a local directory through an HTML interface for offline viewing

As I work on developing an offline interface, I'm encountering an issue with displaying a PDF file when clicking on an image. Unfortunately, the code I have in place isn't working as intended. Can someone please assist me with this problem? Below ...

Code that changes every occurrence of a particular filtered selection of HREF values to a different value

When faced with the limitation in Firefox where links cannot be opened in a new tab if they have a HREF tag diverting to a function, it might be necessary to utilize a script to convert them to an actual HREF. Understanding the functionality of foo: func ...

Clicking the mouse within three.js

I'm facing an issue with my webpage that features a three.js (webgl) graphic created using a .dae file imported from Blender. My goal is to draw a square or mark wherever the mouse is clicked, but for some reason, the square appears in a different loc ...

How do I dynamically incorporate Tinymce 4.x into a textarea?

I am encountering a small issue when trying to dynamically add tinymce to a textarea after initialization. tinymce.init({ selector: "textarea", theme: "modern", height: 100, plugins: [ "advlist autolink image lists charmap print p ...

Merging two arrays that have identical structures

I am working on a new feature that involves extracting blacklist terms from a JSON file using a service. @Injectable() export class BlacklistService { private readonly BLACKLIST_FOLDER = './assets/data/web-blacklist'; private readonly blackl ...

Substitute the ajax reply with the text currently displayed

I am facing an issue with the AJAX response in my HTML page. Currently, the response is being appended on top of the existing text instead of replacing it. Here is a snippet of my code: <html> <head> <h2>This is a test</h2> ...

Updating the count property of an object using setState in React

I have an array of integers ranging from 0 to 6 as my input. My goal is to create an object that gives the count of each number in the array. edition = [6, 6, 6, 1, 1, 2]; const [groupedEdition, setGroupedEdition] = useState([{"0": 0, "1&quo ...

The charAt function in a basic JavaScript if statement is failing to execute

When a user inputs a number that does not start with 6 or 9, an error occurs: console.log($(this).val().charAt(0)); if($(this).val().charAt(0) != 6 || $(this).val().charAt(0) != 9){ x=false; }else { x=true; } The console.log function corre ...

Error Encountered: POST method not supported in ajax request using djangoIs this a

I am currently encountering an issue while trying to pass form data values through ajax. I keep getting a method not allowed error when attempting to add a comment on a blog post. The form below is located inside the blog_detail page: <form id="co ...

The duration required to render DOM elements

Trying to determine the best method for measuring the rendering time of DOM elements in a web browser. Any tips? I'm currently developing an application that involves significant DOM manipulation as part of comparing the performance between Angular 1 ...

Choose the option for overseeing safaris

Hello there! I need some help with Safari. Can you please guide me on how to disable the arrows? https://i.stack.imgur.com/1gzat.png ...

View content from an external file within an iframe

My goal is to showcase a text file within an iframe that updates automatically every second. To achieve this, I have created a simple function: function refresh() { $("#derek").attr('src', $("#derek").attr('src')); }; The automati ...

Another component's Angular event emitter is causing confusion with that of a different component

INTRODUCTION In my project, I have created two components: image-input-single and a test container. The image-input-single component is a "dumb" component that simplifies the process of selecting an image, compressing it, and retrieving its URL. The Type ...

Despite EsLint and Prettier's efforts to improve code quality, users are experiencing frequent unnecessary errors and unexpected auto-insertion of parentheses at

When working with redux saga for handling asynchronous calls, I encountered an issue. After making an API call and storing the retrieved data in local storage, eslint/prettier automatically adds parentheses to the assignment operator at the end of the line ...

React application experiencing freezing when setInterval function is utilized

I've been working on incorporating Conway's Game of Life into a React project, but I'm encountering freezing issues whenever a new generation is triggered. My assumption is that the problem lies in the excessive overhead from constant DOM re ...

While attempting to run the project I downloaded from GitHub using the command npm run serve, I encountered the following error: "Syntax Error: Error: No ESLint configuration found in

After receiving a Vue.js project from GitHub, I attempted to download and run it. However, when I tried the command npm run serve, I encountered an error message: Syntax Error: Error: No ESLint configuration found in C:\Users\User\Desktop&bs ...