appfolio / ae_page_objects

Page Objects for Capybara
MIT License
28 stars 9 forks source link

pk - catch errors in poll_until to prevent errors from being raised without waiting seconds_to_wait #193

Closed pkmiec closed 8 years ago

pkmiec commented 8 years ago

Capybara has a method called Capybara::Base#synchronize. This method waits a certain amount of time for a block to be executed without exceptions. It also rescues common exceptions and automatically retries them. All in the name of more stable tests.

AePageObjects includes a helper method #wait_until which waits a certain amount of time for a block to return something truthy.

It also includes the method #poll_until (which builds on #wait_until) to quickly poll the browser for the truthy answer. It does this with Capybara.using_time_out(0). This method is currently only used internally in AePageObjects to implement window.change_to behavior and element proxy behavior.

The currently implementation (https://github.com/appfolio/ae_page_objects/blob/c17d020c57208f8f40f4925f883c5e6aa4282a0e/lib/ae_page_objects/util/page_polling.rb#L6), however, can lead to flaky tests. The problem lies with the use of Capybara.using_time_out(0), which causes Capybara::Base#synchronize to not rescue any common exceptions (and instead letting them be raised). This means that window.change_to can raise exceptions instead of retrying to evaluate its conditions until one of them evaluates to true.

This problem can be seen in the other usage of #poll_until in element proxy (https://github.com/appfolio/ae_page_objects/blob/c17d020c57208f8f40f4925f883c5e6aa4282a0e/lib/ae_page_objects/element_proxy.rb#L142). Note, how #reload_element tries to catch and retry Selenium::WebDriver::Error::StaleElementReferenceError.

This PRs improves #poll_until method to rescue and retry common errors (much like Capybara::Base#synchronize). This allows to wait the appropriate amount of time for the page / browser to "settle" down and allow us to evaluate the conditions properly.

This PRs also introduces #poll method as a safer alternative to the following pattern,

Capybara.using_wait_time(0) do
  po.has_content?('Admin')
end 

This is commonly used to try to determine whether a page is in a certain state (without having to wait for that state to become true) in order to switch page object functionality. For example, accessing the main nav actions on mobile may be slightly different then accessing nav actions on desktop. The page object can abstract these differences from the actual tests but it needs a way to quickly determine which version of the page we're using.

pkmiec commented 8 years ago

I already ran few builds with these changes. Although it is difficult to tell whether the flakiness is any different, there are no obvious problems.