Using XPath to find a dynamic table value - html

In this table with dynamically changing prices, I wish to always select the link belonging to the first price over $400,00 in XPath 1.0. The correct solution might not always be in the second row, so tr[2]/td[1] will not always be the correct result.
<table>
<tr>
<td>1</td>
<td>-$200,00</td>
</tr>
<tr>
<td>2</td>
<td>$500,00</td>
</tr>
<tr>
<td>3</td>
<td>$100,00</td>
</tr>
</table>
My (non-working) XPath that comes closest so far is:
//tr/td[2][starts-with(.,'$')]/(number(substring-after(.,'$')))>400.00/preceding-sibling::td
It selects the prices by using td[2]
It only selects the prices that have positive values (I don't need the negative values anyway)
It removes the $ signs from the remaining prices
Here is the problem:
I'm trying to use number to convert the strings into numbers so I can compare them to 400 (which is not working)
After that I try to select the corresponding link after the right price has been found.
Any help would be really appreciated (I'm just a simple tester getting dragged into the magical world of test automation) :)

I wish to always select the link belonging to the first price over $400,00 in XPath 1.0.
That would be
//td[number(translate(normalize-space(), ',$', '.')) > 400]/..//a
Note that I translate , to . and $ to nothing**, so if your numbers are formatted any further (digit grouping, for example) this might not be 100% correct yet. Know your data, make the appropriate changes.
For the fun of it (and to demonstrate XPath's flexibility), here's a different path with the same result:
//a[../..//td[number(translate(normalize-space(), ',$', '.')) > 400]]
** translate() replaces one list of characters with another:
translate('abc', 'ac', 'AC') returns 'AbC'
translate('abc', 'ac', 'A') returns 'Ab'.

Related

Find row in a table , given I'm looking a value on a column. XPATH

I'm trying to find the row of a table, given I'm looking for a value in a column. After I have the row value then I can access other columns in the row. I have already know how to do that , but it's not satisfying for me. I'm looking for suggestions.
I have a table with rows that look like this one :
<table data-qa="reportTable">
<tr class="even">
<td style="">Testing</td>
<td>2019-11-21 00:00:00.0</td>
<td>2019-11-24 00:00:00.0</td>
<td>
1
</td>
<td>$1500.00</td>
</tr>
</table>
I want to get the
<a></a>
, the url.
I have tried the following :
//table[#data-qa="reportTable"]//tr[td[1]="Testing"]//a[#class="followLink"]
I select the table.
Then look for the row , the index is defined by step 3.
Look for the content in a column , and get the index.
Look for the URL.
Assume the value I'm looking for is always on 1.
After I find the value on 1 , then I look for the row , and then get "Some URL"
At the end I need the URL.
I just want to know if there is a better idea to do this, since it looks messy, any idea is appreciated. I already took a look in other solutions but all look the same.
Thanks!
You can try this xpath to fetch the required content irrespective of index:
//*[#data-qa='reportTable']//tr[td[.='Testing']]//a[#class='followLink']
This xpath expression:
//table[#data-qa="reportTable"]//td[1][text()="Testing"]/following-sibling::td/a/#href
selects
Some URL
regardless of index, you can you this xpath to get the URL.
//table[#data-qa="reportTable"]/tr[td[text()="Testing"]]/td/a[#class="followLink"]
or if your Testing always comes before your , you can use this.
//table[#data-qa="reportTable"]/tr/td[preceding-sibling::td[text()="Testing"]]/a[#class="followLink"]
You can try the following xpath as well.
//table[#data-qa='reportTable']//td[text()='Testing']/parent::tr//td/a/#href
Here I am trying to find the table, look for the column that has text 'Testing', fetching the row for this column and then finding 'a' in the other columns in the same row in order to get href.
Hope you find it useful.

Scripting Issue with Netsuite Advanced PDF/HTML Template

I need help with the Freemarker format for Netsuite (Advanced PDF/HTML Templates)
There are 3 important data values pulled for this records;
${item.quantity} *Order Value*
${item.fulfilled} *Fulfilled Value*
${item.backordered} *Backorder Value*
Basically, what I try to accomplish is, to only show items that are "on backorder"
However this task seems to be a much harder than I have the time and skill for.
So, my plan B is using a separate template for the backorders (which is working well so far!)
The problem is that if I came across an item which is a NON-INVENTORY item, Netsuite does not calculate any qty for ${item.backordered}
SO
Is there any way I can "calculate" the backorder value with scripting in the template?
Can I use an arithmetic function (like below)?
item.quantity - item.fulfilled = item.backordered
Here is the basic format of the text surrounding this query;
<#if record.item?has_content>
<table><#list record.item as item><#if item_index==0>
<thead>
<tr>
<th> QTY </th>
</tr>
</thead>
</#if>
<tr>
<td> ${item.backordered} </td>
</tr>
</#list></table>
</#if>
I have a basic understanding of HTML and CSS, but scripting is still very new to me, so please only constructive criticism.
Try adding the following Freemarker helper function to your template:
<#function toNumber val>
<#if val?has_content && val?length gt 0 >
<#return val?html?replace('[^0-9.]','','r')?number >
<#else>
<#return 0 >
</#if>
</#function>
This will ensure that all fields are correctly parsed as numbers, and you should be able to perform mathematical calculations on the fields without errors.
So you can replace:
<td> ${item.backordered} </td>
with:
<td> ${toNumber(item.quantity) - toNumber(item.fulfilled)} </td>
See the answer to suitescript set custom column value netsuite for an example that uses arithmetic on line item values.
So I believe the root problem lays with the Java JAR NetSuite is using. It appears it has a null pointer bug with empty int/number type values.
I would recommend adding a UserEvent script to change the values to zero that are empty. This will prevent the error you are getting. You should be able to catch the PRINT type on the before load event. If the zero value is not carried over to the template, you can add a custom column field that is not stored and push the value there.

To create xpath for html table to access desired record using 3 inputs

This is a structure of very basic html table. I want to create xpath for following scenario.
I will insert 2 field names like 'Name' and 'Salary' along with value of 'Name' field (say for example 'STU') then output should be 25k.
I was given hint like
string (xpath which will have 2 field names and one value) output of this function will be that key.
<html>
<body>
<h3>MY TABLE</h3>
<table>
<tbody>
<tr>
<th>id</th>
<th>Name</th>
<th>date</th>
<th>Address</th>
<th>salary</th>
</tr>
<tr>
<td>XYZ</td>
<td>STU</td>
<td>12/20/2015</td>
<td>Mumbai</td>
<td>25k</td>
</tr>
<tr><td>PQR</td>
<td>ABC</td>
<td>01/05/2015</td>
<td>Chennai</td>
<td>25k</td>
</tr>
<tr>
<td>ABC</td>
<td>PQR</td>
<td>03/09/2015</td>
<td>Bangalore</td>
<td>20k</td>
</tr>
<tr>
<td>emp4</td>
<td>XYZ</td>
<td>08/12/2015</td>
<td>Delhi</td>
<td>30k</td>
</tr>
</tbody>
</table>
</body>
</html>
Basics:
The first thing you will need to do is find which column(s) in the table contain(s) the field(s) you are searching for.
For example, to find the position of the Name column:
count(//tbody/tr/th[./text() = 'Name']/preceding-sibling::th) + 1
The above will return 2. (Of course, this technique won't necessarily work well if there are colspan attributes used in the HTML, but there are none in your example.)
Then, you can find the rows that contain a specific value in this column - for example STU - in this column like this:
//tbody/tr/td[position() = $WherePosition][./text() = 'STU']/..
Notice the use of the variable $WherePosition above. Here, I have used it to represent the result of the previous query (1). Depending on what tool you are using to perform the XPath query, you may or may not have the option to set variables. If you wish to use a single XPath expression or you don't want to or can't use variables, you can simply replace it with the previous XPath expression, although it will become less readable.
It's worth noting that aside from readability, variables will also make it easier to re-use the same expression, because you can, for example, substitute 'Name' in the first query for $WhereField, if you tell the XPath evaluator that you want to set the $WhereField variable to Name. And in the query to find the specific row (2), you can substitute 'STU' for $WhereValue, if you also set this variable accordingly.
Apply it:
Now to get the position of the salary column. If you used my tip above about using variables, you could re-execute the same expression as (1) again, but with the $WhereField variable set to salary instead of Name. i.e. count(//tbody/tr/th[./text() = $WhereField]/preceding-sibling::th) + 1
And if you stored the result of query (2) as a variable called $matching_rows and the salary column position as $SalaryPosition, then to return the salary for the row where Name = STU, you could simply finish with: $matching_rows/td[position() = $SalaryPosition]/text(), and you would get the answer 25k.
TL;DR
In summary, as an XPath 1.0 one-liner to get the salary for the row with Name = STU:
//tbody/tr/td[position() = count(//tbody/tr/th[./text() = 'Name']/preceding-sibling::th) + 1][./text() = 'STU']/../td[position() = count(//tbody/tr/th[./text() = 'salary']/preceding-sibling::th) + 1]/text()

Parameters and Loops in snippets

I would like to make a snippet that makes an HTML table.
Here are some examples of things to type :
table name address city
- table team wins losses draws
- table views clicks clickthrough
This is what I want it to output : a table with a columns for each of the fields (with 'table' triggering the snippet).
I'd also like to run these field names through a function to transform them (for example to field names - "First Name" -> 'first_name'.
Is this possible? How would I do it?
Not exactly what you want, but I would go with Emmet (here is a handy cheet sheet). There is a Sublime package available, so it's easy to install. It might be a little overwhelming at first, but once you start to use it, you will get the hang of it and it will speed up your html/css production.
Type table>tr*3>th and hit tab at the end. This will produce:
<table>
<tr>
<th></th>
</tr>
<tr>
<th></th>
</tr>
<tr>
<th></th>
</tr>
</table>
Then you can tab to the ths to type in your column headers.

Select a row based on the contents of a cell with xpath

I have a table that consists of multiple rows that each contain 5 cells, like this:
<tr>
<td></td>
<td>123456</td>
<td>statusText</td>
<td><a>linkText</a></td>
<td>editButton</td>
</tr>
The 123456 could be any string of random letters and numbers. I want to be able to select a link based on the contents of the second cell in the table. I've been trying something like this:
//tr[contains(td, '123456')]
to get me to the cell, but it either returns every row or nothing, depending on how I tweak the xpath.
I've been trying something like this:
//tr[contains(td, '123456')]
to get me to the cell, but it either
returns every row or nothing,
depending on how I tweak the xpath
You get what you asked for. The above XPath expression selects any tr element (row) in the document that has (at least one) td child whose string value contains '123456'.
But you want:
//tr/td[text() = '123456']
this selects every td element (cell) in the document, that has a text node child, whose string value is '123456'.
There can be different variations, depending on whether a td may have more than one text nodes and on whether the white space in a text node should be normalized, but the question doesn't provide any information if any of these apply in this particular case.
I'd research something like //tr[string(td[2]) = '123456']. If this does not work, I'd look up XPath axes.