How to Handle Dynamic Waits in Cypress.

Niluka Sripali Monnankulama
Many Minds
Published in
7 min readMar 30, 2024

In Cypress, there is a command called “wait.”

This command simply pauses the execution of your test for a specified amount of time. It’s useful when you need to wait for something to happen before proceeding with the test.

Here’s a simple explanation of how it works:

// Wait for 2 seconds
cy.wait(2000);

This code tells Cypress to pause for 2 seconds before moving on to the next command in your test.

You can adjust the time value as needed for your specific scenario. However, it’s often better to use commands like cy.get() to wait for specific elements or events rather than relying solely on cy.wait(), as it makes your tests more reliable and efficient.

“Anti-Pattern

You almost never need to wait for an arbitrary period of time. There are always better ways to express this in Cypress.

Read about best practices here.”

Cypress provides multiple ways to use the cy.wait() command to suit different testing scenarios. Here are some examples:

  1. Waiting for a Specific Element to Exist:
// Wait for an element with the class 'my-element' to exist
cy.wait('@my-element')

2. Waiting for a Specific Network Request:

// Wait for a network request with the alias 'my-request' to complete
cy.wait('@my-request')

3. Waiting for a Specific XHR Request:

// Wait for an XHR request with the alias 'my-xhr' to complete
cy.wait('@my-xhr')

4. Waiting for a Specific Time Period:

// Wait for 5 seconds
cy.wait(5000)

5. Waiting for a Specific Number of Milliseconds:

// Wait for 300 milliseconds
cy.wait(300)

6. Waiting for an Alias to be Ready:

// Wait for an alias 'my-alias' to be ready
cy.wait('@my-alias')

These are some of the ways you can use the cy.wait() command in Cypress to synchronize your tests with various events or conditions. Each usage has its own purpose and can be applied based on the specific requirements of your test scenario.

Let’s explore how you can use parameters with the cy.wait() command in Cypress:

  1. Waiting for a Specific Element to Exist with Timeout:
// Wait for an element with the class 'my-element' to exist for a maximum of 5000 milliseconds
cy.wait('@my-element', { timeout: 5000 })

2. Waiting for a Specific Network Request with Timeout:

// Wait for a network request with the alias 'my-request' to complete within 3000 milliseconds
cy.wait('@my-request', { timeout: 3000 })

3. Waiting for a Specific Route with Timeout:

// Wait for a route containing '/users' to be hit within 2000 milliseconds
cy.wait('/users', { timeout: 2000 })

4. Waiting for a Specific Number of Milliseconds with Log:

// Wait for 300 milliseconds and log a message
cy.wait(300, { log: true })

5. Waiting for an Alias to be Ready with Timeout:

// Wait for an alias 'my-alias' to be ready within 4000 milliseconds
cy.wait('@my-alias', { timeout: 4000 })

6. Waiting with a Custom Message:

// Wait for 1000 milliseconds with a custom message
cy.wait(1000, 'Waiting for something important')

These examples demonstrate how you can use parameters such as timeout, log, and custom messages with the cy.wait() command in Cypress to tailor the waiting behavior according to your testing needs.

Important 🌟

When you use cy.wait() with an alias in Cypress, there are two phases of waiting.

Imagine you’re ordering food online:

  1. Placing an Order (Initiating a Request): You select your items, enter your address, and click “Place Order.” This action triggers a network request from your browser to the server, asking it to process your order.
  2. Waiting for Confirmation (Waiting for Response): After you place the order, Cypress (acting as your testing tool) waits for a response from the server confirming that your order has been received and processed successfully.

Now, let’s apply the concepts of requestTimeout and responseTimeout to this scenario:

  • requestTimeout (Waiting for the Order to be Placed): If the network request to place the order isn't initiated within the specified requestTimeout period, Cypress raises an error. For example, if the server is slow to respond or there are network issues, Cypress will give up waiting after the specified time (default is 5 seconds) and consider it a failure.
  • “If no matching request is found, you will get an error message that looks like this:”
  • responseTimeout (Waiting for Confirmation): Once the order request is sent, Cypress waits for the server to respond within the specified responseTimeout period. If the server takes too long to respond (e.g., due to heavy load), Cypress will raise a timeout error. By default, Cypress waits for up to 30 seconds for a response.
  • “If no response is detected, you will get an error message that looks like this:”

Putting it all together:

  • If you place an order and the server quickly responds within the responseTimeout period, Cypress moves on to the next step in your test.
  • If the server is slow to respond but still manages to confirm your order within the responseTimeout period, Cypress waits patiently and then proceeds with your test.
  • If the server doesn’t respond within the responseTimeout period, Cypress considers it a failure and reports a timeout error, indicating that there's an issue with the server's response time.

This approach balances quick error detection with giving enough time for the server to respond, ensuring smooth test execution.

Using an Array of Aliases..

Here’s a technical explanation of how Cypress handles passing an array of aliases to cy.wait():

  1. Waiting for Requests to Start:
  • Cypress waits for each request represented by the aliases in the array to start.
  • It monitors the network traffic and waits for requests with matching aliases to be initiated.
  • If any request fails to initiate within the requestTimeout period, Cypress will raise an error.

2. Waiting for Responses to Be Received:

  • Once all requests have been initiated, Cypress continues to monitor the network traffic and waits for their responses to be received.
  • It ensures that responses corresponding to each initiated request are received within the responseTimeout period after the request has started.
  • If any response is not received within the specified time limit, Cypress will raise a timeout error.

3. Handling Errors:

  • Cypress handles errors based on the outcome of each request and response.
  • If any request fails to initiate within the requestTimeout or if any response is not received within the responseTimeout, Cypress raises appropriate errors, indicating the failure to complete the network requests within the expected time frame.

4. Continuing Test Execution:

  • Once Cypress ensures that all requests have completed (either successfully or with errors) within the specified timeout periods, it continues with the test execution.
  • This allows Cypress to proceed with further test steps, assertions, or actions based on the outcomes of the network requests.

By managing the timing of request initiation and response reception, Cypress ensures that tests are synchronized with the network activity and provides reliable feedback on the status of network requests during test execution.

This approach helps maintain the stability and predictability of Cypress tests, especially when dealing with asynchronous operations and network communication.

Let’s consider a practical example where we have multiple network requests being made and we want to wait for all of them to complete before proceeding with the test.

Suppose we have three network requests:

  1. Logging in (loginRequest)
  2. Fetching user data (userDataRequest)
  3. Loading a dashboard (dashboardRequest)

Here’s how you can use an array of aliases with cy.wait() in Cypress:

// Perform login request and alias it
cy.login().as('loginRequest');

// Fetch user data and alias it
cy.fetchUserData().as('userDataRequest');

// Load dashboard and alias it
cy.loadDashboard().as('dashboardRequest');

// Pass an array of aliases to cy.wait()
cy.wait(['@loginRequest', '@userDataRequest', '@dashboardRequest']).then((responses) => {
// Handle responses here if needed
// For example, you can access each response:
const loginResponse = responses[0];
const userDataResponse = responses[1];
const dashboardResponse = responses[2];

// You can perform assertions or further actions based on the responses
// For example:
expect(loginResponse.status).to.eq(200);
expect(userDataResponse.body).to.have.property('username');
expect(dashboardResponse.headers).to.have.property('content-type');
});

// Continue with other test steps after all requests have completed
// For example:
cy.contains('Welcome, User!').should('be.visible');

In this example:

  • We perform three separate actions that initiate network requests: logging in, fetching user data, and loading a dashboard. Each request is aliased for future reference.
  • We pass an array of aliases (['@loginRequest', '@userDataRequest', '@dashboardRequest']) to cy.wait(), indicating that we want to wait for all these requests to complete.
  • Inside the .then() block, we can handle the responses if needed. Here, we access each response and perform assertions or further actions based on the responses.
  • Finally, we continue with other test steps after all requests have completed.

This example demonstrates how Cypress synchronizes with multiple network requests using an array of aliases with cy.wait(), ensuring that the test proceeds only after all necessary requests have completed.

In summary, handling dynamic waits in Cypress involves ensuring that your tests can accommodate elements or conditions that may appear or change during test execution. Here’s a summary of the approaches:

  1. Using cy.get() with Assertions:
  • Use cy.get() to wait for an element to appear and then perform assertions on it.
  • Assertions like .should('exist'), .should('be.visible'), etc., ensure the element is present and visible.
// Example: Waiting for a button with class 'submit-button' to become visible 
cy.get('.submit-button').should('be.visible');

2. Using cy.contains() with Retry:

  • Use cy.contains() with the retry option to wait for text to appear dynamically.
  • Cypress retries until the text appears or the timeout limit is reached.
// Example: Waiting for 'Submit' button text to appear 
cy.contains('Submit', { timeout: 10000, retryInterval: 1000 });

3. Using Custom Commands (Optional):

  • You can create custom Cypress commands to encapsulate common dynamic wait scenarios.
// Custom command to wait for an element to appear 
Cypress.Commands.add('waitForElement',
{ prevSubject: 'optional' }, (subject, selector, options = {}) => {

cy.get(selector, options);
});

Usage:

// Example: Waiting for an element with class 'dynamic-element' to appear
cy.waitForElement('.dynamic-element');

4. Using cy.intercept() (For Network Requests):

  • Intercept network requests using cy.intercept() and wait for them to complete.
// Example: Intercepting a network request and waiting for it to complete 
cy.intercept('GET', '/api/data').as('dataRequest');
cy.visit('/dashboard');
cy.wait('@dataRequest');

By using these approaches judiciously based on your specific testing needs, you can ensure that your Cypress tests are robust and adaptable to dynamic changes in your application under test.

--

--

Niluka Sripali Monnankulama
Many Minds

An IT professional with over 7+ years of experience. Member of the WSO2 Identity & Access Management Team.