intercepting the onload event fired by the browser in watir - ruby

I have a unique situation over here. I have a button on a form which produces a popup if there are some errors in the form. [I know this is not good practice, but the developers of the product would not be changing that behavior!] The form navigates to a different page if all the required fields are correctly populated. Now, I need to write a script in order to click the "Submit" button on the form which either might produce a popup or navigate to the next page.
I have the used the click_no_wait on the "Submit" button and handled the popup using AutoIt as per Javascript Popups in Watir. Now, if all the information is valid and the form navigates to the next page, I use a delay in the script by following some of the techniques described in How to wait with Watir. I am using a Watir::wait_until() to wait in the script.
Now sometimes because of some network issues, it takes time to go to the next page (report-generation) page when the form is submitted and thus the script fails because of the timeout value specified in the wait_until.
I was wondering whether there is a way to intercept the onload event of the HTML page in Watir, since the onload event isn't fired until the entire page is loaded. By that way I could have an accurate estimate of the timeout value and not experiment with it. Thus, my script will pass 100% rather than say 98% right now.
Thanks for any help on this topic.

You could try setting up a rescue for the time out, then looping a reasonable amount of times (2 or 3?) if it encounters a timeout.
E.g.
# All your button clicking and autoit stuff here
i = 0
begin
b.wait_until{ # the thing you're waiting to happen }
rescue TheSpecificTimeOutException
# Sorry I can't remember it, the command prompt will tell you exactly
# which one
if i < 3
i += 1
retry
else
raise
end
end
I'm sure i'll have messed something up in the above, or there'll be more concise ways of doing it, but you get the idea. When it times out, give it another few tries before giving up.

Related

Capybara wait until button is enabled?

Surprised I actually haven't come across this, but I have a simple button that is disabled until a dropdown is selected. Sometimes the page isn't fast enough to "enable" the button to be clicked on after the previous dropdown is selected causing it to fail.
I could throw in a "sleep" of a second or two and fix this, but that seems like a lazy/poor way to do this.
Is there a way in capybara (or purely selenium) that I can make it wait until the button is actually enabled? I'd like to throw this is the page model method for this button (as im trying to avoid API specific methods/selenium/etc... in the actual test specs (Although I can if I need to).
FWIW this is specifically for Ruby's capybara framework but pure selenium is fine as well.
Assuming the button you're referring to is actually a button (<button> element, or <input> element with type submit, reset, image, or button) then Capybaras :button selector will (by default) wait for it to be non-disabled.
click_button('Something')
or
find_button('button_id').click
or
find(:button, 'button_value').click
If any of the finder or action methods aren't waiting long enough for a specific element you can always increase the maximum wait time for a specific finder/action by passing a :wait option
find(:button, 'Something', wait: 10).click
If you're not using selector types (if not, why not) and instead are just using raw CSS to locate the element then you can use the :enabled pseudo class along with your existing CSS and something like
find('#my_button:enabled', wait: 10).click
If the element you're calling a button isn't actually a button but some other type of element (<a> etc) styled to look like a button, then you'll need to explain exactly how you're disabling the "button".
In Python you can do something like this:
def wait_until_clickable(driver, xpath, timeout = 1):
while timeout > 0:
try:
element = driver.find_element_by_xpath(xpath)
element.click()
return element
except:
time.sleep(0.1)
timeout = timeout - 0.1
return False

how to reload page until a button appears using capybara and ruby

I want to click a button after an action and button does not appear until and unless I reload the page. And some times it takes some time to appear the button and I have to reload page for more than once. I don't want to put static delays. So is there a way to achieve following using capybara and ruby:
do
page.evaluate_script("window.location.reload()")
until a button appears
While Mesut's code should work fine, I would re-write it as:
Timeout.timeout(Capybara.default_max_wait_time) do
loop do
page.evaluate_script("window.location.reload()")
break if page.has_selector?(...)
end
end
This will make sure to fail if it will have to wait more than timeout defined in Capybara settings. It can be useful when for example specs are running on the CI server.
Be aware that it can still lead to unexpected behaviors in some drivers, because it can interrupt while some scripts are evaluating.
reoload until page.has_selector? returns true, check this:
while true
page.evaluate_script 'window.location.reload()'
if page.has_selector?("css_selector")
break
end
end

Is waiting needed in Capybara for non DOM-altering Javascript?

From what I understand of Capybara's autowait feature, it waits for the DOM to change, so I guess it's waiting for all AJAX requests to complete and/or the main frame to finish loading.
I have a test case in which I need to:
fill in a field (name_1)
click a save button
check the object is saved properly
When I fill in the name_1 field, some JS from the app I'm testing fills in other mandatory fields automatically (notably, link_rewrite_1).
Problem is, when I check for the other field to be actually filled, it is not!
I'm thinking the JS that fills the other fields has not had time to complete when the test is run, and the test doesn't wait since there is no AJAX call pending.
Here is the relevant code snippet:
fill_in 'name_1', :with => 'Bob' #some javascript should run and fill link_rewrite_1
find('#link-Seo').click
page.should_not have_field('link_rewrite_1', with: "")
In plain English, I fill in name_1 and expect link_rewrite_1 not to be empty.
I've tried to insert a sleep 1 before the last check, but it doesn't change anything. Since sleep will pause the execution of the entire ruby thread, maybe it just freezes webkit and changes nothing, I don't know.
Am I right in suspecting a timing issue or is there likely something else going on here?
After further debugging, it seems like it is a driver issue. Capybara-webkit just doesn't execute my JS correctly. Works fine with selenium-webdriver.

Watir and Ajax requests

In my webapp I have a simple textfield. To this textfield I have a jQuery function which will be always executed on every keyup. With this function there is an Ajax request assigned which loads every time the result of the SQL-Query. My code is equivalent to the code of
RailsCasts. Now I'm testing my webapp with Selenium. With this line of code
browser.text_field(:id => 'textfield').set("Search text")
the text will be written and the content will be changed. After it should click on a link which is placed on the dynamic content with this code
browser.a(:id => "link").click
The problem now is that the click event won't be executed. Has somebody an idea what the problem could be? Or maybe an example with Watir and Ajax?
Without an example site to test against it's hard to be sure but I will throw out a few potential solutions for you
If the client side javascript is looking for onkeyup events, you may need to fire one after setting the contents of the field. You can do that via the .fire_event method
You could just be looking at a timing issue. If the nature of the link is changing as a result of the input, it's possible that Watir is firing off the two comments in rapid succession and the client side code is still in the midst of doing it's thing (especially if there is back and forth between the jquery code and the webserver that as happening as that also induces networking delays. You may need a brief sleep between commands (brute force) or to wait for a particular element to assume an expected state (a little more work but also a bit more robust and not subject to breaking is the delay exceeds your sleep duration)
I'd suggest executing the commands manually via IRB (you could just cut and paste from your script as needed) to be able to watch the state of the browser, and note any delay in updating the screen/DOM after a particular action. If stuff works properly in IRB but not when executed via a script it's often taken as confirmation of a timing issue.
If this is the original Watir/Firewatir I would try getting it to hover over the link before it attempts to click it.
I've had this problem previously with links that appear after typing into an "autocomplete" field (i.e. it attempts to guess at the text you want by filtering down from a huge list of possibilities).
Try this:
browser.wait_until{browser.link(:id => "link").present?}
browser.link(:id => "link").fire_event("onmouseover")
browser.link(:id => "link").click
If it works, try it without the .fire_event("onmouseover"), because it could just be that it's trying to click the link before it's visible and failing to do so.
If this is actually a question regarding "Selenium" (wrongly labelled Watir) then ignore all of the above, because I that is an application I've never used.
you can use capybara method.
click_link("link")
for ajax set :js => true in you test case
http://opinionatedprogrammer.com/2011/02/capybara-and-selenium-with-rspec-and-rails-3/

Using Watir on Peoplesoft App: each text field reloads the page

I'm using Watir 1.6.7.
I'm working on developing some regression tests for a PeopleSoft App using Watir and Cucumber. I have run into a few issues with forms in the application.
First, when entering a value into a text_field, the page refreshes when the user clicks outside the text_field. Waiting for the next text_field element to exist is problematic because it may locate the element before the page reloads, or after the page reloads as expected. Increasing the wait time never feels like a good solution, even though it "works".
The second issue is that the page refresh is not triggered until the user clicks outside the current field. In this case, that happens when the script tries to access the next text_field to be populated. One solution here would be to send a or keystroke, but I can feel the script becoming more brittle with every addition like this.
Are there any other approaches that would be less brittle, and not require 2-3 extra commands in between each text_field action?
The play-by-play looks like:
Browser navigates to page that contains the form.
Browser fills in first form field. (fix: send keystroke to cause page refresh, wait_until second field is visible again)
Browser selects the second form field to be filled out. (again, keystroke & wait_until)
Page refreshes, script fails. (resolved)
Browser selects the third form field...
The application started exceeding the 5 second sleep duration, and I did not want to increase the wait time any longer. I wanted to see what would happen if I populated the text field faster using "element.value =" rather than character by character with "element.set ".
This change completely resolved all complications. The page no longer refreshes when entering text, and no long requires a send_keys statement to use TAB or ENTER to move to another field. The form is storing all of the data entered even though there are no refreshes or state saves between fields.
Previous method:
def enter_text(element, text)
element.set text
#browser.send_keys("+{TAB}")
sleep 5
Watir:Wait.until { element.exists? }
end
New method:
def enter_text(element, text)
element.value = text
end
Firstly, there are interesting Wait methods here: How do I use Watir::Waiter::wait_until to force Chrome to wait?
Overall, I don't quite understand your problem. As I understand it your script is working. If you could be a bit clearer about your desires compared to what you already have that would help, as would some sample source code.
If you're looking for ideas on custom waiting you could check for changes in the HTML of your page, form or text field. You could check that the text field is .visible?. You could try accessing the next text_field (clicking it, or setting the value for example), then catch the exception if it can't find the text_field and retry until it doesn't break, which would solve both your problems at once.
Why would clicking outside the current field be a bad solution? Do you absolutely need the next step to be a text_field access? I haven't gotten my head around how the next field only exists when you click outside the current field, but you cause this refresh by accessing the next field.
Edit: Most welcome, and thank you for clearing that up, I think I now understand better. If you allow Watir to invoke its page wait, or force it to, then it will wait for the refresh and you can then find the new text_field. Keystrokes do not invoke ie.wait, so if you send a single keystroke, then invoke a wait then the rest of your script will be responding to the post-refresh state.
I highly recommend the OpenQA page on waiting in Watir. If what you're doing to invoke the refresh does not appear on the list of things that invoke Watir page waits then you need to invoke your own page wait... but you need to do it before the page refreshes, so the cause of the refresh should end before the end of the refresh itself.
I don't know peoplesoft's app well enough to know this, but Does the app display anything for the user while it's processing.. like some kind of little 'loading' graphic or anything that you might be able to key off of to tell when it's done?
I've seen apps that do this, and the item is just an animated gif or png and it is displayed by altering the visibility attribute of the div that contains the graphic. In that instance you can tell if the app is still loading by using the .visible? method on that element and sleeping for a while if it's still there.
for the app I'm testing (which has one of those 'icons') I created a simple method I called sleepwhileloading. all it that is does is use a one second sleep wrapped in a while loop that looks to see if the loading icon is visible. works like a charm

Resources