I want to select all the elements on the page containing any text.
Only elements actually containing texts themselves, not the parent elements containing texts in their child elements only.
This XPath is matching elements containing any non-empty texts
//*[text() != ""]
However this
List<WebElement> list = driver.findElements(By.xpath("//*[text() != '']"));
gives me a list of all elements containing texts themselves or in their child elements.
I can iterate over this list with something like this to get elements actually containing texts themselves into real list
List<WebElement> real = new ArrayList<>();
for(WebElement element : list){
js = (JavascriptExecutor)driver;
String text = js.executeScript("""
return jQuery(arguments[0]).contents().filter(function() {
return this.nodeType == Node.TEXT_NODE;
}).text();
""", element);
if(text.length()>0){
real.add(element);
}
But this is a kind of workaround.
I'm wondering is there a way to get the list of elements actually containing any text doing that directly or more elegantly?
List<WebElement> elementsWithOwnText = new ArrayList<WebElement>();
List<WebElement> allElements = driver.findElements(By.xpath("//*"));
for (WebElement element: allElements) {
List<WebElement> childElements = element.findElements(By.xpath(".//*"));
String text = element.getText();
if (childElements.size() == 0 && text.lenght() > 0) {
elementsWithOwnText.add(element);
}
}
Be aware of org.openqa.selenium.StaleElementReferenceException. While looping allElements any of them may be no more attached to the page document (dynamic content f.e.).
You can try this:
it selects all leaf elements with text.
List<WebElement> list = driver.findElements(By.xpath("//*[not(child::*) and text()]"));
for (WebElement webElement : list)
System.out.println(webElement.getText());
Until you find the xpath that you need, as a temporary solution, I would recommand to try the below iteration too (even though is not so efficient as a direct xpath).
In my case it took 1 minute to evaluate 700 nodes with text and returned 152 elements that have its own text:
public static List<WebElement> getElementsWithText(WebDriver driver) {
return driver.findElements(By.xpath("//*[normalize-space() != '']"))
.stream().filter(element -> doesParentHaveText(element))
.collect(Collectors.toList());
}
private static boolean doesParentHaveText(WebElement element) {
try {
String text = element.getText().trim();
List<WebElement> children = element.findElements(By.xpath("./*"));
for (WebElement child: children) {
text = text.replace(child.getText(), "").trim();
}
return text.trim().replace("[\\n|\\t|\\r]", "").length() > 0;
} catch (WebDriverException e) {
return false; //in case something does wrong on reading text; you can change the return false with thrown error
}
}
this could help:
source
List<String> elements = driver.findElements(By.xpath("//a")).stream().map(productWebElement -> productWebElement.getText()).distinct().collect(Collectors.toList());
// Print count of product found
System.out.println("Total unique product found : " + elements.size());
// Printing product names
System.out.println("All product names are : ");
elements.forEach(name -> System.out.println(name));
Related
List<WebElement> deleteBtn = driver.findElements(By.xpath("//div[#class='btn']//div[#class='deleteUsers']"));
public void clickDeleteBtn(WebElement element) {
element.click();
/* Here I want to retrieve the index of the element passed in the function */
}
main() {
clickDeleteBtn(deleteBtn.get(5));
}
Suppose the findElements() above gives me a list of 10 WebElements and I pass element indexed 5 in clickDeleteBtn(). How, in the function, can I get the index of the element passed in?
I have tried element.toString() but it only gives me:
Element: [[ChromeDriver: chrome on WINDOWS (f4f6be3ed1e2a964a2dc8f0d848d3e87)] -> xpath: //div[#class='btn']//div[#class='deleteUsers']]
No information about the index of the element suggested.
I'd really appreciate your advice! Thanks
WebElement itself doesn't know his index inside a list you stored it in.
The only way to know the index of given WebElement object is to iterate over the list of elements and compare the given WebElement against the current WebElement in the list, as following:
WebElement element = unknownWebElement;
List<WebElement> list = entireListOfWebElements;
for(int i=0;i<list.size();i++){
if(element = list.get(i)){
return i;
}
return -1;
}
My goal is to iterate through a list of webelements (generated upon using a filter) within a table, spread across multiple pages and assert equal each string within those webelements with a provided string in a Cucumber step.
These webelements consist of strings (1 webelement = 1 string) placed within a column within the table.
All these strings equal.
Their data-testid is the same.
These webelements are spread across a number of pages (dynamic).
The loop would end when reached the last page, which contains a button of
which attribute text becomes disabled (when last page is displayed).
Here's what I started writing, but I'm a bit stuck at the moment. If
you can advise me how to continue further, I'm really greateful. At the moment, I get this error, when I execute the test.
1. Tests in error:
stale element reference: element is not attached to the page document
2. Not sure how to integrate the assert.
Actual code:
By nextPageBtn = By.xpath("//*[#data-testid='asd']");
By disabledNextPageBtn = By.xpath("//*[#data-testid='asdf']");
By filterValue = By.xpath("//*[#data-testid='asdf1']");
public List<String> sameFilterResultIsDisplayedForAllRows() {
List<WebElement> filterResultsList = new ArrayList<WebElement>();
List<String> stringsFromFilterResultsList = new ArrayList<String>();
boolean disabledNext = false;
do {
click(driver, nextPageBtn);
filterResultsList.addAll(driver.findElements(filterValue));
try {
getValueFromSomeAttribute(driver, disabledNextPageBtn,
"randomElementAttribute");
disabledNext = true;
} catch (Exception e) {
}
} while (disabledNext == false);
for (WebElement filterResultsList) {
System.out.println(a.getText());
try {
stringbookings.add(a.getText());
} catch (Exception e) {
}
}
return stringsFromFilterResultsList;
}
The assert would be something like this:
#Then("the results filtered for all rows contain value {string}")
public void expectedFilteredValues(String expectedFilteredValueString) {
String expectedFilteredResult;
expectedFilteredResult = 'randomString';
List<String> actualFilteredValues = javaMethods.sameFilterResultIsDisplayedForAllRows();
Assert.assertEquals(expectedFilteredResult, actualFilteredValues);
The issue resided with addAll(), prolly because of this:
"The behavior of this operation is undefined if the specified collection is modified while the operation is in progress. (Note that this will occur if the specified collection is this list, and it's nonempty.)"
I had to use add(), and create a secondary array within a for loop.
for (WebElement element : elements) {
stringsFromFilterResultsList.add(bookingTypeElement.getText());
}
Then, the assert is like this:
List list = javaMethods.
sameFilterResultIsDisplayedForAllRows();
for (int i = 0; i < list.size(); i++) {
Assert.assertEquals(expectedValue, list.get(i));
}
I currently have the following code which locates the Show id, then the elements in the option tags beneath and prints them out one by one.
WebElement dropDown = driver.findElement(By.id("Show"));
List<WebElement> options = dropDown.findElements(By.tagName("option"));
for (WebElement el : options) {
System.out.println(el.getAttribute("text"));
}
How can I modify it so that it builds up an array of all the text elements, instead of printing them out one by one?
In WebDriver we've a method in Select class to get all the options available in select tag.
List<WebElement> options = new Select(driver.findElement(By.id("Show"))).getOptions();
To get all the option values arrays follow below logic.
public String[] getOptions() {
String optionValues="";
List<WebElement> options = new Select(driver.findElement(By.id("Show"))).getOptions();
for(WebElement eachOption : options) {
optionValues+=eachOption.getText()+",";
}
return optionValues.split(",");
}
You just need to declare another array (or list, depending on your preference) and change your System.out.println() statement.
For a List of whatever object the text attribute is:
for(WebElement el : options){
secondList.Add(el.getAttribute("text"));
}
For an array, it would be easiest using indexing:
for(int i = 0; i < options.Size(); i++){
secondArray[i] = options.Get(i).getAttribute("text");
}
I have some 200 elements who's mark-up is as follows:
<span id="1356329740258" class="pagename">Sport & leisure</span>
<span id="1356329740259" class="pagename">Food & drink</span>
<span id="1356329740260" class="pagename">Household</span>
<span id="1356329740261" class="pagename">Gardening</span>
I can access them with Webdriver in a fairly ugly manner:
List<WebElement> elements;
elements = driver.findElements(By.xpath( ".//*[starts-with(#id, '135')]"));
...Because each starts with a '135'.
But driver.findElement(By.cssSelector(".pagename");
...does not work, perhaps something to do with the '' tags
What I now need to do, is do a .getText() for each element in the list and verify it against the expected, corresponding array value. I'm starting off thinking of this method:
String[] expected = {"Sport & leisure", "Food & drink", "Household", "Gardening"};
List<WebElement> elements = select.find.Elements(By.xpath( ".//*[starts-with(#id,'135')]"));
// compare #array items with #found elements in List
if (expected.length != elements.size()) {
System.out.println("the wrong number of elements were found");
}
// check value of every pagename class element equals expected value
for (int i = 0; i < expected.length; i++) {
String elementsValue = elements.get(i).getAttribute("value");
if (elementsValue.equals(expected[i])) {
System.out.println("passed on: " + elements);
} else {
System.out.println("failed on: " + elements);
}
}
This has the obvious limitation of potentially having to store 200 odd text strings in the array and will therefore become unwieldy. Is there a more elegant solution? I could read the array values in from a .csv I guess and used Parameterized runner but then I'd still need to declare each value in the constructor right?
You can use the Lists contains or containsAll function to determine equality. So basically like this:
final List<String> expectedElements = readFromCSV("expectedElements.csv");
final List<WebElement> elements = select.find.Elements(By.xpath( ".//*[starts-with(#id,'135')]"));
final List<String> stringElements = new ArrayList<>(elements.length);
for (WebElement element : elements) {
stringElements.add(element.getAttribute("value"));
}
final boolean isSame = stringElements.containsAll(expectedElements);
This is not a direct answer to your question, but only a few corrections to your code:
1.
You can replace the code that you consider "ugly":
List<WebElement> elements = select.findElements(By.xpath(".//*[starts-with(#id,'135')]"));
With a code that finds the elements using their class attribute:
List<WebElement> elements = select.findElements(By.xpath("//span[#class='pagename']"));
2.
Since non of these elements has a value attribute, you should replace the following line:
String elementsValue = elements.get(i).getAttribute("value");
With:
String elementsValue = elements.get(i).getAttribute("innerHTML");
I am using Jsoup to try and read all the elements in the html and loop through and do stuff based on the the type of element.
I'm not having any luck, I can't find the proper method to check for the values of each element.
Any suggestions?
This is my latest attempt:
Elements a = doc.getAllElements();
for(Element e: a)
{
if( e.val().equals("td"))
{
System.out.println("TD");
}
else if(e.equals("tr"))
{
System.out.println("TR");
}
}
This does not print anything.
Try this one:
Elements tdElements = doc.getElementsByTag("td");
for(Element element : tdElements )
{
//Print the value of the element
System.out.println(element.text());
}
Better you select each element by its tags:
Elements tdTags = doc.select("td");
Elements trTags = doc.select("tr");
// Loop over all tdTags - you can do the same with trTags
for( Element element : tdTags )
{
System.out.println(element); // print the element
}
e.tag() will do that
Elements tdElements = doc.getElementsByTag("td");
for(Element element : tdElements )
{
//Print the value of the element
System.out.println(element.tag());
}