cypress-io / cypress

Fast, easy and reliable testing for anything that runs in a browser.
https://cypress.io
MIT License
47.02k stars 3.18k forks source link

Able to specify timeout limit on cy.wait( function ) #43

Closed wwalmnes closed 9 years ago

wwalmnes commented 9 years ago

In the documentation it says the function usage of cy.wait() runs the function until it does not throw. Obviously, there is a timer that makes sure the function doesn't wait forever, but is it possible to change this timer value?

Is it also possible to limit the rate at which it polls the given function?

Maybe this is an odd request, but in my case I know that I have to wait for at least 5 seconds for a roulette animation, but it may be as long as 10 seconds. I could have just done cy.wait(10000), but then in some cases I will have to wait at maximum 5 unnecessary seconds.

brian-mann commented 9 years ago

To answer your questions:

but is it possible to change this timer value Yes, all DOM based commands support options and you can just pass {timeout: num} as the options.

I just tried this and actually there is a bug with the timeout implementation so I will release a patch for this. What I've written below won't work until this patch is released.

Is it also possible to limit the rate at which it polls the given function? Yes, pass {interval: num} which changes the interval at which it will retry. By default this is 50ms.

Neither of those options are documented and so I'll make myself a note to document them.


However, you shouldn't ever have to explicitly wait as you are doing. The reason cy.wait(num) exists is mostly for debugging purposes.

Instead of explicitly waiting, simply describe the state of your application that it has to be in, in order to proceed.

For instance, after the animation runs, when it completes, what are you trying to assert? Will something appear? Disappear? Will a class be added or removed?

The correct way to write this is by using command options and/or selectors which will simply put Cypress in a holding pattern until the state resolves.

Here's an example. Let's assume after we click the button that some message will animate in and take 5 seconds before its finished. When its finished a class animation-is-done is added to the element.

cy
  .get("button").click() // this causes the animation to happen
  .get("#animation.animation-is-done", {timeout: 10000})
  // now perform additional commands below
  // since our animation is guaranteed to be done

That's all we have to do. Cypress will retry the 2nd cy.get until it finds an element with that id and that class. You don't ever have to explicitly cy.wait.

By default Cypress will timeout a command after 4000ms so that's why we need to pass the timeout and raise this up since the animation could take as long as 10 seconds to finish.

An alternative way to write this is by using Cypress's eventually flag in should.

cy
  .get("button").click() // this causes the animation to happen
  .get("#animation").should("eventually.not.have.class", "animating", {timeout: 10000})
  // now perform additional commands below
  // since our animation is guaranteed to be done

BTW: a 5-10 second animation is extremely long, and all your tests around this will take forever to complete. Perhaps you can either turn off the animations, or simply call into the functions which may run at the end of the animation and just test the state there. Often you don't necessarily have to reproduce every single step leading up like a real user would do, it's fairly easy to just reproduce the state you're trying to test using real methods and functions instead of the interface.

wwalmnes commented 9 years ago

Hi, thanks for the answers :).

Yeah, I assumed wait(num) was more for debugging.

What I'm waiting for is four roulettes to finish their spinning. The roulette is almost like this one here (expect that there are four wheels and more images): http://demo.st-marron.info/roulette/sample/demo.html

They get a status of "spinning" while they're spinning. Absence of "spinning" means the roulette is not spinning.

I initially wanted to do something like

cy
    .get('button').click() // start roulette
    .get('.roulette').wait(function ($target) { // wait for roulette to finish
        expect($target).not.to.have.class('spinning')
    }).then(function () {
        //New text with information is visible
    });;

Your last example with the eventually flag would probably also work.

As you mentioned, it might be too long to wait and I might consider skipping the animation and just test the state.

Will you update this thread when the updates to enable {timeout: num} is available?

brian-mann commented 9 years ago

Yes I will update the thread. I'm trying to finish up a large group of features for 0.7.0, and then I'll fix those bugs. I'll try to finish all of it at this weekend or Monday.

brian-mann commented 9 years ago

I released 0.7.0 today. So tonight I'll start on these bug fixes.

brian-mann commented 9 years ago

I released 0.7.1 tonight which includes the bug fix for setting {timeout: 10000} options in DOM based commands like cy.get, etc.

I haven't yet added in support using the eventually flag with cy.should, so I'll leave this issue open until I make a few more upgrades. Part of these fixes is for a larger upgrade affecting other commands.

Your primary issue should be fixed now though. Make sure you update Cypress and let me know if everything passes.

wwalmnes commented 9 years ago

Works fine now! Thanks!

There's one small issue/request I have with the feedback, though. Is it possible to ignore or hide the failed assertions (as long as the assertion passes before the timeout) ? Or possible just limit the assertions by having one list item specifying error with range 4-x (assuming it's the same test with the same error), and (x+1) would be the assertion that passes.

screen shot 2015-06-22 at 09 13 39

brian-mann commented 9 years ago

Haha. Yah. Those errors should all be grouped under one single failure. I'll make a note and fix that at some point soon.

On Jun 22, 2015, at 3:21 AM, William Almnes notifications@github.com wrote:

Works fine now! Thanks!

There's one small issue/request I have with the feedback, though. Is it possible to ignore or hide the failed assertions (as long as the assertion passes before the timeout) ? Or possible just limit the assertions by having one list item specifying error with range 4-x (assuming it's the same test with the same error), and (x+1) would be the assertion that passes.

— Reply to this email directly or view it on GitHub.

brian-mann commented 9 years ago

Actually can you open a new issue with the error grouping? These are two different things, and once I upgrade .should("eventually"...) to accept options I can close out this issue.

wwalmnes commented 9 years ago

Ok, created a new issue :)

brian-mann commented 9 years ago

Fixed in 0.11.0.

You don't need to provide the timeout options to the assertion, instead just apply the timeout option to the command that precedes the assertion and the assertions will automatically retry until that timeout has been reached.