find first level children in nokogiri rails - ruby

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')

Related

Vuetify v-data-table change a row color for a few seconds

We've just moved over from bootstrap to Vuetify, but i'm struggling with something.
We have some updates sent (over signalR) that update a list of jobs, i'd like to be able to target a job that has been changed and change the row color for that particular job for a few seconds so the operator can see its changed.
Has anyone any pointers on how we can do this on a Vuetify v-data-table
Thanks
I ran into the same problem. This solution is a bit crude and a bit too late, but may help someone else.
In this example I change the colour of the row permanently until the page reloads. The problem with a temporary highlight is that if the table is sorted there is no way to put the row in the visible part of the table - v-data-table will put it where it belongs in the sort, even if it's out of the view.
Collect the list of IDs on initial load.
Store the list inside data of the component.
Use a dynamic :class attribute to highlight rows if the ID is not in the list (added or edited rows)
Solution in detail
1. Use TR in the items template to add a conditional class.
<template slot="items" slot-scope="props">
<tr :class="newRecordClass(props.item.email, 'success')">
<td class="text-xs-center" >{{ props.item.email }}</td>
:class="newRecordClass(props.item.email, 'success')" will call custom method newRecordClass with the email as an ID of the row.
2. Add an additional array to store IDs in your data to store
data: {
hydrated: false,
originalEmails: [], <--- ID = email in my case
3. Populate the list of IDs on initial data load
update(data) {
data.hydrated = true; // data loaded flag
let dataCombined = Object.assign(this.data, data); // copy response data into the instance
if (dataCombined.originalEmails.length == 0 ) {
// collect all emails on the first load
dataCombined.originalEmails = dataCombined.listDeviceUsers.items.map( item => item.email)
}
return dataCombined;
}
Now the instance data.originalEmails has the list of IDs loaded initially. Any new additions won't be there.
4. Add a method to check if the ID is in the list
newRecordClass(email, cssClass) {
// Returns a class name for rows that were added after the initial load of the table
if (email == "" || this.data.originalEmails.length==0) return "" // initial loading of the table - no data yet
if (this.data.originalEmails.indexOf(email) < 0 ) return cssClass
}
:class="newRecordClass(..." binds class attribute on TR to newRecordClass method and is being called every time the table is updated. A better way of doing the check would be via a computed property (https://v2.vuejs.org/v2/guide/computed.html#Computed-Properties). Vue would only call it when the underlying data changed - a method is called every time regardless.
Removing the highlight
You can modify newRecordClass method to update the list of IDs with new IDs after a delay to change the colour to normal.
#bakersoft - Did you find a solution? I suspect there is an easier way to skin this cat.

Selecting images inside nested tables using Watir

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.

Accessing a table within a table (Watir/PageObject)

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"

Can I use linq to join two result sets on an ordinal/ index #?

I'm trying to use linq to objects with html agility pack to join two result sets on their relative ordinal position. One set is a list of headers, the other is a set of tables, with each table corresponding to one of the header values. Each set has a count of five. I've read the post here which looks very similar, but can't get it to translate to my purposes.
Here is what I'm using to get the two html node collections:
HtmlNodeCollection ratingsChgsHdrs = htmlDoc.DocumentNode.SelectNodes("//div[#id='calendar-header']");
HtmlNodeCollection ratingsChgsTbls = htmlDoc.DocumentNode.SelectNodes("//table[#class='calendar-table']");
The collection ratingsChgsHdrs contains the headers for each of the tables in ratingsChgsTbls, within the InnerText property. The end result I'm looking for is one result set consisting of all of the rows from all five tables, with the header value added as a property to each row. I hope that is clear.. any help would be great.
This might work:
ratingsChgsHdrs.Select((x, i) => new { x, ratingsChgsTbls.ElementAt(i) });

How to get the 1st column for all rows within a table using Selenium java API

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])

Resources