-
Notifications
You must be signed in to change notification settings - Fork 3
Wait States
The asynchronous nature of browsers makes it difficult to write up a script that only does certain actions when a page is "loaded." This is particularly true in single page applications, where the page is always loaded. To avoid brittle scripts, you should explicitly wait for elements to appear or for DOM changes to occur before trying to interact with the page. Both of these techniques are highly preferable to adding sleep
statements that need constant adjustment.
Symbiont provides a wrapper around watir-webdriver and selenium-webdriver. That means a blocking API is automatically provided.
You can handle wait states by using explicit helper methods. The default timeout for all these methods is 30 seconds, but you can pass an argument to any of these to increase or decrease that timeout, if you feel it is warranted.
If you are using Symbiont's page object pattern, let's say you have an element declaration like this:
div :loading, id: 'loading'
With that in place, you could provide a wait state for that element like this:
@page.loading.wait_until_present
@page.loading.wait_while_present
The wait_until_present
would wait for the div element called "loading" to appear before doing anything else. The wait_while_present
would wait for the div element to disappear before doing anything else.
Do note that that this works even if you are not using the page object pattern. You can call this method on the @browser
object itself, as such:
@browser.div(id: 'loading').wait_until_present
@browser.div(id: 'loading').wait_while_present
You can also take action on a given element while first waiting for the element to be available. Let's say you have a button that you want to click declared as an element declaration like this:
button :submit, id: 'submit'
However let's say the button only appears after a certain period of time. So you have to wait until the button is present before clicking it. You can do that as such:
@page.submit.when_present.click
@page.submit.when_present(2).click
The first line waits for the button to be present using the default timeout of 30 seconds. The second line would only wait two seconds for the button to appear. As with the previous examples, you can make these calls directly on the @browser
object itself.
@browser.button(id: 'submit).when_present.click
@browser.button(id: 'submit').when_present(2).click
You can also use a block with the when_present statement if you want to do more actions. For example, let's says you have a div that you've called "container". You can do this:
@page.container.when_present { |div| div.button.click }
Here you are passing a block. The parameter |div| will contain the object reference of "container". You can then perform any actions you want in the context of that object reference.
Sometimes you my find it useful to simply call out to Watir directly. There are two ways you can do that:
Watir::Wait.until { ...arbitrary predicate... }
Watir::Wait.while { ...arbitrary predicate... }
Here are two examples of how that might be used:
Watir::Wait.until { @page.text.include? 'Submitted' }
Watir::Wait.while { @page.loading_spinner.exists? }
As an alternative, you can use the WebDriver's implicit waits to specify a maximum time (in seconds) that your script will try to find an element before timing out. This is done by setting the property of the underlying driver:
@browser.driver.manage.timeouts.implicit_wait = 10
Note that using implicit waits can make your tests slower and more difficult to understand when they fail.
Progression
- Framework Philosophy
- Models and Drivers
- Basic Watir Script
- Basic Symbiont Script
- Basic Page Definition
- Basic Context Factory
API
Waiting
Data
Elements
Techniques