Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cypress - set global variable from function

I have the following flow:

1) On one page I get the value of a field:

 var myID;
 cy.get('#MYID').
        then(($txt) => {
            myID=  $txt.text();
        })
       .should('not.equal', null); 

2) I then navigate to a new page and try to check if this new page contains this id:

cy.get('#myTable').find('td').contains(myID);

It says that myID is not defined. I know the first function is async and reading the documentation it says I can use an alias. The problem with the alias is it needs to be in the beforeEach() function which not to go into a long story I cannto use in this test case. I tried using async/await but it did not seem to work for me as it was still undefined.

like image 717
user2924127 Avatar asked Aug 01 '26 06:08

user2924127


1 Answers

The basic problem here is that Cypress commands run asynchronously from the test code that creates them. You can see this if you put console logs into your code,

var myID;
cy.get('#MYID')
  .then(($txt) => {
    myID=  $txt.text();
    console.log('1', myID);
  })
 .should('not.equal', null); 

console.log('2', myID);

This prints out

2 undefined
1 myText

You can use an alias to overcome this and pass a value down the command chain.

See this section of the docs which shows a similar pattern of code you are using in the DO NOT USE THIS example.

BUT aliases get cleared down between tests, so you should set up a beforeEach() to obtain a new copy of the required ID for each test.

There's another problem with the way you obtain the text value.

Without a return statement the .then() command passes on whatever subject it receives to the next command. See then- Yields

Additionally, the result of the last Cypress command in the callback function will be yielded as the new subject and flow into the next command if there is no return.

So the .should('not.equal', null) is testing that the element isn't null, not that the text is not null.

A better way is to .invoke('text') which is equivalent to $txt.text() and yields the text value to the .should().

Also .should('not.equal', null) won't test that the content is present, since an empty element returns an empty string from element.text(). Use .should('not.equal', '') instead.

Saving via an Alias

describe('grabbing ID for use in multiple tests', () => {

  beforeEach(() => {
    cy.visit('my-page-1.html')
    cy.get('#MYID')
      .invoke('text')
      .as('mySavedID')
  })

  it('ID should not be null', () => {

    cy.get('@mySavedID')
      .should('not.equal', '')

  })

  it('ID should be found in table', () => {

    cy.visit('app/navigate-to-new-page-2.html');
    cy.get('@mySavedID').then(myID => {
      cy.get('#myTable').find('td').contains(myID);
    })

  })
})

Saving by queuing the setting of the variable

The alias pattern may not be ideal if visiting page #1 is time consuming.

In this case, you can save the variable via a custom command. The difference is that the code you had within the .then() is moved into a command which is queued, so the async problem does not occur.

describe('grabbing ID for use in multiple tests', () => {

  let savedVariable;

  Cypress.Commands.add("saveVariable", {prevSubject: true}, (value) => {
    savedVariable = value;
  });

  it('id should not be null', () => {

    cy.visit('my-page-1')
    cy.get('#someId')
      .invoke('text')
      .should('not.equal', '')
      .saveVariable()

    // OR test the variable separately like this

    cy.wrap(savedVariable)
      .should('not.equal', '')
  })

  it('id should be found in table', () => {

    cy.visit('my-page-2');
    cy.get('#myTable').find('td').contains(savedVariable);

  })
})

NOTE
The above is valid if the two pages are in the same domain, e.g two pages of a SPA. Otherwise, the test runner resets itself when a new domain is encountered, and all javascript variables are lost.

like image 186
Richard Matsen Avatar answered Aug 02 '26 20:08

Richard Matsen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!