Ruby. Cucumber, writing some steps with Xpath - ruby

I have the following piece of HTMl.
<table id="movies">
<thead><tr>
<th>Movie Title</th>
<th>Rating</th>
<th>Release Date</th>
<th>More Info</th>
</tr></thead>
<tbody>
<tr>
<td>The Terminator</td>
<td>R</td>
<td>1984-10-26 00:00:00 UTC</td>
<td>More about The Terminator</td>
</tr>
<tr>
<td>When Harry Met Sally</td>
<td>R</td>
<td>1989-07-21 00:00:00 UTC</td>
<td>More about When Harry Met Sally</td>
</tr>
<tr>
<td>Amelie</td>
<td>R</td>
<td>2001-04-25 00:00:00 UTC</td>
<td>More about Amelie</td>
</tr>
</tbody>
</table>
Now, i want to write the step in my Cucumber in order to check if the specified "ratings" (the second column) are on the page or not.
So, i wrote this (this is a part of the bigger code from my step defination but i checked, everything works till this place):
txt = "//table[#id='movies']/tbody//td[2]"
page.all(:xpath, txt) do |element|
debugger
puts element.text
end
However, there seems to be somewhere a small error, because i never get inside this page.all block... no debugger is invoke, for instance.
Any Help is appreciated :)

After some meditative minutes the problem was fixed im my mind :)
I simply needed to give .each at the end.
txt = "//table[#id='movies']/tbody//td[2]"
page.all(:xpath, txt).each do |element|
debugger
puts element.text
end

Related

Ruby to get the value from HTML table which has same elements

I have a HTML file code which is shown below:
<table id="plans" class="brand-table">
<thead>
<tr>
<th class="domain">Plans</th>
<th class="basic">Basic</th>
<th class="plus">Plus</th>
<th class="prime">Prime</th>
</tr>
</thead>
<tbody>
<tr class="even">
<td>
www.test.com
</td>
<td>
<input name="upgrade" type="radio">
//this span element is hidden
<span class="plan_status"></span>
</td>
<td>
<input name="upgrade" value="plus www.test.com" type="radio">
//this span element is hidden
<span class="plan_status"></span>
</td>
<td>
<input name="upgrade" value="prime www.test.com" checked="" type="radio">
<span class="plan_status">current</span>
</td>
</tr>
</tbody>
</table>
I want to check which plan is the current plan in the page through Ruby Watir. Below is the script:
require 'watir'
browser = Watir::Browser.new(:chrome)
browser.goto('file:///C:/Users/Ashwin/Desktop/new.html')
browser.table(:id, 'plans').tds.each do |table_row|
if table_row.input(:value, 'plus www.test.com').text =~ /current/i
p 'current plan status is plus'
elsif table_row.input(:value, 'prime www.test.com').text =~ /current/i
p 'current plan status is prime'
else
p 'current plan status is basic'
end
end
But I am getting the output as:
C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.11/lib/watir-webdriver/elements/element.rb:513:in `assert_exists': unable to locate element, using {:value=>"plus www.test.com", :tag_name=>"input"} (Watir::Exception::UnknownObjectException)
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.11/lib/watir-webdriver/elements/element.rb:86:in `text'
from C:/Users/Name/Documents/NetBeansProjects/RubyApplication6/lib/new_main15.rb:8:in `block in <main>'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.11/lib/watir-webdriver/element_collection.rb:29:in `each'
from C:/Ruby193/lib/ruby/gems/1.9.1/gems/watir-webdriver-0.6.11/lib/watir-webdriver/element_collection.rb:29:in `each'
from C:/Users/Name/Documents/NetBeansProjects/RubyApplication6/lib/new_main15.rb:7:in `<main>'
But I want the output to be as:
current plan status is prime
Can anyone please help?
Thanks in advance
Instead of checking which td element has the "current" text, I would suggest checking which radio has the checked attribute. This reduces the number of elements you have to worry about interacting with.
You can find the selected radio using:
table_row.radios.find(&:set?).value
You can then check the value of the radio to see if it starts with the word "plus" or "prime":
# Note that we scope to the tbody to ignore the header row.
# Also make sure you do `trs` not `tds` for the rows.
table_rows = browser.table(id: 'plans').tbody.trs
# Iterate through the rows and check the checked radio button
table_rows.each do |table_row|
case table_row.radios.find(&:set?).value
when /^plus/
p 'current plan status is plus'
when /^prime/
p 'current plan status is prime'
else
p 'current plan status is basic'
end
end
Note that for older versions of Ruby (ie v1.9), you will need to find the selected radio using:
table_row.radios.find { |r| r.set? }.value

How can I search a table faster?

I am trying to search a table for specific a specific value using Ruby and Selenium-webdriver. I have a method that works but takes a lot of time for some reason. It is a one row table and the page HTML looks like this:
<div id="permitGridContainer">
<table id="calendar" class="items" style="width:430px;" name="calendar">
<thead>
<tbody>
<tr>
<td id="avail1" class="status r slct" onmouseout="return nd();" onmouseover="return overlib("Available Quota<br>River Launches : 0 of 4");">
<div class="permitStatus">R</div>
</td>
<td id="avail2" class="status r" onmouseout="return nd();" onmouseover="return overlib("Available Quota<br>River Launches : 0 of 4");">
<div class="permitStatus">R</div>
</td>
<td id="avail3" class="status a" onmouseout="return nd();" onmouseover="return overlib("Available Quota<br>River Launches : 89 of 99");">
<a onclick="javascript:setNewArrivalDate("Sun Sep 06 2015", 2);return false;" href="#">
A
<br>
<small>89</small>
</a>
</td>
<td id="avail4" class="status a" onmouseout="return nd();" onmouseover="return overlib("Available Quota<br>River Launches : 97 of 99");">
</tr>
</tbody>
</table>
</div>
... I shortened the table it has 14 columns.
I am looking for a column that has an Item available and I am checking the class for this, but the text also changes so there are other things I could look for.
This is the code I am using, but it visibly slow. I used puts statements to see the progress. My sense is that is has to do with time accessing the element. So I was hoping there is a better way to process the table quickly. Thank you.
for j in 1..days_to_check[i]
check_avail = driver.find_element(id: "avail#{j}")
check_availclass = check_avail.attribute ("class")
if check_availclass == "status a" or check_availclass == "status a slct"
#process if
end
Depending on your comment I would suggest to use the following xpath. I find this is often easier and feasible to use better xpath than looping though the html table
//td[(#class='status a') or (#class='status A')]
This xpath finds the class with status a or status A

how to get value of a node in nokogiri

I have this
1.9.3-p286 :073 > doc.css("tr[class~=strong]").children[3].children
=> [#<Nokogiri::XML::Element:0x3fee5e077e98 name="a" attributes=[#
<Nokogiri::XML::Attr:0x3fee5e077dd0 name="href"
value="http://somelink">]>]
Sample html:
<tr class='strong bf highbeam'>
<td>December 6th</td>
<td>Foo</td>
<td><a href='http://somelink' title='bar'>December 6th 2012 Episode</a></td>
<td><a href='http://somelink/#disqus_thread'></a></td>
</tr>
How can I fetch the value http://somelink at this point?
Don't use children, refine your css selector until you get the element you want:
doc.at('tr.strong a')[:href]

how do i create random letters in selenium ide?

I'm not an expert in Selenium IDE, I want to declare an array in Selenium IDE HTML and call it in the next line.
<tr>
<td>storeEval</td>
<td>new Array('en','de','da','cs','fi','fr','it','ja','ko','nl','no','pl','pt','ru','sv','tr')</td>
<td>myArray</td>
</tr>
<tr>
<td>type</td>
<td>FieldName</td>
<td>${myArray}</td>
</tr>
Thanks
Code below will randomly select item from array and type it to element with id=FieldName:
<tr>
<td>storeEval</td>
<td>var chars = 'en de da cs fi fr it ja ko nl no pl pt ru sv tr'.split(' '); str = chars[Math.floor(Math.random() * chars.length)];</td>
<td>item</td>
</tr>
<tr>
<td>type</td>
<td>FieldName</td>
<td>${item}</td>
</tr>
To access item from your initial array (lets say second item), you can add one more command:
<tr>
<td>storeEval</td>
<td>new Array('en','de','da','cs','fi','fr','it','ja','ko','nl','no','pl','pt','ru','sv','tr')</td>
<td>myArray</td>
</tr>
<tr>
<td>getEval</td>
<td>storedVars['item'] = storedVars['myArray'][2]</td>
<td></td>
</tr>
<tr>
<td>type</td>
<td>FieldName</td>
<td>${item}</td>
</tr>
You can pass random int in range [0 .. length_of_array] to storedVars['myArray'][randomInt] to retrieve values randomly.

How do I parse a plain HTML table with Nokogiri?

I'd like to parse a HTML page with the Nokogiri. There is a table in part of the page which does not use any specific ID. Is it possible to extract something like:
Today,3,455,34
Today,1,1300,3664
Today,10,100000,3444,
Yesterday,3454,5656,3
Yesterday,3545,1000,10
Yesterday,3411,36223,15
From this HTML:
<div id="__DailyStat__">
<table>
<tr class="blh"><th colspan="3">Today</th><th class="r" colspan="3">Yesterday</th></tr>
<tr class="blh"><th>Qnty</th><th>Size</th><th>Length</th><th class="r">Length</th><th class="r">Size</th><th class="r">Qnty</th></tr>
<tr class="blr">
<td>3</td>
<td>455</td>
<td>34</td>
<td class="r">3454</td>
<td class="r">5656</td>
<td class="r">3</td>
</tr>
<tr class="bla">
<td>1</td>
<td>1300</td>
<td>3664</td>
<td class="r">3545</td>
<td class="r">1000</td>
<td class="r">10</td>
</tr>
<tr class="blr">
<td>10</td>
<td>100000</td>
<td>3444</td>
<td class="r">3411</td>
<td class="r">36223</td>
<td class="r">15</td>
</tr>
</table>
</div>
As a quick and dirty first pass I'd do:
html = <<EOT
<div id="__DailyStat__">
<table>
<tr class="blh"><th colspan="3">Today</th><th class="r" colspan="3">Yesterday</th></tr>
<tr class="blh"><th>Qnty</th><th>Size</th><th>Length</th><th class="r">Length</th><th class="r">Size</th><th class="r">Qnty</th></tr>
<tr class="blr">
<td>3</td>
<td>455</td>
<td>34</td>
<td class="r">3454</td>
<td class="r">5656</td>
<td class="r">3</td>
</tr>
<tr class="bla">
<td>1</td>
<td>1300</td>
<td>3664</td>
<td class="r">3545</td>
<td class="r">1000</td>
<td class="r">10</td>
</tr>
<tr class="blr">
<td>10</td>
<td>100000</td>
<td>3444</td>
<td class="r">3411</td>
<td class="r">36223</td>
<td class="r">15</td>
</tr>
</table>
</div>
EOT
# Today Yesterday
# Qnty Size Length Length Size Qnty
# 3 455 34 3454 5656 3
# 1 1300 3664 3545 1000 10
# 10 100000 3444 3411 36223 15
require 'nokogiri'
doc = Nokogiri::HTML(html)
Use CSS to find the start of the table, and define some places to hold the data we're capturing:
table = doc.at('div#__DailyStat__ table')
today_data = []
yesterday_data = []
Loop over the rows in the table, rejecting the headers:
table.search('tr').each do |tr|
next if (tr['class'] == 'blh')
Initialize arrays to capture the pertinent data from each row, selectively push the data into the appropriate array:
today_td_data = [ 'Today' ]
yesterday_td_data = [ 'Yesterday' ]
tr.search('td').each do |td|
if (td['class'] == 'r')
yesterday_td_data << td.text.to_i
else
today_td_data << td.text.to_i
end
end
today_data << today_td_data
yesterday_data << yesterday_td_data
end
And output the data:
puts today_data.map{ |a| a.join(',') }
puts yesterday_data.map{ |a| a.join(',') }
> Today,3,455,34
> Today,1,1300,3664
> Today,10,100000,3444
> Yesterday,3454,5656,3
> Yesterday,3545,1000,10
> Yesterday,3411,36223,15
Just to help you visualize what's going, at the exit from the "tr" loop, the today_data and yesterday_data arrays are arrays-of-arrays looking like:
[["Today", 3, 455, 34], ["Today", 1, 1300, 3664], ["Today", 10, 100000, 3444]]
Alternatively, instead of looping over the "td" tags and sensing the class for the tag, I could have grabbed the contents of the "tr" and then used scan to grab the numbers and sliced the resulting array into "today" and "yesterday" arrays:
tr_data = tr.text.scan(/\d+/).map{ |i| i.to_i }
today_td_data = [ 'Today', *tr_data[0, 3] ]
yesterday_td_data = [ 'Yesterday', *tr_data[3, 3] ]
In real-world development, like at work, I'd use that instead of what I first wrote because it's succinct.
And notice that I didn't use XPath. It's very doable in Nokogiri to use XPath and accomplish this, but for simplicity I prefer CSS accessors. XPath would have allowed accessing individual "td" tag contents, but it also would begin to look like line-noise, which is something we want to avoid when writing code, because it impacts maintenance. I could also have used CSS to drill down to the correct "td" tags like 'tr td.r', but I don't think it would improve the code, it would just be an alternate way of doing it.

Resources