Webdriver in Ruby, how to check elements exist - ruby

I am using Webdriver in Ruby and I want to verify three text exists on a page.
Here is the piece of html I want to verify:
<table class="c1">
<thead>many subtags</thead>
<tbody id="id1">
<tr class="c2">
<td class="first-child">
<span>test1</span>
</td>
manny other <td></td>
</tr>
<tr class="c2">
<td class="first-child">
<span>test2</span>
</td>
manny other <td></td>
</tr>
<tr class="c2">
<td class="first-child">
<span>test3</span>
</td>
manny other <td></td>>
</tr>
</tbody>
</table>
How do I verify "test1", "test2" and "test3" presents on this page using
find_element
find_elements
getPageSource?
What is the best approach for it?
Thank you very much.

I would go with #find_elements method,because with other 2 options there will be a chance to get no such element exception.
First collect it in an array -
array = driver.find_elements(:xpath,"//table[#class='c1']//tr[#class='c2']//span[text()='test1']")
Now check the array size
"test1 is present" unless array.empty?
The same way we can test for test2 and test3.

Following sample code will help you to perform your task :
def check_element_exists
arr = ["test1", "test2", "test3"]
arr.each do |a|
if $driver.page_source.include? a
puts a
else
print a
puts " Not present"
end
end

Related

How can I load this "label" using importxml & xPath

I've been trying to get this to load into google spreadsheet, but no success so far:
http://www.aastocks.com/tc/stocks/analysis/company-fundamental/basic-information?symbol=00027
The, I have the formula as follows:
=importxml ("http://www.aastocks.com/tc/stocks/analysis/company-fundamental/basic-information?symbol=" & To_Text(A7),"//td[#id='sb2-last']/label[#id='SQ_Last']/following-sibling::label/text()")
Result:
The content imported is empty.
The html part:
<table>
<tbody>
<tr>
<td>現價<label id="SQ_Currency">(港元)</label></td>
<td id="sb2-last">
<label id="SQ_Last" class="cls">**57.500**</label>
</td>
</tr>
</tbody>
</table>
try:
=INDEX(IMPORTXML(
"http://www.aastocks.com/tc/stocks/analysis/company-fundamental/basic-information?symbol="&
TO_TEXT(A7), "//td[#class='mcFont cls']"), 26)

How to select elements when there is a space in an HTML class

How do I use CSS selectors to get the "This is the text I need" line below?
I don't know how to deal with spaces in the table class.
<table class="some name">
<thead>
</thead>
<tbody>
<tr>
<td style="text-align:center;">50</td>
<td style="text-align:left;">This is the text I need</td>
If there are spaces in the class attribute value, that means there are multiple classes applied to the element. To locate an element with multiple classes, the css selector is just a chain of the classes. Generally, the form looks like:
element.class1.class2
Therefore, assuming the link is the first in the table with class "some" and "name", you can do:
require 'nokogiri'
html = %Q{
<table class="some name">
<thead>
</thead>
<tbody>
<tr>
<td style="text-align:center;">50</td>
<td style="text-align:left;">This is the text I need</td>
</tr>
</tbody>
</table>
}
doc = Nokogiri::XML(html)
# Assuming you need both classes to uniquely identify the table
p doc.at_css('table.some.name a').text
#=> "This is the text I need"
# Note that you do not need to use both classes if one of them is unique
p doc.at_css('table.name a').text
#=> "This is the text I need"

xpath selecting text from link in <td> & text from <td>

I have the following code which works very well:
rows = diary_HTML.xpath('//*[#id="main"]/div[2]/table/tbody/tr')
food_diary = rows.collect do |row|
detail = {}
[
["Food", 'td[1]/text()'],
["Calories", 'td[2]/text()'],
["Carbs", 'td[3]/text()'],
["Fat", 'td[4]/text()'],
["Protein", 'td[5]/text()'],
["Cholest", 'td[6]/text()'],
].each do |name, xpath|
detail[name] = row.at_xpath(xpath).to_s.strip
end
detail
end
However the "Food" td does not only include text, but also a link from which I want to get the text.
I know I can use 'td[1]/a/text()'to get the link text, but how do I do both?
'td[1]/a/text()' or 'td[1]/text()'
EDITED - Added Snippet.
I am trying to include the <tr class="meal_header">
<td class="first alt">Breakfast</td> on the first row, all lines with other regular tds on other rows whilst excluding td1 on the bottom row.
<tr class="meal_header">
<td class="first alt">Breakfast</td>
<td class="alt">Calories</td>
<td class="alt">Carbs</td>
<td class="alt">Fat</td>
<td class="alt">Protein</td>
<td class="alt">Sodium</td>
<td class="alt">Sugar</td>
</tr>
<tr>
<td class="first alt">
<a onclick="showEditFood(3992385560);" href="#">Hovis (Uk - White Bread (40g) Toasted With Flora Light Marg, 2 slice</a> </td>
<td>262</td>
<td>36</td>
<td>9</td>
<td>7</td>
<td>0</td>
<td>3</td>
</tr>
<tr class="bottom">
<td class="first alt" style="z-index: 10">
Add Food
<div class="quick_tools">
Quick Tools
<div id="quick_tools_0" class="quick_tools_options hidden">
<ul>
<li><a onclick="showLightbox(200, 250, '/food/quick_add?meal=0&date=2013-04-15'); return false;">Quick add calories</a></li>
<li>Remember meal</li>
<li>Copy yesterday</li>
<li>Copy from date</li>
<li>Copy to date</li>
</ul>
</div>
<div id="recent_meals_0" class="recent_meal_options hidden">
<ul id="recent_meal_options_0">
<li class="header">Copy from which date?</li>
<li>Sunday, April 14</li>
<li>Saturday, April 13</li>
</ul>
</div>
</div>
</td>
<td>285</td>
<td>39</td>
<td>9</td>
<td>10</td>
<td>0</td>
<td>3</td>
<td></td>
The short answer is: use Nokogiri::XML::Element#text, it will give the text of the element plus subelements (your a for example).
You can also clean that code up quite a bit:
keys = ["Food", "Calories", "Carbs", "Fat", "Protein", "Cholest"]
food_diary = rows.collect do |row|
Hash[keys.zip row.search('td').map(&:text)]
end
And as a final tip, avoid using xpath with html, css is so much nicer.
I think you can achieve this by altering the logic to look at element content when you don't have an explicit text() extraction in the xpath
rows = diary_HTML.xpath('//*[#id="main"]/div[2]/table/tbody/tr')
food_diary = rows.collect do |row|
detail = {}
[
["Food", 'td[1]'],
["Calories", 'td[2]/text()'],
["Carbs", 'td[3]/text()'],
["Fat", 'td[4]/text()'],
["Protein", 'td[5]/text()'],
["Cholest", 'td[6]/text()'],
].each do |name, xpath|
if xpath.include?('/text()')
detail[name] = row.at_xpath(xpath).to_s.strip
else
detail[name] = row.at_xpath(xpath).content.strip
end
end
detail
end
You could also add e.g. a symbol to the array, to describe how you were extracting the data, and have a case block which handled items depending on what the last stage was to do following the xpath
Note you could also do what you want by walking the node structure returned by xpath recursively, but that seems like overkill if you just want to ignore markup, links etc.

Watir Display Content from a table in a text file

I am using watir-webdriver, I am trying to write the content displayed in a table below (RATE CODE SELECTED NOT AVAILABLE THIS LOCATION OR CAR 138DE) into a text file but I can't get it to display the content. Any idea what i doing wrong ? Thanks in advance for your help.
errorMes = browser.select_list(:name => 'seamlessMessages').value
f3=File.open( 'Error Log - Conf Res.txt', 'a')
f3.puts "Cancel Reservation - FAILED - Res ID: " +tasid+" - " + CSAsite + " - Message: " +errorMes
f3.puts "\n"
f3.close
code
<TD><B>Error/Informational Messages</B></TD>
<TD><B>Number of Messages</B></TD>
<TD class="tableData"><span name="nbrOfMessages"/>1</span></TD>
<TD> </TD>
</TR>
<TR>
<TD height="1"></TD>
</TR>
</TABLE>
<TABLE width="100%" class="displaySubsection" cellpadding="0" cellspacing="0">
<TR>
<TD width="5" rowspan="3"></TD>
<TD width="800"></TD>
<TD width="5" rowspan="3"></TD>
</TR>
<TR>
<TD>
<select name="seamlessMessages" size="2" readonly tabIndex="-1" class="readonly wide"><option value="0" selected>RATE CODE SELECTED NOT AVAILABLE THIS LOCATION OR CAR 138DE </option></select>
</TD>
I assume your problem is that you are getting "0" instead of "RATE CODE SELECTED NOT AVAILABLE THIS LOCATION OR CAR 138DE".
When you do errorMes = browser.select_list(:name => 'seamlessMessages').value, it is returning the 'value' attribute of the selected option.
If you want the text, you need to do:
errorMes = browser.select_list(:name => 'seamlessMessages').selected_options.first.text
Yes you're right I was getting "0"
Just to let you know that when i tried
errorMes = browser.select_list(:name => 'seamlessMessages').selected_options.first.text
I got an error message in <main>': undefined methodtext' for "RATE CODE SELECTED
NOT AVAILABLE THIS LOCATION OR CAR 138DE":String (NoMethodError)
By removing .text it did work. Thanks a lot for your help on this.
errorMes = browser.select_list(:name => 'seamlessMessages').selected_options.first

Nokogiri next_element with filter

Let's say I've got an ill formed html page:
<table>
<thead>
<th class="what_I_need">Super sweet text<th>
</thead>
<tr>
<td>
I also need this
</td>
<td>
and this (all td's in this and subsequent tr's)
</td>
</tr>
<tr>
...all td's here too
</tr>
<tr>
...all td's here too
</tr>
</table>
On BeautifulSoup, we were able to get the <th> and then call findNext("td"). Nokogiri has the next_element call, but that might not return what I want (in this case, it would return the tr element).
Is there a way to filter the next_element call of Nokogiri? e.g. next_element("td")?
EDIT
For clarification, I'll be looking at many sites, most of them ill formed in different ways.
For instance, the next site might be:
<table>
<th class="what_I_need">Super sweet text<th>
<tr>
<td>
I also need this
</td>
<td>
and this (all td's in this and subsequent tr's)
</td>
</tr>
<tr>
...all td's here too
</tr>
<tr>
...all td's here too
</tr>
</table>
I can't assume any structure other than there will be trs below the item that has the class what_I_need
First, note that your closing th tag is malformed: <th>. It should be </th>. Fixing that helps.
One way to do it is to use XPath to navigate to it once you've found the th node:
require 'nokogiri'
html = '
<table>
<thead>
<th class="what_I_need">Super sweet text<th>
</thead>
<tr>
<td>
I also need this
</td>
<tr>
</table>
'
doc = Nokogiri::HTML(html)
th = doc.at('th.what_I_need')
th.text # => "Super sweet text"
td = th.at('../../tr/td')
td.text # => "\n I also need this\n "
This is taking advantage of Nokogiri's ability to use either CSS accessors or XPath, and to do it pretty transparently.
Once you have the <th> node, you could also navigate using some of Node's methods:
th.parent.next_element.at('td').text # => "\n I also need this\n "
One more way to go about it, is to start at the top of the table and look down:
table = doc.at('table')
th = table.at('th')
th.text # => "Super sweet text"
td = table.at('td')
td.text # => "\n I also need this\n "
If you need to access all <td> tags within a table you can iterate over them easily:
table.search('td').each do |td|
# do something with the td...
puts td.text
end
If you want the contents of all <td> by their containing <tr> iterate over the rows then the cells:
table.search('tr').each do |tr|
cells = tr.search('td').map(&:text)
# do something with all the cells
end

Resources