Skip to content

Latest commit

 

History

History
93 lines (73 loc) · 3.85 KB

while-loop-diy-workaround-cypress.md

File metadata and controls

93 lines (73 loc) · 3.85 KB

while loop DIY workaround in Cypress

Simplified Dev Story

  • While running end-to-end tests in Cypress, I want to perform an action a varying number of times until a certain condition is met.
  • Unfortunately, I can't use a while loop because Cypress runs its commands asynchronously so loops don't behave as expected.

Simplified Solution

To perform an action while as many times as necessary until a condition is met, most devs would use normally use a while loop. Unfortunately, loops don't work in Cypress. Still, here's a pseudocode version of this idea:

while (!condition) {
  perform(actionsToRepeat);
}
// This runs only if the condition is met.
perform(finalAction);

Here's the convoluted Cypress solution I that worked for me. It required recursion.

const recursiveFunction = () => {
  cy.get("relevantElementsForCondition") //
    .then((relevantElementsArg) => {
      // stop condition
      if (conditionIsMet(relevantElementsArg)) return;
      // run until stop condition (select next month in dropdown)
      cy.actionsToRepeat();
      /*
       */

      /**
       * I know it't best to avoid adding explicit waits, but
       * it seems like it's required here. Even the Cypress
       * docs include an explicit wait. In my case, 100ms
       * worked, but the Cypress documentation included 500ms 🤷‍♂️
       */
      cy.wait(100);
      recursiveFunction();
    });
};
recursiveFunction();

cy.finalAction();

A real user story

Here is an actual use case. There's a Dropdown with month names and a table that lists data for the selected month. We're doing visual testing, so we want to capture a screenshot of the table with data rows, to make sure they are rendered correctly.

<Dropdown with month names (default = current month)>

Heading (always visible) Heading (always visible)
Data rows (min = 0, max = 10)
Summary row (always visible)
  • The Dropdown's selects the current month by default.
  • The table has 2 rows that are always present (Heading at the top and Summary at the bottom). The data rows are loaded for the selected month. If the current month has no data, no data rows are loaded. If there are n rows of data, they are all loaded.
  • Some months may not have any data so the table will have just 2 rows: the Heading and Summary. It's not possible to know how many data-less months there are, but we need to capture a table with data rows.

A real world solution

We need to use the Dropdown to change the selected month until we reach a month with n > 0 rows of data, so the table can have rows > 2.

const selectFirstMonthWithDataRows = () => {
  cy.get("tr") // table rows
    .as("expect table rows > 2") // this is optional but labels the command in the Cypress test runner.
    .then((tableRows) => {
      // stop condition
      if (tableRows.length > 2) {
        // optional but nice to see in the Cypress runner.
        cy.log(" 👍 Selected month has data rows");
        return;
      }
      // run until stop condition (select next month in dropdown)
      cy.get("#dropdown").click();
      // find the selected month in the dropdown and click the next sibling (next month)
      cy.get("li[aria-selected=true]").next().click();
      cy.wait(100);
      selectFirstMonthWithDataRows();
    });
};
selectFirstMonthWithDataRows();

cy.takeCustomScreenshots("Table with data rows FTW! 🎉");

Resources

The Cypress docs are seriously awesome! They explained clearly why loops don't work as expected and showed DOs and DONTs when dealing with these unique situations.