Gauthier Peel
Gauthier Peel

Reputation: 1518

Cypress access alias with this.* doesn't works even with function

Trying to access the alias value on the this () Context, I followed the docs from https://docs.cypress.io/api/commands/as#Fixture

In the .then callback the dataExample parameter is filled OK, but on this.example it is undefined.

describe('cy.as and this', function () {

  it('Testing cy.as and this', function () {

    cy.fixture('example.json').as('example')
      .then(function (dataExample) {
        cy.log('ACCESSING this, dataExample', this, dataExample);
      });

    cy.log(`ACCESSING this.example : ${JSON.stringify(this['example'])}`, this);
  });

});

The first log outputs the following: with the dataExample correctly filled, but the contex.example undefined logging this and dataExample in the .then

The second log outside of .then, with this.example also undefined this

If I move the cy.fixture line into a beforeEach() it does work. Cold somebody explain this behavior?

describe('alias', () => {
  beforeEach(() => {
    cy.fixture('example.json').as('example');
  });

  it('can access all aliases as properties', function () {
    expect(this['example']).not.to.eq(undefined); // true
    cy.log('THIS', this); // curious =>  [0]: Context {_runnable: undefined, test: undefined, example: undefined}
    cy.log('THIS.example', this['example']); // {name: 'Using fixtures to represent data', email: '[email protected]',
                                             // body: 'Fixtures are a great way to mock data for responses to routes'}
  });
});

Upvotes: 1

Views: 464

Answers (2)

TesterDick
TesterDick

Reputation: 10560

A helpful way to think of the flow is that there are two passes:

  • first pass, runs the javascript of the test and executes plain JS but enqueues Cypress commands (and their callbacks)

  • second pass executes those commands

So when

cy.log(`ACCESSING this.example : ${JSON.stringify(this['example'])}`, this)

is enqueued in the first pass, the parameter this['example'] is evaluated immediately (value is undefined).

It's not storing the parameter expression to be evaluated in the second pass, which is what most people expect.

But you can defer the parameter evaluation by putting it inside another callback.

cy.then(() => {
  // this callback code is enqueued
  // so the evaluation of "this['example']" is deferred until the queue is running
  cy.log(`ACCESSING this.example : ${JSON.stringify(this['example'])}`, this)
})

Gleb Bahmutov has a blog
How to replace the confusing cy.then command with the less confusing cy.later command

Upvotes: 6

Mikhail Bolotov
Mikhail Bolotov

Reputation: 1114

Cypress commands are asynchronous. Cypress does not execute a command immediately, it just puts one into a queue to be executed lately. So any command inside a single block of code will not be executed in this block. And the other block of code means any of this:

  1. other it test
  2. before/beforeAll/after/afterAll hooks
  3. then/should callback

So if you want to use a value from a cypress command inside the same it test, you have to use a then callback in order to give Cypress time to execute the command.

If you use a cypress command in a beforeEach hook, the command will be executed by the time when it code starts.

Upvotes: 1

Related Questions