HashNuke / hound

Elixir library for writing integration tests and browser automation
http://hexdocs.pm/hound
MIT License
1.36k stars 145 forks source link

Wait for javascript to finish executing on the page #35

Closed jontonsoup closed 9 years ago

jontonsoup commented 9 years ago

I have a test that submits a form via ajax andthen does a page redirect. Is there a way to block until these actions are finished before the next hound command is run? Currently I have to throw a :timer.sleep(5000) into my code to make the tests pass.

I'm using page_source as a poor stand in for expect(page).to have_text("some_text"). If there is a more direct way of doing either of these things, I'd love to know. Thanks! :).

jontonsoup commented 9 years ago

The second half of this is irrelevant now after this PR: https://github.com/HashNuke/hound/pull/36

HashNuke commented 9 years ago

@jontonsoup In an Ember app I had a func to load app and check if Ember loaded every 3 seconds using execute_script. If loaded then return.

HashNuke commented 9 years ago

I think we could have a helper like wait_for_javascript. Not sure if there's anything generic to check and confirm if page is loaded

jontonsoup commented 9 years ago

I thought about it a little more. The problem lies in that page_source returns immediately (without blocking for JS) and the assertion is done after this. 

How does adding a function is_visible_text?/1 sound? It would retry several times before failure like the finders do. 

 I can implement this if you think it makes sense.

On Mon, Jun 29, 2015 at 12:34 AM, Akash Manohar notifications@github.com wrote:

I think we could have a helper like wait_for_javascript. Not sure if there's anything generic.

Reply to this email directly or view it on GitHub: https://github.com/HashNuke/hound/issues/35#issuecomment-116432039

HashNuke commented 9 years ago

@jontonsoup I would look at capybara's implementation of wait_for_javascript instead. That aside, we could have a separate matchers module to use with assert() easily.

jontonsoup commented 9 years ago

@HashNuke I'm thinking of this functionality in capybara: https://github.com/jnicklas/capybara#asynchronous-javascript-ajax-and-friends, which seems to be implimented with a retry mechinism.

I do think this functionality is important as well (https://robots.thoughtbot.com/automatically-wait-for-ajax-with-capybara) but it wasn't the use case I was thinking.

Let me know which one you prefer (I like the former), and I can take a crack at it.

HashNuke commented 9 years ago

@jontonsoup elements are already retried by default. Finders in Hound accept a retry argument in the end to change the default.

I'm open to ideas. Below are some notes:

jontonsoup commented 9 years ago

@HashNuke ah I see, digging deeper into the docs it seems capybara implements this with custom rspec matchers. I assumed it was done with the retry functionality inside capybara itself. https://github.com/jnicklas/capybara/blob/master/lib/capybara/rspec/matchers.rb#L166

IMO seems like we should strive for a couple things:

  1. Abstraction from the implementation detail (the same functions should work regardless of if ajax is on the page)
  2. JS Framework agnostic

My goal is to be able to write this code without knowing the implementation of how the text gets there.

expect(page).to have_text("some_text")

Adding wait_for_X seems counter to the idea of abstracting away from the implementation. It seems like everything should wait (which is currently done via retry).

If we went the custom matcher route as you proposed, how do you see that being implemented under the hood?

HashNuke commented 9 years ago

@jontonsoup Hound provides no matchers right now. With ExUnit you would write something like this

element_text = find_element(:css, ".post") |> visible_text
assert Regex.match?(~r/foo/, element_text)

find_* helpers accept an arg for number of retries - http://hexdocs.pm/hound/Hound.Helpers.Page.html

Adding wait_for_X seems counter to the idea of abstracting away from the implementation. It seems like everything should wait (which is currently done via retry).

Abstracting the waiting increases the time to run tests that will fail since every element will end up being checked for a long time. Adding a wait_for_* would mean that the user knows that there's going to be a delay for the element to appear and can explicitly use it when required.

That's the problem with testing all frontend-heavy apps. When testing a frontend-heavy app, I wrote custom wait functions.

jontonsoup commented 9 years ago

@HashNuke Ah yes, I was using espec notation. Regardless of the notation / matchers, my concern is that feature testing that conflates implementation with user actions can lead to brittle tests, which is my hesitation around explicitly waiting for javascript.

We always try to test from the perspective of the user (preferring user actions over CSS finders etc), which has a long term advantage of more stable tests. If the test is independent from the implementation of the feature, I can switch my feature's implementation while preserving the functionality without having to refactor the tests. This has saved our company countless hours over the years as we've transitioned front end technologies.

Hound does a great job of doing this for clicking / form interaction etc, but is missing the ability to do this for text assertions. I love the idea of having the option to explicitly use a wait for javascript if I need to, but also would prefer for the framework to take care of javascript for me in the majority of simple cases, which would make using wait_for_javascript reserved for exceptional cases.

My main concern is having to use find_element in your example. I should be able to find text without knowing the underlying HTML structure. Although the example works fine with javascript as written, there is no way to write this currently that works with javascript and does not require me to know the underlying implementation.

I'd love to be able to write or something like this

assert visible_on_page?(element_text)

and have this work with javascript.

Capybara allows me to do this very easily. What do you think?

HashNuke commented 9 years ago

@jontonsoup

I should be able to find text without knowing the underlying HTML structure

Go ahead and see if you can implement visible_on_page?/2 in a new module called Hound.Matchers. The second arg being the number of retries, depending on how you implement it.

AFAIK the retries default to 5 in finders, so you can have the same default.

jontonsoup commented 9 years ago

@HashNuke thanks! I'll take stab at it asap.

HashNuke commented 9 years ago

@jontonsoup if you have any other matchers in mind, please do create an issue(s) with todo list if required. I'll pitch in too and implement some if required.

jontonsoup commented 9 years ago

@HashNuke will do. I have another one in mind.

jontonsoup commented 9 years ago

PR here: https://github.com/HashNuke/hound/pull/39

HashNuke commented 9 years ago

Closing issue since PR has been merged.

viniciussbs commented 7 years ago

In an Ember app I had a func to load app and check if Ember loaded every 3 seconds using execute_script. If loaded then return.

@HashNuke Could you share a gist with your Ember-related functions?