Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"Your callback function returned a promise which never resolved" error with cy.queryByText() #37

Closed
vfernandestoptal opened this issue Feb 21, 2019 · 7 comments

Comments

@vfernandestoptal
Copy link

  • cypress-testing-library version: 2.3.6
  • node version: 10.12.0
  • npm (or yarn) version: 6.4.1

I have a few tests checking that an elements doesn't exist using a command like

cy.queryByText("xxxxxxx", { timeout: 300 }).should("not.exist");

Usually, everything is fine. But, occasionally, I get a failure like this:

     CypressError: cy.then() timed out after waiting '400ms'.

Your callback function returned a promise which never resolved.

The callback function was:

function Command__queryByText(thenArgs) {
              return commandImpl(thenArgs.document)
            }
      at Object.cypressErr (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:65283:11)
      at Object.throwErr (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:65248:18)
      at Object.throwErrByPath (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:65275:17)
      at http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:54007:21
      at tryCatcher (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:127195:23)
      at http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:122505:41
      at tryCatcher (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:127195:23)
      at Promise._settlePromiseFromHandler (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:125213:31)
      at Promise._settlePromise (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:125270:18)
      at Promise._settlePromise0 (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:125315:10)
      at Promise._settlePromises (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:125390:18)
      at Async._drainQueue (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:122119:16)
      at Async._drainQueues (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:122129:10)
      at Async.drainQueues (http://xxx.xxx.xxx:8084/__cypress/runner/cypress_runner.js:122003:14)
      at <anonymous>

I nailed it down to the timeout on https://github.com/kentcdodds/cypress-testing-library/blob/master/src/index.js#L53 , and everything seems to work if we remove the timeout option.

Since waitForElement is already called with a timeout option on https://github.com/kentcdodds/cypress-testing-library/blob/master/src/index.js#L24 , is it needed on the cypress command chain? Could it be removed from that line 53?

Sorry for not providing a code sample, but I have not been able to reliably reproduce it.

@sompylasar
Copy link
Collaborator

I remember this piece of code, I think I wrote it. At that time I never used "should not exist" and I wonder why does it not take care of the timeout inside Cypress. The timeout was originally meant to wait for the element to exist.

@sompylasar
Copy link
Collaborator

Nope, at the time of writing there was no timeout ef22f87#diff-1fdf421c05c1140f6d71444ea2b27638R20

The timeout appears later ec3b712#diff-1fdf421c05c1140f6d71444ea2b27638R43

CC @npeterkamps

@vfernandestoptal
Copy link
Author

vfernandestoptal commented Feb 23, 2019

Removing the timeout option on the call to then() will cause an issue when specifying a timeout value larger than the default command timeout.

I think the original problem happens when waitForElement takes longer than 100ms to queue and execute the timeout code, but I can't figure out a way to avoid or recover from that.

Adding more than 100ms might make the issue less frequent, but it is not a sure way to avoid it :(

@alexkrolick
Copy link
Contributor

alexkrolick commented Sep 17, 2019

Is this still an issue after the changes to retries in #78 ?

@jagregory
Copy link
Contributor

Still an issue.

In fact, since moving to 5.0.0 I'm seeing these failures when I wasn't before. Unhelpfully, I now no longer which command actually failed.

CypressError: cy.then() timed out after waiting '4000ms'.

Your callback function returned a promise which never resolved.

The callback function was:

thenArgs => {
        const getValue = () => {
          const value = commandImpl(thenArgs.document);
          const result = Cypress.$(value); // Overriding the selector of the jquery object because it's displayed in the long message of .should('exist') failure message
          // Hopefully it makes it clearer, because I find the normal response of "Expected to find element '', but never found it" confusing

          result.selector = `${queryName}(${queryArgument(args)})`;

          if (result.length > 0) {
            consoleProps.yielded = result.toArray();
          }

          return result;
        };

        const resolveValue = () => {
          // retry calling "getValue" until following assertions pass or this command times out
          return Cypress.Promise.try(getValue).then(value => {
            return cy.verifyUpcomingAssertions(value, waitOptions, {
              onRetry: resolveValue
            });
          });
        };

        if (queryRegex.test(queryName)) {
          // For get* queries, do not retry
          return getValue();
        }

        return resolveValue().then(subject => {
          // Remove the error that occurred because it is irrelevant now
          if (consoleProps.error) {
            delete consoleProps.error;
          }

          return subject;
        });

@NicholasBoll
Copy link
Contributor

I'm a little late to the party. I introduced some changes to the way these Cypress Custom commands work. Is this still an issue in 5.2.1?

I know for sure the original error shouldn't happen, because the .then was removed in #108 (lines here: https://github.com/testing-library/cypress-testing-library/pull/108/files#diff-1fdf421c05c1140f6d71444ea2b27638L75-L77)

In fact, since moving to 5.0.0 I'm seeing these failures when I wasn't before. Unhelpfully, I now no longer which command actually failed.

Hopefully this is no longer an issue. That's what #108 sought to address - better debugging of failures.

My recommendation is to change all query* to find*. query* commands do not retry until success like find* queries do and like all other built-in Cypress commands. They will pass or fail almost instantly. They also can be a little flaky because technically in @testing-library/dom query* commands are synchronous and all Cypress commands are asynchronous. query* queries try to mimic synchronous behavior by executing asap and not retrying. If your application is a bit slower for whatever reason, query could fail.

The changes introduced in 5.2.1 could fail where previously they passed, but not because the previous logic was correct, but because query* never failed if it didn't find what you were looking for. If query* failed to find an element, it would return an empty object. So the following would never fail before but does now:

cy.queryByText('Does not exist').should('exist')

@kentcdodds
Copy link
Member

I think we're safe to close this now. If this is still a problem for anyone please open a new issue. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants