I'm trying to use Watir to scrape a page which has AJAX-generated tables. As each level of the table is expanded, additional nested tables are inserted and the expand link moves in one level. The number of levels is not always the same. The end result is something like this, loaded in asynchronously one layer at a time:
<table>
Data Type 1
<table>
<table>
<table>
<img src="expand.gif" />
</table>
</table>
</table>
</table>
<table>
Data Type 2
<table>
<table>
<table>
<img src="expand.gif" />
<img src="expand.gif" />
</table>
</table>
</table>
</table>
I want to interact only with the table containing "Data Type 1." The table IDs are randomly generated (this is an annoying ASP.net application), so the only thing to select on is "Data Type 1."
Thus, I have:
while browser.table(:text => /Type 1/).image(:src => "expand.gif").present?
links = browser.table(:text => /Type 1/).images(:src => "expand.gif")
links.each do |li|
li.click
end
end
This works fine to expand the first nested table (with the expand.gif directly inside the table element that contains the selector text. However, the next layer of tables does not expand--clearly, my selector isn't going into child tables.
If I leave out the table selector altogether, the code will happily expand every nested table on the page until it runs out of levels, but I only want "Data Type 1."
Any suggestions how to accomplish this? Thanks in advance.
Related
I am interested in displaying quick counts of email stats in my application, but I'm getting hung up on finding an efficient way to generate the counts. I am hoping to just use the eloquent relationship with some sort of "count where" statement inside of blade.
Getting the total count works as it should:
{{count($emails->mandrillemails)}}
but is something like this possible?:
{{count($emails->mandrillemails->msg_state == 'bounced')}}
{{count($emails->mandrillemails->msg_state == 'open')}}
Here is my block of code with the #if and #foreach statements:
#if(count($sentEmails) > 0)
#foreach ($sentEmails as $emails)
<tr>
<td>
#if(count($emails->mandrillemails) > 0)
<p><span class="badge"><i class="fa fa-paper-plane"></i> {{count($emails->mandrillemails)}}</span> <span class="badge"><i class="fa fa-exclamation-triangle"></i> {{count($emails->mandrillemails->msg_state == 'bounced')}}</span></p>
#endif
</td>
</tr>
When I attempt this, I get an undefined property error on "msg_state"
You can count a relationship like this:
$emails->mandrillemails()->whereMsgState('bounced')->count();
Since you're #foreaching things, you may want to eager load this data. This is a little more complex when you're doing counts on the relationship - see http://laravel.io/forum/05-03-2014-eloquent-get-count-relation and http://softonsofa.com/tweaking-eloquent-relations-how-to-get-hasmany-relation-count-efficiently/ for some possible techniques to eager-load the count value instead of the whole list of related items.
$emails->mandrillemails()->whereMsgState('bounced')->count() should work.
I'm running Watir-webdriver with Cheezy's PageObject for Cucumber testing. I am new to ruby, testing, pageobjects, and watir.
I am trying to access a table. Specifically, I'm trying to click an image in the 7th column.
This is my code:
tables.downcase!
tables.gsub!(/\s+/, '_')
tables_elements = self.send("#{tables}s_elements")
the_table = tables_elements[table_number]
the_table[row_number][column_number].click
With this code running, it becomes apparent that it believes the table (columns, rows, everything) is one cell. After looking at the html, it appears there is another table within this one. The table is a gwt table of some sort. What this means is that the ID I have is not for the table I want, but for a table containing the table I want. Is there anyway to get what I want? For instance:
the_table[0].element[row_number][column_number]
Or do I have to manipulate the html directly? Because, sadly, there doesn't appear to be much to manipulate, and no way that I can see to set a class (multiple of these tables on every page) through the original java code. If you can think of another way to click the image, I'd be happy to hear it
Thank you in advance
Accessing table from table:
You can get the table within a table by chaining elements. Assuming that you want the first table within the table with an id, you can do:
your_table_element.table_element
For example, say the page html is:
<table id="known_id">
<tr>
<td>
<table>
<tr>
<td>A0</td>
<td>B0</td>
</tr>
</table>
</td>
</tr>
</table>
You can create a page object with a reference to the known table:
class MyPage
include PageObject
table(:your_table, :id => 'known_id')
end
The following would then give you the first row's second cell of the child table:
puts page.your_table_element.table_element[0][1].text
#=> "B0"
Note that you can pass additional locators to the table_element method. For example, if the child table is actually the second instead of the first table, you can do:
puts page.your_table_element.table_element(:index => 1)[0][1].text
#=> "B0"
Accessing child table directly:
Alternatively, assuming you always want the child table, you can change the page object to access it instead. This is done by pass a block to the accessor method:
class MyPage
include PageObject
table(:actual_table){ table_element(:id => 'known_id').table_element }
end
Which then means you do not need to call the extra table_element method:
puts page.actual_table_element[0][1].text
#=> "B0"
I've faced with a problem how to find first level children from the current element ?
For example i have html :
<table>
<tr>abc</tr>
<tr>def</tr>
<table>
<tr>second</tr>
</table>
</table>
I am using Nokogiri for rails :
table = page.css('table')
table.css('tr')
It returns all tr inside table.
But I need only 2 that first level for the table.
When you say this:
table = page.css('table')
you're grabbing both tables rather than just the top level table. So you can either go back to the document root and use a selector that only matches the rows in the first table as mosch says or you can fix table to be only the outer table with something like this:
table = page.css('table').first
trs = table.xpath('./tr')
or even this (depending on the HTML's real structure):
table = page.xpath('/html/body/table')
trs = table.xpath('./tr')
or perhaps one of these for table (thanks Phrogz, again):
table = page.at('table')
table = page.at_css('table')
# or various other CSS and XPath incantations
You can do
rows = page.css('body > table > tr')
Perhaps you have to adapt the selector to your container element (i chose 'body' here)
As yet another way, you can try to use something like this:
text = <<HERE
<table>
<tr>abc</tr>
<tr>def</tr>
<table>
<tr>second</tr>
</table>
</table>
HERE
xml = Nokogiri::XML(text)
xml.xpath("/table/tr/").each do |node|
puts node.text
end
In this example, '/table/tr' expression represents an absolute path to the required element - 'tr' in our case.
xpath did not work for me
The below code worked fine for me.
table = page.css('table')
table.css('> tr')
I got a table with <tr> data as:
<tr class="iceDatTblRow1" id="body-subview:myMainPage:MainTabs:0:dataTable:0">
<td class="iceDatTblCol1"><span class="iceOutTxt" id="body-
subview:myMainPage:MainTabs:0:dataTable:0:j_idt252">Data that I want</span>
</td>
</tr>
for some reason, i can't seem to locate the <td> with id attribute within it...
please share your expertise - thanks,
I don't know about Selenium but there are many XPath solutions here. A few examples:
span everywhere:
//span[#id='body-subview:myMainPage:MainTabs:0:dataTable:0:j_idt252']
span only inside td:
//td/span[#id='body-subview:myMainPage:MainTabs:0:dataTable:0:j_idt252']
If your requirement is to get all the first column for all rows with Selenium and Xpath then you can try this code:
String tdLocator="//tr"
int count = selenium.getXpathCount(tdLocator)
for(int i = 1;i<=count;i++)
{
get to td by using //tr[i]/td[0]
}
If you have a table ID then you can also use
selenium.getTable("table_id.[rownumber].[colnumber])
I’m using HtmlAgilityPack in order to retrieve the following html (notice the nested table):
<table class="123">
<tr>
<table class="789">
<tr>
<td>abc</td>
</tr>
<tr>
<td>def</td>
</tr>
</table>
</tr>
<tr>
<td>info 1</td>
</tr>
<tr>
<td>info 2</td>
</tr>
<tr>
<td>info 3</td>
</tr>
</table>
Now, I’m trying to find a clever way to obtain some information from the parent table and some information from the nested table…
So far I have the following:
var parentTable = document.DocumentNode.SelectNodes("//table[#class='123']").FirstOrDefault();
var nestedTable = parentTable.SelectNodes("//table[#class='789']").FirstOrDefault();
I can now play around with the nestedTable and get what I want (abc, def)...
But when I try to get the <tr>’s from the parent table like so:
var parentTableRows = parentTable.SelectNodes(".//tr");
It seems to include (in the collection) the <tr>’s from the nested table as well...
In other words, according to the above html code, I was expecting to have a collection of 4 <tr>’s but since it includes the <tr>’s from the nested table, I’m getting a collection of 6 <tr>’s.
How can I skip the first <tr> that happens to hold the nested table so I can play around and get the information I want (info1, info2, info3)
(hope I’m making sense…)
Thanks in advance!
// is an XPATH expression that means "scan all nodes and sub nodes". That's why //tr gets all tr below the root one.
If you just do parentTable.SelectNodes("tr") (or "./tr" which is equivalent), you will select all TR below the root one.
If you want to skip the first one, then you can add an XPATH filter on element's position() (an XPATH function):
var parentTableRows = parentTable.SelectNodes("tr[position() > 1]");