ptk
ptk

Reputation: 7653

How to get values of multiple aliases without introducing callback hell in Cypress?

Say I want to retrieve the values of two Cypress aliases and use it in my test case. How do I do so without nesting them in the following manner?

cy.get('@alias1')
    .then((alias1) => {
        cy.get('@alias2').then((alias2) => {
            someFunctionThatUsesBothAliases(alias1, alias2);
        })
    })

Upvotes: 20

Views: 10808

Answers (5)

Aladin Spaz
Aladin Spaz

Reputation: 10018

Update for Cypress v13

The code @KondasamyJayaraman refers to appears to be broken, I see it relies on internal Cypress implementation from about 5 years ago.

Here is a simpler one-line version which uses a standard Cypress custom command:

it('custom command to get all alias values', () => {

  Cypress.Commands.add('aliases', function(aliasNames) { 
    return aliasNames.map(a => this[a])
  })

  cy.wrap('a').as('foo');
  cy.wrap('b').as('bar');
  cy.wrap('c').as('baz');

  cy.aliases(['foo', 'bar', 'baz']).then(values => {
    expect(values).to.deep.eq(['a', 'b', 'c'])
  })
})

enter image description here


Substituting the test Context for `this`

If you are still having trouble, Cypress applies the test context to callback functions, so you can use cy.state('ctx') instead of this, then there is no need to change the arrow functions to function()

// works with arrow function

Cypress.Commands.add('aliases', (aliasNames) => { 
  return aliasNames.map(a => cy.state('ctx')[a])
})

Note cy.state('ctx') is an internal method.

Upvotes: 26

Mikhail Bolotov
Mikhail Bolotov

Reputation: 1114

this.* notation can be used to access multiple aliases within a single code block:

    cy.wrap('foo').as('foo');
    cy.wrap('bar').as('bar');
    cy.wrap('baz').as('baz');

    cy.then(function () {
      cy.log(this.foo + this.bar + this.baz)
    })

See this article for details

Upvotes: 9

Tree Hugger
Tree Hugger

Reputation: 9

This is similar to the values array used above but I do the following:

// Make all data available in one alias:
    const testData = {};
    cy.get('@data1').then((data1) => {
      testData.data1 = data1;
    });
    cy.get('@data2').then((data2) => {
      testData.data2 = data2;
    });
    cy.get('@data3').then((data3) => {
      testData.data3 = data3;
    });
    cy.wrap(testData).as('testData');

Then when you need the data:

cy.get('@testData').then((testData) => {
   const { data1, data2, data3 } = testData;
   ...
});

Upvotes: 0

Kondasamy Jayaraman
Kondasamy Jayaraman

Reputation: 1864

One different solution which is custom developed by Mr. Nicholas Boll, where you will get aliases as an array using custom commands createAlias/ getAliases,

// get many aliases - API is similar to Promise.all
cy.getAliases([getFoo, getBar, getOne]).then(([foo, bar, one]) => {
  foo // string
  bar // string
  one // number
  console.log(foo, bar, one) // logs 'foo', 'bar', 1
})

Here is the complete details in his blog - https://medium.com/@NicholasBoll/cypress-io-making-aliases-type-safe-b6f5db07d580

Code reference - https://github.com/NicholasBoll/cypress-example-todomvc/tree/feat/type-safe-alias

Upvotes: 7

dwelle
dwelle

Reputation: 7302

You can do this:

it('test', () => {
    cy.wrap('foo').as('foo');
    cy.wrap('bar').as('bar');
    cy.wrap('baz').as('baz');

    const values = [];
    cy.get('@foo').then( val => {
        values.push(val);
        return cy.get('@bar');
    }).then( val => {
        values.push(val);
        return cy.get('@baz');
    }).then( val => {
        values.push(val);
        someFunc(...values);
    });
});

Or you can use this helper I cobbled together (put it in your 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;
}

and use it like so:

it('test', () => {

    cy.wrap('one').as('one');
    cy.wrap('two').as('two');
    cy.wrap('three').as('three');

    cy.all(
        cy.get(`@one`),
        cy.get(`@two`),
        cy.get(`@three`)
    ).then( values => {
        someFunc(...values);
    });
});

Upvotes: 12

Related Questions