Now i have such watir code:
require "watir-webdriver"
browser = Watir::Browser.new :ff
browser.driver.manage.timeouts.implicit_wait = 3 #3 seconds
browser.goto "https://page***/default.aspx"
browser.select_list(:name => 'ctl00$tresc$cbListaKrajow').select_value('6')
puts "Selected country"
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').wait_until_present
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').select_value('95')
puts "Selected city"
But main trouble is that ctl00$tresc$cbListaPlacowek is loading it's options only via
ctl00$tresc$cbListaKrajow is changed. It could take 1 or 30 seconds so `browser.driver.manage.timeouts.implicit_wait = 3 #3 seconds` is not a good idea. How can i write that it try to select option unless it is present? Now i get errors like it could find such option...
Note that you could have also done:
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').option(:value => '95').wait_until_present
Just a little nicer to write.
Update:
If you want to wait for any option to appear in the select list you can do:
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').option.wait_until_present
This is technically waiting for a first option to appear. Note that depending on what you are doing, this could give you false positives. For example, if the select list is already populated with options, you trigger some event to re-populate the list, the select list would already have a first option. In this case, you might have to also check that the list of options has changed.
If you want to select an option with a value that matches any in an array, you could do:
browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').options.find do |option|
somearray.include?(option.value)
end.select
Founded by myself:
Watir::Wait.while { browser.select_list(:name => 'ctl00$tresc$cbListaPlacowek').include?('95') }
Related
I am using cucumber and watir. The question is in reference to the code below:
When(/^I click on all 'Show more'$/) do
#browser.links(:class, "more-matches").each do |d|
if d.text == "Show more"
d.click
end
end
end
Now, when the test case reaches this step-definition, the test case is shown as passed without clicking on all the links captured using #browser.links(:class, "more-matches").
The particular code does not get implemented may be because the ajax call has not been completed yet and the array holds zero elements and does not loop through. The code works if I introduce a "sleep 2" at the beginning of this step definition. Can anyone tell me how to handle this case by adding a code so that the ajax call has completed and the array holds all the elements and loops successfully. I have also tried adding the code:
if #browser.execute_script('return jQuery.active').to_i == 0
but it did not work as well.
Kindly suggest a way that the step definition gets executed and does not pass because of empty array.
Using Element#wait_until_present
Usually, you would know how many links should be present. Therefore, you could wait until the expected number of links are present.
When(/^I click on all 'Show more'$/) do
# Wait for the expected number of links to appear
# (note that :index is zero-based, hence the minus 1)
expected_number = 5
#browser.link(:class => "more-matches",
:index => (expected_number-1)).wait_until_present
# Click the links
#browser.links(:class, "more-matches").each do |d|
if d.text == "Show more"
d.click
end
end
end
If you do not know how many links are expected, it makes it a bit more difficult to ensure consistency. However, you might be able to get away with just checking that at least one link is present. Hopefully if one is present, all of the others are present.
When(/^I click on all 'Show more'$/) do
# Wait until at least one link appears
#browser.link(:class => "more-matches").wait_until_present
# Click the links
#browser.links(:class, "more-matches").each do |d|
if d.text == "Show more"
d.click
end
end
end
Using Browser#wait_until
An alternative approach is to use wait_until. The waiting for at least 5 links can be re-written as:
When(/^I click on all 'Show more'$/) do
# Wait for the expected number of links to appear
expected_number = 5
#browser.wait_until do
#browser.links(:class => "more-matches").length >= expected_number
end
# Click the links
#browser.links(:class, "more-matches").each do |d|
if d.text == "Show more"
d.click
end
end
end
I am trying to verify that the text of a link is present in a . The div is not available until a link is clicked. After the link is click more links become available to click. Below is what the html looks like after the click to expose the other links.
<div class="more-links">
First Link
Second Link
Third Link
Fourth Link
Fifth Link
</div>
I want to find make sure the text "Third Link" is there after the div appears.
I have tried, but watir is saying that it cannot find that element.
assert_equal(true, browser.div(:class => "more-links").a(:text => "Third Link"))
I can find the text with this but is really slow and want to specify this particular div
browser.text.include?("Third Link")
Thoughts?
This following code isn't going to return true:
browser.div(:class => "more-links").a(:text => "Third Link")
#=> #<Watir::Link:0x..f99c8864c located=false specifiers={:tag_name=>["a"], :text=>"Third Link"}>
So, the assert_equal is returning false.
If exists? is appended to that line, then it returns true:
browser.div(:class => "more-links").a(:text => "Third Link").exists?
#=> true
browser.text.include?("Third Link")
I can find the text with this but is really slow and want to specify
this particular div
...which is exactly what you did here:
browser.div(:class => "more-links").a(:text => "Third Link")
So in your original line of code, you just need to replace 'browser' with your more specific search:
browser.div(:class => "more-links").a(:text => "Third Link")
Also note, that you would never write:
assert_equal(true, ...)
because you can save yourself some typing and just write:
assert(...)
I am trying to click all links on stackoveflow horizontal menu (Questions, Tags, Users, Badges, Unanswered). I have this code but this clicks on first link (this link is Questions), then prints 1, and after that raises error. What could be problem with this?
require 'watir-webdriver'
class Stackoverflow
def click_all_nav_links
b = Watir::Browser.new
b.goto "http://stackoverflow.com"
counter = 0
b.div(:id => 'hmenus').div(:class => 'nav mainnavs').ul.lis.each do |li|
li.a.click
puts counter += 1
end
end
end
stackoverflow = Stackoverflow.new
stackoverflow.click_all_nav_links
Error message is:
https://gist.github.com/3242300
The StaleElementReferenceError often occurs when storing elements and then trying to access them after going to another page. In this case, the reference to the lis becomes stale after you click the links and navigate to a new page.
You have to store off attributes or the index of the lis first. This will allow you to get a fresh reference to each li after clicking a link.
Try this:
class Stackoverflow
def click_all_nav_links
b = Watir::Browser.new
b.goto "http://stackoverflow.com"
#Store the text of each locate so that it can be located later
tabs = b.div(:id => 'hmenus').div(:class => 'nav mainnavs').ul.lis.collect{ |x| x.text }
#Iterate through the tabs, using a fresh reference each time
tabs.each do |x|
b.div(:id => 'hmenus').div(:class => 'nav mainnavs').ul.li(:text, x).a.click
end
end
end
stackoverflow = Stackoverflow.new
stackoverflow.click_all_nav_links
I'm trying to use the DOM inspector to find the ID of the "More" button at the bottom of this page that reveals more results.
I'm trying to do something like this example:
require 'watir-webdriver'
b = Watir::Browser.new
b.goto 'svpply.com/editors_pick'
#count products
puts b.elements(:xpath => '//li[#data-class="Product"]').count
#=> 30
#Now click button
show_all = b.button(:id => "btn_all")
show_all.click
sleep 4
#count products again
puts b.elements(:xpath => '//li[#data-class="Product"]').count
#=>60
However, I'm unclear on how to search for that particular id within the DOM structure. Can someone also explain the difference between an attribute, element, id, and node?
To use the DOM Inspector for the More button:
Open the DOM Inspector (Ctrl+Shift+I)
Click the [More] button that you want to inspect
On the right side of the DOM Inspector bar, click the [HTML] button
This should show the HTML for the page, which will include the details of the [More] control. You'll notice that the element is actually a DIV not a button. As well that the ID is in the form "_more".
--> This should show the HTML for the page, which will show the details of the [More] control. You'll notice that the element is actually a DIV not a button. As well that the ID is in the form "_more".
So to do your example with the Quora page, you would do something like:
require 'watir-webdriver'
class QuoraPage
def initialize(browser)
#browser = browser
end
def goto()
#browser.goto 'http://www.quora.com/Startups/best_questions'
wait_questions_loaded
end
def click_more()
#browser.div(:id, /_more/).click
wait_questions_loaded
end
def questions_count()
#browser.links(:class, 'question_link').count{ |x| x.visible? }
end
def wait_questions_loaded()
begin
questions_start_count = questions_count()
sleep(2)
end while questions_start_count != questions_count()
end
end
page = QuoraPage.new(Watir::Browser.new :chrome)
page.goto
puts page.questions_count
page.click_more
puts page.questions_count
Note that I had to put the sleeps in otherwise webdriver hangs like anonygoose mentioned. I tried different wait_untils, but did not manage to find something that worked (other than sleep which is not very robust).
Regarding your question about nodes, elements, etc. I think you are best to look at http://www.w3schools.com/dom/default.asp.
To press the button on svpply you can use simply
b.button(:text => "Show All").click
Counting all the products that appear on the page could potentially be done with
b.lis(:class => "grab large").count
This is all for the svpply site. I can't get quora to automate at all, it just stalls my watir-webdriver indefinitely.
You'll also want to wait before you have watir count the products. This can be done with:
b.wait_until{b.lis(:class => "grab large").count > 30}
I'm trying to automate the process of searching for alternative telephone numbers using SayNoTo0870 . Every time one searches for an alternate number or name it brings up the '/companysearch.php' page.
Clearly this page has no reference, and in my mind you can't just link to this page.
What I'm hoping to do is use the code below, to automate the opening of a browser, searching of a name/number, stripping out the HTML and then providing the top 5 results. I've got the automation part down, but clearly when trying to save the webpage using Hpricot it only brings up the 'Sorry nothing can be found page' because I can't link directly to the search result page.
Here is my code thus far:
(I've removed comments to shorten it)
require 'rubygems'
require 'watir'
require 'hpricot'
require 'open-uri'
class OH870
def searchName(name)
browser = Watir::Browser.new
browser.goto 'http://www.saynoto0870.com/search.php'
browser.text_field(:name => 'search_name').set name
browser.button(:name => 'submit').click
end
def searchNumber(number)
browser = Watir::Browser.new
browser.goto 'http://www.saynoto0870.com/search.php'
browser.text_field(:name => 'number').set number
browser.button(:name => 'submit').click
end
def loadNew(website)
doc = Hpricot(open(website))
puts(doc)
end
def strip_tags
stripped = website.gsub( %r{</?[^>]+?>}, '' )
puts stripped
end
end # class
class Main < OH870
puts "What is the name of the place you want?"
website = 'http://www.saynoto0870.com/companysearch.php'
question = gets.chomp
whichNumber = OH870.new
whichNumber.searchName(question)
#result = OH870.new
#withoutTags = website.strip_tags
#result.loadNew(withoutTags)
end
Now I'm not sure whether there's a way of "asking watir to follow through to the companysearch.php page and dump the results without having to pass this page as a variable.
I wonder if anyone has any suggestions here?
With WATIR, minus the extraneous libraries, here's all it takes to accomplish what you've described (using the 'name' test case only). I've pulled it out of the function format since you already know how to do that, and this will be a clearer test case path.
require 'watir'
#browser = Watir::Browser.new :firefox #open a browser called #browser
#browser.goto "http://(your search page here)" #go to the search page
#browser.text_field(:name => 'name').value = "Awesome" #fill in the 'name' field
#browser.button(:name => 'submit').click #submit the form
If all goes well, we should now be looking at the search results. WATIR already knows it's on a new page - we don't have to specify a URL. In the case that the results are in a frame, we do need to access that frame before we can view its content. Let's pretend they're in a DIV element with an ID of "search_results":
results = #browser.div(:id => "search_results").text
resultsFrame = #browser.frame(:index => 1) #in the case of a frame
results = resultsFrame.div(id => "search_results).text
As you can see, you do not need to save the entire page to parse the results. They could be in table cells, they could be in a different div per line, or a new frame. All are easily accessible with WATIR to be stored in a variable, array, or immediately written to the console or log file.
#results = Array.new #create an Array to store our results
#browser.divs.each do |div| #for each div element on the page
if div.id == "search_results" #if the div ID equals "search_results"
#results << div.text #add it to our array named #results
end
end
Now, if you just wanted the top 5 there are many ways to access them.
#results[0] #first element
#results[0..4] #first 5 elements
I'd also suggest you look into a few programming principles like DRY (Don't Repeat Yourself). In your function definitions where you see that they share code, like opening the browser and visiting the same URL - you can consolidate those:
def search(how, what)
#browser = Watir::Browser.new :firefox
#browser.goto "(that search url again)"
#browser.text_field(:name => how).value = what
etc...
end
search("name", "Hilton")
search("number", "555555")
Since we know that the two available text_field names are "name" and "number", and those make good logical sense as a 'how', we can parameterize them and use a single function for both the Search by Name and Search by Number test cases. This is more efficient, as long as the test cases remain similar enough to be shared.