I have a website to test and there is this piece of html code in it:
<table id="tableid">
<tbody>
<tr class="first">
<td>Hello World</td>
</tr>
<tr class="second">
<td>Bye World</td>
</tr>
</tbody>
</table>
So I want to create a list of the tr-Tags and iterate over them with the following code:
List<WebElement> list = driver.findElements(By.xpath("//table[#id='tableid']/tbody/tr"));
for(WebElement l : list){
System.out.println(l.getAttribute("class"));
System.out.println(l.getLocation());
System.out.println(l.hashCode());
System.out.println(l.findElement(By.xpath("//td")).getText());
}
These four System.out.println's are the following:
first
(32, 300)
1573
Hello World
second
(64, 600)
1574
Hello World
So the location is different, even the class attributes are different. But the getText method returns only the text from the first element. Why? Am I missing something? Doing something wrong? I can't figure it out.
EDIT/UPDATE:
This seems kind of odd. The above code does not work. If I do the following code it works fine. Any explanations?
List<WebElement> list = driver.findElements(By.xpath("//table[#id='tableid']/tbody/tr/td"));
System.out.println(list.get(0).getText());
System.out.println(list.get(1).getText());
Output:
Hello World
Bye World
Your XPath is wrong. //td means "any element anywhere in the document". Try l.findElement(By.xpath("td")).getText() instead - I think you'll get the result you want.
Related
I'm trying to use Rselenium+seleniumPipes to access this zipfile by name: "PUB004_PRE_20220316.zip"
<tr id="l1_VkFMX1BSRV9SUl8yMDIyMDMxMi56aXA" class="elfinder-cwd-file elfinder-ro ui-selectee ui-draggable-handle" title="PUB004_PRE_20220316.zip
Hoy 02:20 PM (4.22 MB)">
<td class="elfinder-col-name">
<div class="elfinder-cwd-file-wrapper">
<span class="elfinder-cwd-icon elfinder-cwd-icon-application elfinder-cwd-icon-zip">.</span>
<span class="elfinder-perms"></span>
<span class="elfinder-lock"></span>
<span class="elfinder-cwd-filename">PUB004_PRE_20220316.zip</span>
</div>
</td>
<td class="elfinder-col-perm">lectura</td>
<td class="elfinder-col-date">Hoy 02:20 PM</td>
<td class="elfinder-col-size">4.22 MB</td>
<td class="elfinder-col-kind">Archivo ZIP</td>
</tr>
Picture of the whole code
But cant seem to get the xpath correctly.
Some of my tries:
select_file <- robot$findElement(
"xpath", "//tr[.//td[.//div[#class='elfinder-cwd-file-wrapper']]]//span[#class='elfinder-cwd-filename']//*[text()='PUB004-PRE-20220316.zip']")
select_file$clickElement()
select_file <- robot$findElement(
"xpath", "//*[#class='elfinder-cwd-file-wrapper']//*[#class='elfinder-cwd-filename']//*[text()='PUB004-PRE-20220316.zip']")
select_file$clickElement()
select_file <- robot$findElement(
"xpath", "//*[#class='elfinder-cwd-filename']//*[text()='PUB004-PRE-20220316.zip']")
select_file$clickElement()
This is the webpage. I want to download a the zip files.
Note: I need to do it by name because I'm interested in downloading the file programmatically by date (20220316).
Seems you were close enough, instead of _ character it should have been - character and should have been PUB004-PRE-20220316.zip
Solution
To identify the element you can use either of the following locator strategies:
Using xpath and the innerText:
select_file <- robot$findElement("xpath", "//span[text()='PUB004-PRE-20220316.zip']")
Using xpath with class and the innerText:
select_file <- robot$findElement("xpath", "//span[#class='elfinder-cwd-filename' and text()='PUB004-PRE-20220316.zip']")
If I understand you correctly, you want to click on tr parent element containing the span element containing the desired file name in it's text. Right?
But the tr element itself contains that string in it's title.
So, why not simply to use this XPath: ?
"//tr[contains(#title,'20220316')]"
We are building a dynamic table with Thymeleaf.
Some cells, of null values, are holding "-" sign, while cells with values hold some data from the object:
<td th:text="${person.getAddress()} ? ${person.getAddress().getCity() : '-'"}
So the current state is this:
<table border=1>
<tr><td>John</td><td>London</td></tr>
<tr><td>Paul</td><td>-</td></tr>
</table>
Now, we like to add a tooltip, that when hovering the relevant table cell, more data can be seen (e.g. the person's full address).
We found this CSS example for tooltip and we figure out our final result should be something like that:
<td class="tooltip">London
<div class="tooltiptext">
<div>Street: Green</div>
<div>Number: 123</div>
</div>
</td>
But when trying to implement it in Thymeleaf we got stuck.
This is what we tried:
<div th:switch="${person.getAddress()}">
<div th:case="null"><td>-</td></div>
<div th:case="*">
<td> // now what? how to both inject value and the sub divs? </td>
</div>
</div>
Another option we thought of is to create by concatenation the full HTML within a td th:text=...
But both of the ways seems very cumbersome.
You can use the safe navigation operator in combination with the elvis operator instead of your null check.
No need for switch or any logic like this. Create a couple extra tags and move your logic deeper into the html.
Don't use .getAddress(), you can just use .address for properties with correctly named getters/setters.
For example:
<td>
<span th:text="${person.address?.city} ?: '-'" />
<div th:unless="${person.address == null}" class="tooltiptext">
<div>Street: Green</div>
<div>Number: 123</div>
</div>
</td>
Without all the fancy stuff, you could also simply do something like this:
<td th:if="${person.address == null}">-</td>
<td th:if="${person.address != null}">
<span th:text="${person.address.city}" />
<div class="tooltiptext">
<div>Street: Green</div>
<div>Number: 123</div>
</div>
</td>
Given a multiple row user table...
<tr>
<td class="cell--select">
<input class="choice__input" type="checkbox">
</td>
<td>
<div class="user">
<ul class="user-info">
<li class="name">Jane Doe</li>
</ul>
</div>
</td>
</tr><tr>
...
I want to select the row with a given username and click the checkbox on that row. I've tried a number of ways to do this including withText and/or parent() and/or find() etc... but nothing works.
Typically, I would grab all the li.names, check for the correct name and use the index to check the correct checkbox but I also can't figure out a way to accomplish that.
Stuck... ideas?
There is a bit simpler way to achieve the desired behavior. You can use the withText method to identify a table row:
const checkboxToClick = await Selector('tr')
.withText('Jane Doe')
.find(".choice__input");
await t.click(checkboxToClick);
Okay, I found a way. This is a bit more brittle than I'd like but it works. Please do add an answer if there's a better solution!
const checkboxToClick = await Selector('.name')
.withText('Jane Doe')
.parent("tr")
.find(".choice__input");
await t.click(checkboxToClick);
I have 2 objects results and headers being headers generated from _.keys(result[0])
r{
data:{
headers:['head1','head2']
result:[
{head1:'content1',head2:'content2'}
{head1:'content3',head2:'content4'}
{head1:'content5',head2:'content6'}
]
}
I have to create a table dinamically so I create this:
<table class="ui celled table segment">
<thead>
<tr>
{{#headers}}
<th>{{.}}</th>
{{/headers}}
</tr></thead>
<tbody>
{{#result:i}}
<tr>
{{#headers:h}}
<td>{{????}}</td> <-- Here is where I fail to know what to put into
{{/headers}}
</tr>
{{/result}}
</tbody>
</table>
Can someone help me to fill in the blanks. So I can create a table that display the contents
If I remove the {{#headers}} part and I already know the elements <td>{{.head1}}</td> work perfectly the problem is that I'am generating different objects on the fly.
{{#result:i}}
<tr>
{{#headers:h}}
<td>{{result[i][this]}}</td>
{{/headers}}
</tr>
{{/result}}
The reason this works is that the <td> is repeated for each item in the headers array, because it's inside a headers section - so far, so obvious. Because of that, we can use this to refer to the current header (head1, head2 etc). The trick is to get a reference to the current row - and because you've already created the i index reference, we can do that easily with result[i]. Hence result[i][this].
Here's a demo fiddle: http://jsfiddle.net/rich_harris/dkQ5Z/
Is it possible to use regex to remove HTML tags inside a particular block of HTML?
E.g.
<body>
<p>Hello World!</p>
<table>
<tr>
<td>
<p>My First HTML Table</p>
</td>
</tr>
</table>
I don't want to remove all P tags, only those within the table element.
The ability to both remove or retain the text inside the nested p tag would be ideal.
Thanks.
There are a lot of mentions regarding not to use regex when parsing HTML, so you could use Html Agility Pack for this:
var html = #"
<body>
<p>Hello World!</p>
<table>
<tr>
<td>
<p>My First HTML Table</p>
</td>
</tr>
</table>";
HtmlDocument document = new HtmlDocument();
document.LoadHtml(html);
var nodes = document.DocumentNode.SelectNodes("//table//p");
foreach (HtmlNode node in nodes)
{
node.ParentNode.ReplaceChild(
HtmlNode.CreateNode(node.InnerHtml),
node
);
}
string result = null;
using (StringWriter writer = new StringWriter())
{
document.Save(writer);
result = writer.ToString();
}
So after all these manupulations, you'll get the next result:
<body>
<p>Hello World!</p>
<table>
<tr>
<td>
My First HTML Table
</td>
</tr>
</table></body>
I have found this link in which it seems the exact question was asked
"I have an HTML document in .txt format containing multiple tables and other texts and I am trying to delete any HTML (anything within "<>") if it's inside a table (between and ). For example:"
Regex to delete HTML within <table> tags
<td>[\r\n\s]*<p>([^<]*)</p>[\r\n\s]*</td>
The round brackets denote a numbered capture group which will contain your text.
However, using regular expressions in this way relies on a lot of assumptions regarding the content of the <p> tag and the construction of the HTML.
Have a read of the ubiquitous SO question regarding using regular expressions to parse (X)HTML and see #Bruno's answer for a more robust solution.
Possible to some extent but not reliable!
I will rather suggest you to look at HTML parsers such as HTML Agility Pack.