The root issue here lies in the asynchronous nature of Cypress commands running independently from the test code that initiates them. This becomes evident when incorporating 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 results in the following output:
2 undefined
1 myText
To address this, using an alias allows for passing a value down the command chain.
Refer to this section of the documentation, which showcases a similar code pattern as demonstrated in the DO NOT USE THIS example.
However, aliases are cleared between tests, so setting up a beforeEach()
function is necessary to obtain a fresh copy of the required ID for each test.
Another issue arises in how you retrieve the text value.
Without a return statement, the .then()
command forwards whatever subject it receives to the subsequent command. Check then- Yields
Moreover, if there is no return present, the result of the last Cypress command within the callback function will be yielded as the new subject and flow into the next command.
Subsequently, the .should('not.equal', null)
is confirming that the element is not null rather than verifying the non-null status of the text itself.
A more effective approach involves using .invoke('text')
, equivalent to $txt.text()
, providing the text value to the .should()
.
Additionally, .should('not.equal', null)
does not check for content presence since an empty element returns an empty string via element.text()
. Utilize .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
If visiting page #1 proves time-consuming, the alias pattern might not be ideal.
In such cases, saving the variable through a custom command is viable. The code previously placed within the .then()
now resides within a queued command, mitigating the async problem's occurrence.
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 method holds validity if both pages reside within the same domain, e.g., two pages of a single-page application (SPA). If encountering a new domain, the test runner resets itself, resulting in the loss of all javascript variables.