I'm using the Ruby Watir library to do automated testing for a client and I'm having issues with the XPath selector. I think I just need another set of eyes to let me know if I'm just missing something.
Here is the selector I'm using:
puts ie.cell(:xpath, "//img[#src='3.jpg']/../").text
For this set of tables, it works as expected and prints "Third Image":
<table>
<tr>
<td><img src="1.jpg">First Image</td>
</tr>
</table>
<table>
<tr>
<td><img src="2.jpg">Second Image</td>
</tr>
</table>
<table>
<tr>
<td><img src="3.jpg">Third Image</td>
</tr>
</table>
But is is breaking when I remove the second table:
<table>
<tr>
<td><img src="1.jpg">First Image</td>
</tr>
</table>
<table>
<tr>
<td><img src="3.jpg">Third Image</td>
</tr>
</table>
Using the puts code above I get this error on the second example:
Watir::Exception::UnknownObjectException: Unable to locate element, using :xpath, "//img[#src='3.jpg']/../"
I reproduced the problem, and restarting the browser (IE6) fixed it for me.
For current versions of Watir the better way to do this would be
browser.img(:src => '3.jpg').parent.text
Related
I have html page like this:
<table>
<thead>
<tr>
<td>title</td>
<td>desc</td>
<td>status</td>
</tr>
</thead>
<tbody>
<tr>
<td><label>lorem1</label></td>
<td><label>desc1 lorem</label></td>
<td><label>active</label></td>
<td> Delete </td>
</tr>
<tr>
<td><label>lorem2</label></td>
<td><label>desc2 lorem</label></td>
<td><label>active</label></td>
<td> Delete </td>
</tr>
<tr>
<td><label>lorem3</label></td>
<td><label>desc3 lorem</label></td>
<td><label>deactive</label></td>
<td> Delete </td>
</tr>
</tbody>
</table>
Now I delete record lorem2 from above list (with click on delete link) and after that I want to check lorem2 that deleted shouldn't exist or contain in page.
I write this code but it's not correct:
expect(element(by.css("table")).getText()).not.toBe('lorem2');
You will delete the lorem2 by a locator may be xpath
below for deleting
//tr/td//label[contains(text(),"lorem2")]/following::td/a
below for checking if exist after deletion
//tr/td//label[contains(text(),"lorem2")]
you should parameterize xpath (i.e) the text Lorem2 for other text.
expect(element(by.xpath('//tr/td//label[contains(text(),"lorem2")]
')).isPresent()).toBe(false);
I want to iterate over each row of a table.
This is the relevant source code showing 6 table rows in total.
3 of them have no class name and 3 others do, the ... represent some attributes.
<tbody>
<tr> … </tr>
<tr class="even"> … </tr>
<tr> … </tr>
<tr class="even"> … </tr>
<tr> … </tr>
<tr class="even"> … </tr>
</tbody>
Assuming that doc is a Nokogiri::HTML::Document the following code generates only 3 tr elements instead of 6. It only returns the tr elements having the class="even".
doc.css('#main_result table tbody tr').each do |tr|
p tr
end
How can I now get an array of all tr elements, making it able to iterate over them?
This actual HTML can be found on the following link:
http://www.motogp.com/en/Results+Statistics/1949/TT/500cc/RAC
I don't really know how to paste the source code nicely... sorry
The HTML in that page is malformed, and is missing some <tr> tags, it actually looks something like this:
<tbody>
<td></td>
...
</tr>
<tr class="even">
<td></td>
...
</tr>
<td></td>
...
</tr>
<tr class="even">
<td></td>
...
</tr>
<td></td>
...
</tr>
<tr class="even">
<td></td>
...
</tr>
</tbody>
Note how only the tr tags with class="even" are present, the others are missing. Nokogiri therefore only sees three rows when parsing the page.
One possible solution to this could be to use Nokogumbo, which adds Google’s Gumbo HTML5 parser to Nokogiri, and better handles and corrects malformed HTML like this:
require 'nokogumbo' # install the gem first
doc = Nokogiri.HTML5(the_page)
puts doc.css('#main_result table tbody tr').size
# should now be 6 rather than 3
I'm stumped on why and how to do this query.
My html structure is like this (tables nested inside tables):
<root>
<table>
</table>
<table>
<tr>
<td>
<table>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
</td>
</tr>
</table>
</root>
If I start out my xpath like:
var tables = blah.SelectNodes("//table");
which returns me the 3 parent tables, then I want to select the td's from the 2nd tr like this:
var td = tables[2].SelectNodes("//tr[2]/td");
But, when I do this, it goes back to the parent/root, the "blah" level. Why is this, and how can I keep filtering my search results down?
Note: The example xml structure may not directly match the queries written, just trying to give a general idea...
Just keep extending the XPath
This one returns the <tr> items (four of them) of the second table:
/table/tr/td/table/tr
This one returns the second <tr> item:
/table/tr/td/table/tr[2]
Your best bet, though, is to give individual id attributes to each table, so that you can find it directly using that attribute.
Using something like this:
<root>
<table id="1">
</table>
<table id="2">
<tr>
<td>
<table id="3">
<tr>
<td></td>
<td></td>
</tr>
<tr>
<td></td>
<td></td>
</tr>
</table>
</td>
</tr>
</table>
</root>
You can get the items in the innermost table with:
//table[#id="3"]
You can get an individual <td> item from that innermost table with:
//table/tr/td/table/tr[2]/td[1]
Assigning an id attribute makes it a little easier (note missing /tr/td items after the first table):
//table[#id="3"]/tr[2]/td[1]
I have a list of 'product' which I want to show as a list of row table using an html template.
The html template looks like:
<tr th:fragment="productTemplate">
<td th:text="${productName}">product name</td>
<td th:text="${productprice}>product price</td>
</tr>
Here is what I did:
<table>
<tr th:each="product : ${products}" th:substituteby="product :: productTemplate" th:with="productName=*{name}, productPrice=*{price}" />
</table>
If I use th:include, there will be tr nested to each tr
If I use th:substituteby, substitute has the priority on th:each
I cant find a way to replace my loop items by an other.
Somebody have a solution to do this?
I got it:
<table>
<tr th:each="product : ${products}" th:include="product :: productTemplate"
th:with="productName=${product.name}, productPrice=${product.price}"
th:remove="tag" />
</table>
And here, we can keep the template class on the tr element (that what I wanted)
<tbody th:fragment="productTemplate">
<tr class="my-class">
<td th:text="${productName}">product name</td>
<td th:text="${productPrice}">product price</td>
</tr>
</tbody>
here's the result:
<table>
<tr class="my-class">
<td>Lettuce</td>
<td>12.0</td>
</tr>
<tr class="my-class">
<td>Apricot</td>
<td>8.0</td>
</tr>
</table>
thanks to danielfernandez from the official thymeleaf forum
th:include is what you are looking for. The code below works for me. I prefer to put multiple fragments in one file so I've included that here.
<table>
<tr th:each="product : ${products}" th:include="/fragments/productFragment :: productRow" />
</table>
...
/fragments/productFragment.html
...
<tr th:fragment="productRow">
<td th:text="${product.productName}">product name</td>
<td th:text="${product.productPrice}">product price</td>
</tr>
...
I have a partial view like this:
#model List<user>
#foreach (var user in Model)
{
<tr>
<td>#user.name</td>
<td>...</td>
</tr>
}
And get an error like this:
Validation (HTML5): Element 'tr' cannot be nested within element 'tr'.
It's annoying me more than it should, but I want to get rid of it. Installing Web Standards Update didn't help. Any ideas?
Edit
This is the main view:
<table>
<thead>
<tr>
<th>#i18n.name</th>
<th>...</th>
</tr>
</thead>
<tbody id="results">
#Html.Partial("list_rows", #Model.users)
</tbody>
</table>
This is the generated HTML:
<table>
<thead>
<tr>
<th>naam</th>
<th>...</th>
</tr>
</thead>
<tbody id="results">
<tr>
<td>...</td>
<td>...</td>
</tr>
<tr>
<td>...</td>
<td>...</td>
</tr>
</tbody>
</table>
Edit Pulling the entire page through the W3C validator gives
This document was successfully checked as HTML5!
This error appears when you open a <tr> element before you loop through your model. So far the code you postet is correct and free of errors.
Just make sure that your code looks something like this:
<table>
#foreach (var user in Model)
{
<tr>
<td>#user.name</td>
<td>...</td>
</tr>
}
</table>
It seems like you already have an open tr tag in which you are trying to add more tr tags. If you already have tr tags in your table, just make sure they are all closed before the loop starts:
<tr>..</tr>