I'm trying to set a variable within a .then() command which is declared outside of it, and after the whole block finished (the .then()) I'm returning that value.
The problem is, when I return the value, the variable is undefined, but within the .then() block, the variable is loaded.
Here is the example code:
public getValueFromElement(): string {
cy.log("Obtaining the Value");
let myNumber: string; // Here I'm declaring my variable
cy.get(this.labelWithText).then(($element) => {
let originalLabelText: string = $element.text();
let splittedText: string[];
splittedText = originalLabelText.split(": ");
myNumber = splittedText[1]; // Here I'm assigning the value
cy.log("Inside the THEN" + myNumber); //This logs the number correctly
});
return myNumber; // But after I return it using the function, the value is `undefined`!
}
I'm assuming this could be related to the async / sync problem, as the return statement is being executed immediately when the function is called, and the promise created by the .then() is still running, but I don't know how to fix this.
Do you know how I can wait for the .then() to finish first before returning the value?
Thanks!!
You say "The problem is, when I return the value, the variable is undefined".
That's because the return myNumber line runs before the cy.get(this.labelWithText).then(($element) => { completes, because the command is running asynchronously.
You need to return the command itself, and also the derived myNumber is returned from inside the .then().
public getValueFromElement(): Chainable<string> { // cannot return the raw string
cy.log("Obtaining the Value");
return cy.get(this.labelWithText).then(($element) => {
...
const myNumber = splittedText[1];
cy.log("Inside the THEN " + myNumber)
return myNumber
})
}
Use it like this
getValueFromElement().then(myNumber => {
cy.log("Outside the function " + myNumber)
})
You can do it synchronously like this
public getValueFromElement(): string {
cy.log("Obtaining the Value");
const $element = Cypress.$(this.labelWithText)
const originalLabelText: string = $element.text()
const splitText = originalLabelText.split(": ")
const myNumber = splitText[1]
return myNumber
}
Here you sacrifice the retry options which are built into asynchronous commands.
Cypress says to use it only if you are sure the element exists already, which depends on the context of your text.
@MikhailBolotov indeed. This is how you'd handle that
cy.get("myOpenElementSelector").click() // async code
.then(() => { // must wrap sync code in then
const myNumber = getValueFromElement() // to ensure correct sequence
expect(+myNumber).to.eq(64)
})
@Mihi has the idomatic way, but it's sometimes difficult when composing page object methods.
I've come to the conclusion that this works:
public async getTheNumber(): Promise<string> {
return new Promise((resolve, reject) => {
cy.log("Retrieving the number");
cy.get(this.selector).then(($element) => {
let myNumber = $element.text().split(": ")[1];
cy.log(`The Number is ${myNumber}`);
resolve(myNumber);
});
});
}
and when reading it from the test I'm doing this:
myNumberAtTestLevel = await myObject.getTheNumber();
Thing is that I've seen that I have to change my it() method to async in order for this to work.
However, I've come across this documentation of Cypress: https://docs.cypress.io/api/utilities/promise#Syntax
I'm trying to implement the same using Cypress.Promises but I'm not able to.
Any ideas?