HMR
HMR

Reputation: 39250

How to repeat actions in cypress and get the result of each action

I am trying to repeat a certain action in cypress defined in doTheAction and then get the results of those repeated actions. When I try Promise.all it immediately resolves to an array of undefined

describe("some test", () => {
  it("for each", () => {
    const data = { a: 1, b: 2 };
    Promise.all(
      Object.entries(data.en).map(([key, value]) =>
        doTheAction(value).then(result => [key, result])
      )
    ).then(result => {
      debugger;
      // array of undefined before the browser did anything
      console.log(result);
    });
  });
});
const doTheAction = text => {
  cy.visit(
    "https://my.site"
  );
  cy.get("some input").type(text);
  return cy
    .get("the result")
    .then(result => result[0].innerText);
};

When I try to chain the actions I get undefined for results when the second action is finished:

describe("some test", () => {
  it("for each", () => {
    const data = { a: 1, b: 2 };
    Object.entries(data).reduce(
      (results, [key, value]) =>
        results.then(results =>
          doTheAction(value).then(
            result =>
              //results is undefined after the second action
              results.concat([[key, result]]) //current result
          )
        ),
      Promise.resolve([]) //initial results
    );
  }).then(results => {
    //never gets here
    console.log(results);
  });
});
const doTheAction = text => {
  cy.visit("https://my.site");
  cy.get("some input").type(text);
  return cy
    .get("the result")
    .then(result => result[0].innerText);
};

Either with Promise.all or chaining the promises the actions will run in the correct order (cypress schedules them and runs them in order) but I don't see how it's possible to get a result out of it.

Upvotes: 2

Views: 5652

Answers (1)

Richard Matsen
Richard Matsen

Reputation: 23463

Data driven tests are quite easy, one generalized pattern is

const arrayOfTestData = [...];
const arrayOfExpectedResults = [...];  

const functionContainingSingleTest = (dataItem, index) => {

  // Commands that use dataItem, and check arrayOfExpectedResults[index]

}

arrayOfTestData.forEach((dataItem, index) => 
  functionContainingSingleTest(dataItem, index)
);
// After loop, reached before commands finish.

What happens is your test en-queue's the commands within the function, as if you had written them sequentially.


If you want to gather results, it becomes more difficult because Commands don't return a usable value. You could try chaining .then(result => results.push(result)), but the problem is any code external to the loop that uses the results array will likely run before the command sequence finishes.

One way is to set a Cypress alias at the loop end and wait for it.

Test

describe('Data-driven tests', () => {

  it('should wait for results', () => {

    const arrayOfTestData = [1,2,3];
    const results = [];

    const functionContainingSingleTest = (dataItem, index) => {

      cy.visit('app/data-driven.html');
      cy.get('input').type(dataItem);

      cy.get('input').invoke('val')
        .then(result => {
          results.push(result[0]);
        });

      if (index === arrayOfTestData.length -1) {
        // Note, the following line executes before all tests finish, 
        // but is just queueing a command, which will run after
        // all the tests have completed
        cy.wrap(true).as('done');  
      }
    }

    arrayOfTestData.forEach((dataItem, index) => 
      functionContainingSingleTest(dataItem, index)
    );

    // After loop, reached before commands finish.
    console.log(results);  // outputs []

    // Wait for the signal 
    cy.get('@done').then(_ => {
      console.log(results);  // outputs ["1", "2", "3"]
    });

  })
})

app/data-driven.html

<input />

Upvotes: 5

Related Questions