Is it possible in Cypress.io to gather results of multiple assertions inside .then
construction so that results can be used outside .then
?
Based on below example - if I have some pages that I would like to smoke-test (to check eg. if code is different than 404) how to gather information about them? How to verify results all together in one smoke test?
Please look at this simple piece of code that shows the problem:
describe('Smoke tests for some pages', () => {
it('should be able to view page that was loaded correctly', () => {
// Arrange:
const pageAddressList = [
'https://en.wikipediaaa.org',
'https://en.wikipedia.org'];
const errors = Array();
// Act:
pageAddressList.forEach((pageAddress) => {
cy.request(pageAddress).then((response) => {
// check response and add some error to errors if needed
})
});
// Assert:
// check if errors is empty
});
});
EDIT 2: This solution was written for Cypress ~3.6.0 or thereabouts so it's possible that newer versions of Cypress work very differently internally, even if you fix some superficial incompatibilities of this code.
EDIT: often someone suggests using Promise.all
, which is not a correct solution. Cypress chainer objects aren't Promises/A+-compatible, they simply appear to be promises because they implement .then
interface. That's why Promise.all
is able to consume an array of chainer objects, but that's it. The resolution values passed to Promise.all().then
callback is not going to be what you expect (see https://github.com/cypress-io/cypress/issues/915).
You can use a helper I've suggested in a similar answer:
// put this in cypress/support/index.js
const chainStart = Symbol();
cy.all = function ( ...commands ) {
const _ = Cypress._;
const chain = cy.wrap(null, { log: false });
const stopCommand = _.find( cy.queue.commands, {
attributes: { chainerId: chain.chainerId }
});
const startCommand = _.find( cy.queue.commands, {
attributes: { chainerId: commands[0].chainerId }
});
const p = chain.then(() => {
return _( commands )
.map( cmd => {
return cmd[chainStart]
? cmd[chainStart].attributes
: _.find( cy.queue.commands, {
attributes: { chainerId: cmd.chainerId }
}).attributes;
})
.concat(stopCommand.attributes)
.slice(1)
.flatMap( cmd => {
return cmd.prev.get('subject');
})
.value();
});
p[chainStart] = startCommand;
return p;
}
usage:
it('test', () => {
const urls = [
'https://en.wikipediaaa.org',
'https://en.wikipedia.org'
];
cy.all(
...urls.map(url => cy.request(url))
).then(responses => {
responses.forEach( resp => {
expect(resp.status).to.eq(200);
});
});
});
That being said, you can also do this:
const urls = [
'https://en.wikipediaaa.org',
'https://en.wikipedia.org'
];
let passes = 0;
urls.forEach( url => {
cy.request(url).then( resp => {
if ( resp.status === 200 ) passes++;
});
});
cy.then(() => {
expect(passes).to.eq(urls.length);
});
The cy.all
helper atop is really useful if you want to access the results without having to keep around globals and accessing them from a cy.then()
callback --- but like I've shown in the last example, everything can be worked around using just vanilla cypress.
Or, if you don't need responses at all, you can simply do:
const urls = [
'https://en.wikipediaaa.org',
'https://en.wikipedia.org'
];
urls.forEach( url => {
cy.request(url).its('status').should('eq', 200);
});