Combining implicit wait and explicit wait together results in unexpected wait times - ruby

My two scenarios -
1) First
#driver.manage.timeouts.implicit_wait = 30
#wait = Selenium::WebDriver::Wait.new(:timeout => 45) # Time greater than implicit
#wait.until {#driver.find_element(:tag_name => "body").text.include?("hey")}
Which gives the driver 45 seconds to search for the text(which is expected)
2) Second
#driver.manage.timeouts.implicit_wait = 30
#wait = Selenium::WebDriver::Wait.new(:timeout => 5) # Time less than implicit
#wait.until {#driver.find_element(:tag_name => "body").text.include?("hey")}
This now gives the driver 30 seconds to search for the text(not expected)
Is there a way to make selenium wait only for the explicit wait time and not for the greater of the two?
Note - Not declaring the implicit wait time is not an option, cause I cannot afford to let selenium hang each time the driver is unable to find something.
Using Selenium version 30, windows, ff

Don't mix implicit and explicit waits. Part of the problem is that implicit waits are often (but may not always be!) implemented on the "remote" side of the WebDriver system. That means they're "baked in" to IEDriverServer.exe, chromedriver.exe, the WebDriver Firefox extension that gets installed into the anonymous Firefox profile, and the Java remote WebDriver server (selenium-server-standalone.jar). Explicit waits are implemented exclusively in the "local" language bindings. Things get much more complicated when using RemoteWebDriver, because you could be using both the local and remote sides of the system multiple times.
This is how that would work: local code -> Java remote server -> local Java language bindings on the remote server -> "remote" component like the Firefox extension, chromedriver.exe or IEDriverServer.exe. It's even more complex in the grid case, since there could be other hops in between.
Thus, when you try to mix implicit and explicit waits, you've strayed into "undefined behavior". You might be able to figure out what the rules of that behavior are, but they'll be subject to change as the implementation details of the drivers change. So don't do it.
You shouldn't be experiencing "hangs" when an element can't be found if you're not using implicit waits. The driver should throw a NoSuchElement exception immediately.

The best practice is to set implicitlyWait() at the beginning of each test, and use WebDriverWait() for waiting an element, or AJAX element to load.
However, implicitlyWait() and WebDriverWait() do not work well together in the same test.
You would have to nullify implicitlyWait() before calling WebDriverWait because implicitlyWait() also sets the "driver.findElement()" wait time.
Whenever you are using WebDriverWait() with implicitlyWait() already set some initial value, follow the steps -
nullifying implicitlyWait()
executing WebDriverWait (), and return element
reset implicitlyWait() again
Example Java code -
driver.manage().timeouts().implicitlyWait(0, TimeUnit.SECONDS); //nullify implicitlyWait()
WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
element = wait.until(ExpectedConditions.visibilityOfElementLocated(by));
driver.manage().timeouts().implicitlyWait(DEFAULT_WAIT_4_PAGE, TimeUnit.SECONDS);

Related

Last index can't be located

I have a very different problem here.
I have this code
xpath = "//div[#class='z-listheader-content'][normalize-space()='Name']/ancestor::table/../following-sibling::div/table"
array = #browser.table(xpath: xpath, visible: true).rows.map { |row| [row.cells[0], row.cells[0].text] }
col = array.filter_map { |x| x if x[1].eql?(result) }
When I am executing the aforementioned code, it throws the following error
timed out after 10 seconds, waiting for #<Watir::Row: located: false; {:xpath=>\"//div[#class='z-listheader-content'][normalize-space()='Name']/ancestor::table/../following-sibling::div/table\", :visible=>true, :tag_name=>\"table\"} --> {:index=>10}> to be located\""
10 is actually the last index of the table.
But it works fine if I put sleep 5 before my code segment. However, I was expecting WATIR to be automatically waited, but this is not the case, May I know why?
Here is more clear explanation
I am printing this line
p #browser.table(xpath: xpath, visible: true).rows.count
With sleep this is printing 10
without sleep, this is throwing the following error ends with {:index=>11}> to be located\, you could see the full error below
"timed out after 10 seconds, waiting for #<Watir::Row: located: false; {:xpath=>\"//div[#class='z-listheader-content'][normalize-space()='Name']/ancestor::table/../following-sibling::div/table\", :visible=>true, :tag_name=>\"table\"} --> {:index=>11}> to be located\""
Per the Documentation on waiting there are a few key points that should be noted:
The idea behind implicit waits is good, but there are two main issues with this form of implementation, so Watir does not recommend and does not provide direct access for setting them.
The wait happens during the locate instead of when trying to act on the element. This makes it impossible to immediately query the state of an element before it is there.
Implicit waits by themselves will not be sufficient to handle all of the synchronization issues in your code. The combination of delegating waiting responsibilities to the driver and leveraging polling in the code (explicit waits) can cause weirdness that is difficult to debug...
...The second and recommended approach to waiting in Selenium is to use explicit waits...
...Note that Watir does its automatic waiting when taking actions, not when attempting to locate...
In your case the wait is needed for location and thus the "automatic" wait you were expecting is not actually how watir works.
The Watir library does however provide mechanisms for explicit waits:
wait_until which waits until a specific condition is true
wait Waits until readyState of document is complete.
It appears that based on your posted issue that a waiting timeout has expired for your location before the element could be found.
It is possible that wait_until would resolve this e.g.
table = #browser.table(xpath: xpath).wait_until(&:visible?)
puts table.rows.count

How to control test execution speed in watir-webdriver with the Watir::Browser#speed= method? [duplicate]

This question already has answers here:
Is there a way to slow down execution of Watir Webdriver under Cucumber?
(2 answers)
Closed 7 years ago.
Is there any way to control test execution speed in watir? How can I slow down the speed of test execution?
Someone suggested me to use following method browser.speed = :slow but, there is no such method for the Watir::Browser class with the browser driver I'm currently using.
Speed Defaults to Slow
According to the documentation, Watir::Browser#speed= already defaults to :slow. However, as far as I can tell changing this option is only valid for Internet Explorer, and has no effect on other browsers. In fact, with Chrome or Firefox the constructor won't accept a speed argument, and provides no public interface for the option.
However, as with most things Ruby, you can always access the variables directly and tweak them. This may or may not work for your use case, but you could certainly do something like this:
browser = Watir::Browser.new :chrome
#=> #<Watir::Browser:0x..fee868224270c4e1c url="data:," title="data:,">
browser.instance_variable_set :#speed, :slow
#=> :slow
browser.instance_variable_get :#speed
#=> :slow
Other Alternatives
In practice, testing browsers often involves a lot of asynchronous JavaScript events, so you probably want to wait for events or elements rather than trying to slow down the test itself. To do that, you can use explicit or implicit waits.
Implicit Waits
You can add an implicit wait in seconds. For example, to wait up to 30 seconds for each event or element:
browser.driver.manage.timeouts.implicit_wait = 30
Explicit Waits
Watir supports both Watir::Wait#until and Watir::Wait#while. For example, to wait until a login field is visible:
Watir::Wait.until { browser.text_field(name: 'login').visible? }
Use Sleep
Under the hood, Watir is Ruby, so you can also put explicit sleeps into your tests with Kernel#sleep. The main downside to doing this is that it isn't responsive. Your code will sleep for the defined time period even if the event or element you are waiting on triggers or changes earlier. This can make your tests unnecessarily slow.

Watir webdriver - click an element and not wait for load

I have 2 sides in my page, clicking one of the buttons on the left side, refreshes the right side.
Now I want to see whether the site gets stuck if i click the buttons too fast while not letting the right side to fully load.
Right now watir waits for the click command to return, so the test doesnt do what it should:
arr = ["div1", "div2", "div3"]
for i in 1..20
print "#{i}\r"
choise = arr.sample
b.div(:id=>choise).click
end
Any way to make it send the command and return without any delays?
Are you getting the same result in multiple browsers?
The spec (which may or may not be implemented or implemented the same way by the different browsers), says that webdriver prevents other commands from being executed while there are outstanding network requests. Though, it also says it should wait for document.readyState to be present for the frame currently handling the commands, so it is unclear if the outstanding network request is supposed to apply to only the current frame or to all frames.
But since Webdriver is designed for commands to be handled in a synchronous manner, it is likely just not designed to do what you are trying to do.
Not sure but you can try with javascripts:
#browser.execute_script("document.getElementById('choise').click")
b.div(:id => 'choise').exist? - to check exist it or aren't
b.div(:id => 'choise').click
I think titusfortner is right. But look this answer.
begin
Timeout::timeout(10) do
# perform actions that may hang here
end
rescue Timeout::Error => msg
put "Recovered from Timeout"
end

How can I implement wait_for_page_to_load in Selenium 2?

I am new to automated web testing and I am currently migrating from an old Selenium RC implementation to Selenium 2 in Ruby. Is there a way to halt the execution of commands until the page gets loaded, similar to "wait_for_page_to_load" in Selenium RC?
I fixed a lot of issues I was having in that department adding this line after starting my driver
driver.manage.timeouts.implicit_wait = 20
This basically makes every failed driver call you make retry for maximum 20 seconds before throwing an exception, which is usually enough time for your AJAX to finish.
Try using Javascript to inform you!
I created a couple methods that checks via our javascript libraries and waits to see if the page has finished loading the DOM and that all ajax requests are complete. Here's a sample snippet. The javascript you will need to use is just going to depend on your library.
Selenium::WebDriver::Wait.new(:timeout => 30).until { #driver.execute_script("[use javascript to return true once loaded, false if not]"}
I then wrapped these methods in a clickAndWait method that clicks the element and calls the waitForDomLoad and waitForAjaxComplete. Just for good measure, the very next command after a clickAndWait is usually a waitForVisble element command to ensure that we are on the right page.
# Click element and wait for page elements, ajax to complete, and then run whatever else
def clickElementAndWait(type, selector)
#url = #driver.current_url
clickElement(type, selector)
# If the page changed to a different URL, wait for DOM to complete loading
if #driver.current_url != #url
waitForDomLoad
end
waitForAjaxComplete
if block_given?
yield
end
end
If you are using capybara, whenever you are testing for page.should have_content("foo"), capybara will not fail instantly if the page doesn't have the content (yet) but will wait for a while to see if an ajax call will change that.
So basically: after you click, you want to check right away for have_content("some content that is a consequence of that click").

How do you test an Ajax application with Selenium and keep it stable?

Our team has been testing our application with Selenium as it's heavily JavaScript driven we've always had issues with tests occasionally failing. As the number of tests has increased the probability of at least one two tests failing in a complete run has become a certainty.
What we recently figured out is that we probably have a race condition where selenium will click links before the initialization JavaScript has had a chance to attach event handlers to the element that is being clicked. Of course at this point the effects we're looking for don't happen and we get a failing test.
For the time being we've added a slight delay before clicks to give the initialization JavaScript code time to finish, this is obviously a bit hackish, adds time to overall test execution, and doesn't guarantee tests won't still fail so we're looking for a better solution.
The best idea we've come up with so far is to inject a hidden element into the DOM that Selenium can wait for, before firing the click event to know that it's ready. This will be a lot of extra overhead in terms of developer time when we're working our asynchronous events, removing and adding the element. Also it adds extra stuff to our pages that really isn't necessary for the application.
Does anyone have any better strategies? What have you done to effectively solve this problem?
We moved to Selenium 2 (WebDriver) and are using Page Objects pattern with PageFactory/AjaxElementLocatorFactory - an example of this is here
I did exactly like you : add some delay and wait for some elements to be present on the page. And I'm perfectly fine with it. Maybe switching to Webdriver / selenium 2.0 would help though. Test execution can be trimmed down if you work with an in-memory database or sharing the same selenium/selenium server between tests, or even with parallelization (easy with TestNG for instance).
Have you tried the waitForElementPresent command, and then make it click ?
To eliminate race conditions use Selenium's runScript(String initCondition) combined with waitForCondition(String jsConditional, String timeout) methods.
For example, if the AJAX functionality you want to test causes a new element to be added to the dom you can use something like the following.
String jsPoll = "";
jsPoll += "selenium.browserbot.getCurrentWindow()";
jsPoll += ".document.getElementById('DOMID')";
selenium.waitForCondition(jsPoll, "30000");
The condition will evaluate true when the element is added and the method will continue. If your AJAX function swaps elements (ie: one div for another similarly identified div), you can initialize your conditional with something like the following.
String jsInit = "";
jsInit += "!selenium.browserbot.getCurrentWindow()";
jsInit += ".document.getElementById('DOMID').setAttribute('SELENIUMTEST','1')";
String jsPoll = "";
selenium.runScript(jsInit);
jsPoll += "selenium.browserbot.getCurrentWindow()";
jsPoll += ".document.getElementById('DOMID').getAttribute('SELENIUMTEST') != 1";
selenium.waitForCondition(jsPoll, "30000");
The condition evaluates true when the element is swapped out by the AJAX function.

Resources