Iterating through the options of a drop down - ruby

I'm using Selenium in Ruby ( a language that I am currently learning) and I have a drop down menu that I want to iterate though, select each option, do some stuff, and then move onto the next option.
I have looked at several answers that are somewhat similar. Only one Stack Overflow question had to similar idea in mind as mine but it's in Python and I just don't know the syntax for Ruby.
I have read through the documentation for Ruby and haven't found anything that does anything similar to the Python way.
Essentially what I want to do is:
select first option
click a button
navigate to a different page
download a csv
return back to the previous page
select second option
do the same thing
etc...until all the options are done
Is this possible? I can figure out returning to the previous page and clicking the csv option but I would like some help on the syntax part.
Thank you

The ruby bindings for selenium-webdriver have a Select class for manipulating select lists.
Here's a contrived example that locates a select_list element, passed the element to a Select object, and prints the text of each option in the list. YMMV...
require "selenium-webdriver"
driver = Selenium::WebDriver.for :firefox
driver.navigate.to "https://www.seleniumeasy.com/test/basic-select-dropdown-demo.html"
element = driver.find_element(id: 'select-demo')
select_list = Selenium::WebDriver::Support::Select.new(element)
select_list.options.each { |option| puts option.text}
#=> Please select
#=> Sunday
#=> Monday
#=> Tues
...

Related

How can I get Watir to make a fresh reference on a non-stale element?

A portion of some tests I am writing calls for checking if an option gets removed from a select list once that option has been used. I am inconsistently getting this error: timed out after 60 seconds, waiting for {:xpath=>"//select[#id = 'newIdentifierType']//option", :index=>31} to be located (Watir::Exception::UnknownObjectException)
It causes my test to fail maybe 2-3 times out of 10 runs and seems kind of random. I think Watir is looking for the "old" select list with this ID since it caches the element and may also include that it had 32 items, but it times out since a select list with this ID and 32 items no longer exists. The new select list has the same ID but only 31 items.
Is there a way to always get a new reference on this element even though it's not technically going stale? Am I experiencing this problem due to a different issue?
My current code for getting the options in the select list:
#browser.elements(:xpath => "//select[#id = 'newIdentifierType']//option")
I am using Ruby/Cucumber with Selenium and Watir Webdriver level. I first tried defining the element as a select_list in a page-object but moved it to the step definitions using #browser.element to see if that would stop the timeout. I thought it may ignore Watir's cached elements and get the most current one with the ID, but that does not appear to be the case.
Please avoid using XPath with Watir. Everything you can do with XPath, Watir has a much more readable API to handle.
To check for a specific option not being there, you should avoid collections and locate directly:
el = browser.select_list(id: "newIdentifierType").option(value: "31"))
# or
el = browser.select_list(id: "newIdentifierType").option(text: "This one"))
Then to see if it has gone away:
el.stale?
# or
el.wait_until(:stale?)
That won't test the right thing if the entire DOM has changed, though, so you might need to just relocate:
browser.select_list(id: "newIdentifierType").option(text: "This one")).present?
If you are intent on using a collection, the correct way to get the list of options is:
options = #browser.select(id: 'newIdentifierType').options
el = options.find { |o| o.text == 'This one' }
# Do things
el.stale?

Unable to click link using selenium webdriver

I'm trying to get an automated Google search to click on the first link. So far I have not been successful and was wondering if someone could assist me. The search results populate, although the act of clicking the first link fails every time.
require "selenium-webdriver"
driver = Selenium::WebDriver.for :firefox
driver.navigate.to "http://google.com"
element = driver.find_element(:name, 'q')
element.send_keys "translate"
element.submit
resultlink = driver.find_Element(:link, "Google Translate")
resultlink.click
How about you try locating the First link using CSS selectors, something like this:
driver.find_element(:css, "#rso li:nth-child(1) div > h3 > a").click
where the 1 in the brackets (after nth-child) refers to the first search result.
Also I may be wrong, but try :link_text instead of :link, something like this :
resultlink = driver.find_element(:link_text, "Google Translate")
resultlink.click
If you watch this while it's happening, you might notice that it's failing before the results load. This is probably the single most annoying aspect of automation: timing.
I tried adding sleep(5) before defining the element and it worked. However, sleeps are generally bad, so you should instead give selenium a little leeway to find the element before deciding it doesn't exist. You do this through implicit waits. For example:
driver.manage.timeouts.implicit_wait = 5 #time in seconds
This sets the maximum time that selenium will allow for an element to load. If it finds it sooner, it will continue right away. For this reason, it is far more efficient than sleep. More info is available in the documentation. Set this any time before you need to find your element. Once set, this will apply for the remainder of your test. It's a good idea in general to allow for slight delays and/or network hiccups.
First, if you are learning Selenium, don't use any of the Google pages to start with. They look simple but are extremely tricky and complex under the hood. Find another website to automate please. It is against Google's user agreement anyway.
Then I can provide you working code. Note Google search results may render differently in different browsers, and you also need to use WebDriverWait for waiting.
require 'selenium-webdriver'
driver = Selenium::WebDriver.for :firefox
driver.navigate.to "http://google.com"
element = driver.find_element(:name, 'q')
element.send_keys "translate"
element.submit
wait = Selenium::WebDriver::Wait.new(:timeout => 10)
wait.until {
driver.find_element(:css , 'h3 > a')
}
# click first result
# driver.find_element(:xpath , '(.//h3/a)[1]').click
results = driver.find_elements(:css , 'h3 > a')
results.each { |result|
if result.attribute('textContent') == 'Google Translate'
result.click
break
end
}
(.//h3/a)[1] means the first result. In Firefox, results don't have unique data-href for identifying, so you need to use index.
Otherwise, you can loop through all result links for a link that its attribute textContent equals Google Translate. Note the link text for it is actually Google <em>Translate</em>, so using text() in XPath might not work.
If you find the solution above is too much to take in, it proves you shouldn't start learning Selenium using Google pages in the first place. ;)

Translating jQuery to Watir

It's entirely possible that I'm missing something fundamental, but this is a new realm for me and I could use some pointers. I'm getting started using Ruby and Watir to drive/test a web application that's all AJAX-built. Many of the items don't have explicit classes/ids, and the dev team of course uses jQuery to get to them. I'm looking for a way to translate their jQuery into Watir to use/modify/check values of the same objects.
For example, they use this to see if there are values in a data grid's fifth column:
$("div.dataTable table tbody tr").has("td:eq(4):not(:empty)").length > 0
How would I go about doing something similar?
You could make the same check in Watir using:
#Get the rows of the table (assuming there is just one dataTable)
table_trs = browser.div(:class, 'dataTable').table.tbody.trs
#Find how many rows have data in the 5th cell
# Note that both jQuery and Watir are 0-based index (ie 4 means 5th cell)
rows_with_data = table_trs.count{ |tr| tr.td(:index, 4).text != '' }
#Do your comparison
rows_with_data > 0
You can write it all as one line, but I broke it up here for readability.
You could also use Pincers. It's a small ruby gem, like Watir, but offers an API similar to jQuery on top of Webdriver.
Example:
require('selenium-webdriver')
require('pincers')
driver = Selenium::WebDriver.for :firefox
pincers = Pincers.for_webdriver driver
pincers.goto 'www.somesite.com'
pincers.css('a#link-id').click
(Disclosure: I work at Platanus.)

Extracting text from a webtable in Watir / Ruby

I'm trying to extract the data from an Income Statement, url is http://finance.yahoo.com/q/is?s=LMT+Income+Statement&annual
I was unable to find the table using the browser.table(:name, 'blah') or (:id, 'blah'), but had some luck using the xpath with Nokogiri using this code, which picks up after I've initialized everything and browsed to the page:
page_html = Nokogiri::HTML.parse(browser.html)
tobj = page_html.xpath('//*[#id="yfncsumtab"]').inner_text
Now I'm able to take tobj and pull the data out, but it doesn't do me any good for trying to manipulate the object as a table. Any suggestions on how to go about storing the table as a variable would help. I can probably figure out iterating through the rows/columns from there, but I wouldn't mind if you tacked on some code that would do that.
Do you know Watir has xpath support?
browser.element(:xpath => '//*[#id="yfncsumtab"]')
Look at it this way:
doc = Nokogiri::HTML.parse(browser.html)
table = doc.at('table#yfncsumtab')
# iterate through tr's
table.search('tr').each do |tr|
# do something with tr
end
Try browser.element(id: "yfncsumtab").text

extract single string from HTML using Ruby/Mechanize (and Nokogiri)

I am extracting data from a forum. My script based on is working fine. Now I need to extract date and time (21 Dec 2009, 20:39) from single post. I cannot get it work. I used FireXPath to determine the xpath.
Sample code:
require 'rubygems'
require 'mechanize'
post_agent = WWW::Mechanize.new
post_page = post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')
puts post_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div[2]/text()').to_s.strip
puts post_page.parser.at_xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div[2]/text()').to_s.strip
puts post_page.parser.xpath('//[#id="post1960370"]/tbody/tr[1]/td/div[2]/text()')
all my attempts end with empty string or an error.
I cannot find any documentation on using Nokogiri within Mechanize. The Mechanize documentation says at the bottom of the page:
After you have used Mechanize to navigate to the page that you need to scrape, then scrape it using Nokogiri methods.
But what methods? Where can I read about them with samples and explained syntax? I did not find anything on Nokogiri's site either.
Radek. I'm going to show you how to fish.
When you call Mechanize::Page::parser, it's giving you the Nokogiri document. So your "xpath" and "at_xpath" calls are invoking Nokogiri. The problem is in your xpaths. In general, start out with the most general xpath you can get to work, and then narrow it down. So, for example, instead of this:
puts post_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div[2]/text()').to_s.strip
start with this:
puts post_page.parser.xpath('//table').to_html
This gets the any tables, anywhere, and then prints them as html. Examine the HTML, to see what tables it brought back. It probably grabbed several when you want only one, so you'll need to tell it how to pick out the one table you want. If, for example, you notice that the table you want has CSS class "userdata", then try this:
puts post_page.parser.xpath("//table[#class='userdata']").to_html
Any time you don't get back an array, you goofed up the xpath, so fix it before proceding. Once you're getting the table you want, then try to get the rows:
puts post_page.parser.xpath("//table[#class='userdata']//tr").to_html
If that worked, then take off the "to_html" and you now have an array of Nokogiri nodes, each one a table row.
And that's how you do it.
I think you have copied this from Firebug, firebug gives you an extra tbody, which might not be there in actual code... so my suggestion is to remove that tbody and try again.
if it still doesn't work ... then follow Wayne Conrad's process that's the best!

Resources