Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gather results from multiple cypress promisses

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
    });
});
  1. Is above approach correct?
  2. Should there be separate tests for each page?
  3. What if I have 50+ pages to check?
  4. What is best approach in Cypress.io in such case?
like image 448
Drop Avatar asked Oct 08 '19 11:10

Drop


2 Answers

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);
});
like image 167
dwelle Avatar answered Nov 20 '22 08:11

dwelle


  • Can gather multiple results from cy.requests?

You can collect them all and act when only all responses are ready with Cypress.Promise.all

  it.only('Gathering results', () => {
    const urls = ['https://google.com',
      'https://en.wikipedia.org']
    const requests = urls.map(url => {
      console.log(`a request sent to  ${url}`)
      return new Cypress.Promise(resolve => {
        cy.request(url).then(resopnse => {
          console.log(`'Response from ${url}`)
          resolve(resopnse)
        })
      })

    })
    Cypress.Promise.all(requests).then(responses => {
      // process responses
      console.log("All responses ready")
      console.log(responses.map(response => response.redirects.toString()))
    })
  })
  • Should we use Cypress for checking the status of an external site?

I don't have the complete context of what you're doing. Apparently, it seems you're using Cypress as a monitoring tool that regularly checks the availability of some external sites and send out notifications. If so, I would say no, Cypress is a testing framework to write tests of your own services/systems.

like image 4
Hung Tran Avatar answered Nov 20 '22 07:11

Hung Tran