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

cypress-promise with Cypress 4.0 / cypress-cucumber-preprocessor #5

Open
jhonyasanuma opened this issue Feb 10, 2020 · 12 comments
Open

Comments

@jhonyasanuma
Copy link

Is cypress-promise still working with Cypress 4.0 and cypress-cucumber-preprocessor?

I tried this simple code:

Given('I do simple request', async function () {
  const foo = await promisify(cy.request('https://www.google.com/'));
  console.log(foo);
});

but received a cypress error:

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

Your callback function returned a promise which never resolved.

The callback function was:

() =>
resolveAndRunStepDefinition.call(
this,
stepDetails,
replaceParameterTags,
exampleRowData,
state.feature.name
)

@NicholasBoll
Copy link
Owner

I upgraded to v4.0.1 and ran the tests in this repository and everything worked. Do you have some reproduction steps? I don't have any experience with the cypress-cucumber-preprocessor. Maybe you could look at the output?

The tests run in this library are here: https://github.com/NicholasBoll/cypress-promise/blob/master/cypress/integration/example_spec.js

@jhonyasanuma
Copy link
Author

Hi @NicholasBoll , thank you for your reply.

I created an exemple project for simplify reproduction steps:
https://github.com/jhonyasanuma/cypress-promise-exemple

Test scenarios are at:
cypress > integration > features > example.feature

Test steps execution (code) are at:
cypress > support > step_definitions > example_steps.js

Cypress execution is same, using npx cypress open / run.

This is the full output of stack trace:

Running: features/exemple.feature (1 of 1)

Exemple project to reproduce issue: #5
1) Execute simples request

0 passing (4s)
1 failing

  1. Exemple project to reproduce issue: cypress-promise with Cypress 4.0 / cypress-cucumber-preprocessor #5 Execute simples request:
    CypressError: cy.then() timed out after waiting '4000ms'.

Your callback function returned a promise which never resolved.

The callback function was:

() =>
resolveAndRunStepDefinition.call(
this,
stepDetails,
replaceParameterTags,
exampleRowData,
state.feature.name
)
at Object.cypressErr (http://localhost:46811/__cypress/runner/cypress_runner.js:86207:11)
at Object.throwErr (http://localhost:46811/__cypress/runner/cypress_runner.js:86162:18)
at Object.throwErrByPath (http://localhost:46811/__cypress/runner/cypress_runner.js:86194:17)
at http://localhost:46811/__cypress/runner/cypress_runner.js:69302:21
at tryCatcher (http://localhost:46811/__cypress/runner/cypress_runner.js:120203:23)
at http://localhost:46811/__cypress/runner/cypress_runner.js:115344:41
at tryCatcher (http://localhost:46811/__cypress/runner/cypress_runner.js:120203:23)
at Promise._settlePromiseFromHandler (http://localhost:46811/__cypress/runner/cypress_runner.js:118139:31)
at Promise._settlePromise (http://localhost:46811/__cypress/runner/cypress_runner.js:118196:18)
at Promise._settlePromise0 (http://localhost:46811/__cypress/runner/cypress_runner.js:118241:10)
at Promise._settlePromises (http://localhost:46811/__cypress/runner/cypress_runner.js:118316:18)
at Async../node_modules/bluebird/js/release/async.js.Async._drainQueue (http://localhost:46811/__cypress/runner/cypress_runner.js:114928:16)
at Async../node_modules/bluebird/js/release/async.js.Async._drainQueues (http://localhost:46811/__cypress/runner/cypress_runner.js:114938:10)
at Async.drainQueues (http://localhost:46811/__cypress/runner/cypress_runner.js:114812:14)

Just ask me if you need any addictional information.

Kind regards,
Jhony

@jhonyasanuma
Copy link
Author

jhonyasanuma commented Feb 10, 2020

I also created a branch with a example without cucumber-preprocessor, it worked!

Just received an error at second scenario using .promisify()

TypeError: cy.request(...).promisify is not a function

Probably I missed any configuration..

Please, if you could make it work with cucumber-preprocessor it will be very appreciated.

Thank you

@NicholasBoll
Copy link
Owner

I was able to reproduce. I think this is related to every issue filed against this library - there is just a mismatch for how Cypress works and promises. I'm trying to figure out how to work around.

Your example gets transpiled into "regenerator" code instead of actual promises:

(0, _steps.Given)('I do simple request', function _callee() {
  var foo;
  return _regenerator["default"].async(function _callee$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          console.log('first');
          _context.next = 3;
          return _regenerator["default"].awrap((0, _cypressPromise["default"])(cy.request('https://www.google.com/')));

        case 3:
          foo = _context.sent;
          cy.log('foo', foo);

        case 5:
        case "end":
          return _context.stop();
      }
    }
  });
});

There is an issue where beforeEach doesn't work which I'm assuming Given uses. I'd have to test more to see if it is how the default babel config works. I don't have any options to disable that transpilation.

@NicholasBoll
Copy link
Owner

I tried without any transpilation (using native async/await by the browser). It doesn't seem to make a difference.

@NicholasBoll
Copy link
Owner

@jhonyasanuma Do you know if you even need this library? Cypress will already wait for previous commands to finish before new commands are run by doing command enqueuing. Updating your example exemple_steps.js to the following works:

import { Given, Then } from 'cypress-cucumber-preprocessor/steps';

Given('I do simple request', function () {
  cy.request('https://www.google.com/').then(foo => {
    cy.log('foo', foo)
  });
});

Then('I check some results', function () {
  cy.request('https://www.google.com/').then(bar => {
    cy.log('bar', bar);
  });
})

await does prevent nesting, but in most cases it isn't too bad. I'm not sure this library will ever work 100% the way one would expect. I'm sure if I had access to more Cypress internals that I could get things to work, but perhaps what would be better is a transpilier than simply rewrote code to be nested .thens instead of runtime hacks? Similar to what https://github.com/MatAtBread/fast-async does...

@jhonyasanuma
Copy link
Author

I was looking for some tool to improve our APIs automation suite, built in node / super test.

Cypress come up with cool features, but the way we work here, using extra abstractions for services and validators layers, unfortunately didn't work well with cypress chained commands.

I achieved simple scenarios using cypress alias (.as) command, but in more complex scenarios became unreadable, plus, without async/await, its add more effort for migration from super test.

Thank you

@NicholasBoll
Copy link
Owner

I think most people don't understand the compositional power of promises and sometimes async/await is a crutch. Code like:

const foo = await getFoo()
const bar = await getBar(foo)
return await getBaz(bar)

Could be written like:

return getFoo()
  .then(getBar)
  .then(getBaz)

Now there has been times I need 2 variables from 2 different promises and await is more convenient for that. I found that doesn't happen as much in Cypress test code as it does when I'm writing scripts.

One of the most powerful features of Cypress chains is the retryability that is lost if you use a .then . A function inside a .then is never retries.

I made an alternative to .then that allows for retryability: https://github.com/NicholasBoll/cypress-pipe

I can understand the migration effort. I'm not sure this library can work 100%. Maybe a code transformer code

@FFdhorkin
Copy link

FFdhorkin commented Mar 12, 2020

await does prevent nesting, but in most cases it isn't too bad. I'm not sure this library will ever work 100% the way one would expect. I'm sure if I had access to more Cypress internals that I could get things to work, but perhaps what would be better is a transpilier than simply rewrote code to be nested .thens instead of runtime hacks? Similar to what https://github.com/MatAtBread/fast-async does...

If it'd be possible to turn cypress-promise into a transpiler, that'd be awesome (and probably safer than what it's doing now). For example:

const foo = await cy.request(...).its('body.foo').promisify();
const bar = await cy.request(...).its('body.bar').promisify();
cy.request(...)
    .its('body')
    .should(({count}) => {
        // do some stuff
    });

getting converted to the following before Cypress sees it:

cy.request(...)
    .its('body')
    .then(({foo}) => {
        cy.request(...)
        .its('body')
        .then(({bar}) => {
            cy.request(...)
                .its('body')
                .should(({count}) => {
                    // do some stuff
                });
        });
    });

This is obviously a toy example, but it'd reduce our max indent level (currently, 9) significantly.

@marcospassos
Copy link

Same problem here working with cucumber. Any workaround to make it work?

@kimgysen
Copy link

Why does Cypress need to re-invent Javascript? Adds zero value wrapping native JS into Cypress-specific api that is unintuitive for developers who have been used to how promises work for a decade. Just all that needless decorator stuff, like seriously.

@BodhiHu
Copy link

BodhiHu commented Apr 30, 2021

sigh... cypress makes simple things complex

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