Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cypress : Setting a variable from the result of a task

I need to retrieve an url from an email that was sent during the begining of a cypress scenario.

Waiting for the email and retrieving the url inside it is an asynchronous task, so I created a cypress plugin called "readMail" : it will take the email address as input, wait for the gmail api to actually have an email whose recipient is this address, then read the mail and return a promise resolving an url contained in the body of the email. This process works fine, because logs from the plugin do correctly show the extracted url.

My issue is to make this extractedUrl available for later use as a param e.g. cy.visit(extractedUrl) : outside the body of task(...).then(...), the variable is still not set.

Here is the code :

it('should generate correct email', () => {
    const recipientEmail: string = '[email protected]';
    const extractedUrl:string = '';

    // ....scenario generating a email to '[email protected]'.....

    cy.task('readMail', recipientEmail) // plugin logs "https://some.url.com" after a few seconds
    .then((result:any) => {
        extractedUrl= result;
        console.log(result); // logs "https://some.url.com" in the terminal
        cy.log("1:" + extractedUrl); // logs "1:https://some.url.com" in cypress UI
    });
    cy.log("2:"+ extractedUrl); // logs "2:" in cypress UI
    console.log(extractedUrl); // logs nothing
    cy.visit(extractedUrl);
});

I am probably missing something about how cypress handles async... I tried several different things : cy.wrap and the cypress-wait-until plugin but couldn't find any way to make those work.

Actually I found one way which was to declare extractedUrl outside the test and begin a new test it('...', () => {...}) : then, extractedUrl actually has the correct value at the beginning of the next test, but that is not ideal and I want to understand why. Thanks for your help !

like image 231
Sbu Avatar asked Jan 07 '20 10:01

Sbu


People also ask

How do you return a value from then in Cypress?

To do this I'm using wrap() to return a Chainable containing the value I want to return. Since wrap() is returning a Cypress. Chainable , we can call . then() on our commands.

How do you pass variables in Cypress?

By storing the variable outside of the test context using a getter and setter, you are able to pass variables between Cypress tests. This is especially useful in case you need to split up your test for cross-domain form submission and result verification.

How do you fetch text in Cypress?

Cypress can validate the text on an element with the help of jQuery text() method. This method shall help us to fetch the text content on the selected element. We can also put assertions on the text content of the element. cy.

How do you get baseUrl in Cypress?

To get baseUrl value use Cypress. config(). baseUrl or Cypress. config('baseUrl') .


1 Answers

TL;DR; put your entire code inside the then callback

it('should generate correct email', () => {
    const recipientEmail: string = '[email protected]';
    const extractedUrl:string = '';

    // ....scenario generating a email to '[email protected]'.....
    // plugin logs "https://some.url.com" after a few seconds
    cy.task('readMail', recipientEmail).then((result:any) => {
        extractedUrl= result;
        console.log(result); // logs "https://some.url.com" in the terminal
        cy.log("1:" + extractedUrl); // logs "1:https://some.url.com" in cypress UI
        cy.log("2:"+ extractedUrl); // logs "2:" in cypress UI
        console.log(extractedUrl); // logs nothing
        cy.visit(extractedUrl);
    });

});

Contrary to what it seems, Cypress commands do not return promises, they return promise-like object and the execution queue is handle by the Cypress framework. That's the main reason some actions that would be logical when handling promises will not work.

More info here: Await-ing Cypress Chains #1417

You could also try cypress-promise, that is a plugin to change cypress promise-like objects into actual promises. More info here: Cypress.io — Using async and await by Nicholas Boll

UPDATE #1:

You could also use alias in order to access the value:

it('should generate correct email', () => {
    const recipientEmail: string = '[email protected]';
    const extractedUrl:string = '';

    // ....scenario generating a email to '[email protected]'.....
    // plugin logs "https://some.url.com" after a few seconds
    cy.task('readMail', recipientEmail).then((result:any) => {
        extractedUrl= result;
        cy.wrap(result).as('extractedUrl');
        console.log(result); // logs "https://some.url.com" in the terminal
        cy.log("1:" + extractedUrl); // logs "1:https://some.url.com" in cypress UI
    });

    cy.get('@extractedUrl').then((extractedUrl) => {
        cy.log("2:"+ extractedUrl); // logs "2:" in cypress UI
        console.log(extractedUrl); // logs nothing
        cy.visit(extractedUrl);
    })

});

like image 97
JDCL32 Avatar answered Sep 30 '22 14:09

JDCL32