question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Await-ing Cypress Chains

See original GitHub issue

Current behavior:

await-ing cypress chains yields undefined

Desired behavior:

await-ing cypress chains yields the value of the chain (since the chain is a promise-like, it should work with await out of the box)

Additional Info (images, stack traces, etc)

I understand the recommended way to use cypress is closures, however, when dealing with multiple variables, we run into callback hell’s lil’ brother closure hell.

The ability to use await on Cypress chains could be beneficial in many ways:

  • avoiding nesting closures many levels deep, thus preventing unreadable code
  • making it easy for programmers familiar with traditional async patterns to start using Cypress

Example code using closures

describe('Filter textbox', () => {
    beforeEach(() => {
        cy.get(test("suplovaniTable")).as("suplovaniTable");
        cy.get(test("filterTextbox")).as("filterTextbox");
    });

    it('changes data on filter text change', () => {
        cy.get("@suplovaniTable").then((el) => {
            return el[0].innerHTML;
        }).then((innerHTML) => {
            return sha256(innerHTML);
        }).then((hash) => {
            cy.get("[data-test=suplovaniTable] > tbody > :nth-child(1) > :nth-child(2)")
                .then((tds) => {
                    const text = tds[0].innerText;
                    cy.get("@filterTextbox").type(text);
                    cy.get("@suplovaniTable")
                        .then(el => el[0].innerHTML)
                        .then((innerHTML) => {
                            expect(hash).to.not.equal(sha256(innerHTML));
                        });
                });
        });
    });
});

Example code using await

describe('Filter textbox', () => {
    beforeEach(() => {
        cy.get(test("suplovaniTable")).as("suplovaniTable");
        cy.get(test("filterTextbox")).as("filterTextbox");
    });

    it('changes data on filter text change', async () => {
        const table = await cy.get("@suplovaniTable");
        const hash = sha256(table[0].innerHTML);

        const filterCell = await cy.get("[data-test=suplovaniTable] > tbody > :nth-child(1) > :nth-child(2)");
        await cy.get("@filterTextbox").type(filterCell[0].innerText);

        const newTable = await cy.get("@suplovaniTable");
        const newHash = sha256(newTable[0].innerHTML);
        expect(hash).to.not.equal(newHash);
    });
});
  • Operating System: Windows 10 x64
  • Cypress Version: Beta 2.1.0
  • Browser Version: Chrome 64

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:407
  • Comments:79 (31 by maintainers)

github_iconTop GitHub Comments

110reactions
brian-manncommented, Mar 6, 2018

Cypress commands are not 1:1 Promises.

https://gitter.im/cypress-io/cypress?at=5a9ec8bf458cbde55701c7a8

They have promise like features in the sense that they have a .then(fn) and they can assume other thenables and do the right thing, but they themselves are not true Promises.

This has serious implications where the async/await implementation would simply not work correctly in Cypress.

What is possible - is to use async/await to replace the .then(fn) but even that has problems.

You could not use try / catch because that is the same thing as .catch() for Promises, which Cypress does not and will never have.

Because Cypress enqueues commands on a master singleton, it already manages the async coordination for you. It’s impossible to ever lose a chain of commands.

What this means is - you would sometimes add the await keyword when you need to work with the yielded value of a command - but then mostly not do this.

This also means you wouldn’t use the async keyword on functions that return Cypress commands.

This IMO would end up being confusing. You would use async/await inconsistently from how you’d use it outside of Cypress. It would require a lot of explanation in the docs.

FWIW you can already avoid callback hell in Cypress by writing much more terse JS. There is almost never a situation where you ever would need to create nested callbacks, or ever need to make heavy use of const.

describe('Filter textbox', () => {
  beforeEach(() => {
    cy
    .get(test('suplovaniTable')).as('suplovaniTable')
    .invoke('html')
    .then(sha256)
    .as('hash')

    cy.get(test('filterTextbox')).as('filterTextbox')
  })

  it('changes data on filter text change', () => {
    cy
    .get('@suplovaniTable')
    .find('tbody > :nth-child(1) > :nth-child(2)')
    .invoke('text')
    .then((text) => {
      cy.get('@filterTextbox').type(text)
    })

    cy
    .get('@suplovaniTable')
    .invoke('html')
    .should(function ($html) {
      expect(this.hash).to.not.equal(sha256($html))
    })
  })
})
61reactions
yannicklerestifcommented, Nov 8, 2018

+1 Protractor / Selenium are deprecating their old specflow (that works just like Cypress) and replacing it with a native promise based one. In my previous assignment, we migrated our tests to use async / await and really makes things way clearer. Using Cypress now and it’s clearly an improvement over protractor / selenium on many topics, but this is a big disappointment.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Cypress.io — Using async and await | by Nicholas Boll | Medium
Async/await makes it much easier to unwrap values, but Commands are not Promises. Using await on a Cypress chain will not work as...
Read more >
then | Cypress Documentation
If the return value is a chain of Cypress commands (eg return cy.get('button') ) ... .then() can time out waiting for a promise...
Read more >
Introduction to Cypress - Cypress Documentation
To match the behavior of web applications, Cypress is asynchronous and relies on timeouts to know when to stop waiting on an app...
Read more >
Understanding the Asynchronous nature of Cypress
This means you cannot use things like async/await within your Cypress tests. ... that to resolve before continuing forward through the chain of...
Read more >
Cypress.Promise
Waiting for Promises. it('waits for promises to resolve', () => { let waited = false function waitOneSecond() { // return a promise that...
Read more >

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found