Reputation: 39250
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
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