Verify over 200 List elements on a page in Webdriver - java

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");

Related

Iterate through a list of webelements within a table and assert equal each string

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));
}

Selenium: Select all the elements on the page containing any text

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

get element from ArrayList (java)

Struggling with what is probably a simple query to match and return an element in an ArrayList.
I first store a HashSet in the ArrayList because HashSet has no get method and then check the ArrayList (which contains the correct elements, strings of socket references).
List theseSocks = new ArrayList(NodeConnMaster.sockList);
System.out.println("array list contains " + theseSocks);
I then want to iterate through and find the matching ipAddress in the element and once found set a variable to the entire element, so something like -
for (int i =0; i< theseSocks.size(); i++) {
if (theseSocks.toString().contains(ipAddress)) {
int element = theseSocks.get();
System.out.println("the element is " + element);
}
}
but it appears that get needs an index position and I am trying to get based on string contents, not index.
Is there an easier way than deleting all the elements except the matching one and then returning index 0.
Or is ArrayList not the way to go.
The solution was, with SBylemans's help -
Object currentSock = null;
for (int i =0; i< theseSocks.size(); i++)
{
currentSock = theseSocks.get(i);
if (currentSock.toString().contains(ipAddress))
{
System.out.println("the element is " +currentSock);
break;
}
}
Regards
Ralph
You can use stream of Java8 for filtering required elements like:
List wantedList = theseSocks.stream()
.filter(e ->e.toString().contains(ipAddress))
.collect(Collectors.toList())
You're looping over the ArrayList and want to compare based on the String value. But looping like this will immediately also give you the index. Your loop should look something like this:
for (int i =0; i< theseSocks.size(); i++)
{
String currentSock = theseSocks.get(i);
if (currentSock.equals(ipAddress))
{
System.out.println("the element is " +currentSock);
break;
}
}
Or even with a forEach loop
for (String currentSock: theseSocks)
{
if (currentSock.equals(ipAddress))
{
System.out.println("the element is " +currentSock);
break;
}
}
The break is used to interupt the for loop once your element is found.
Additionaly, your if condition will cause a print of every element if the array contains the ipAddress you're looking for.
Edit And then when using java 8, you can also use streams as posted by others.

Find match element in array

completely new to Java, I am trying to find the matched element from one array into another, cannot seem to understand how to do it. Here is a sample of how the data is and how far I've gotten:
In this code and after printing this line, this is how the data is:
ArrayList<String> all_accounts = new ArrayList<String>();
all_accounts.add(acc);
System.out.println("\nArray 1:" + all_accounts);
Result Array 1:
Array 1:[77737320]
Array 1:[88405378]
Array 1:[00056893]
Array 1:[10709816]
ArrayList<String> cancel_accounts = new ArrayList<String>();
cancel_accounts.add(cancel_acc);
System.out.println("\nArray 2:" + cancel_accounts);
Results from Array 2:
Array 2:[77737320]
Array 2:[]
Array 2:[]
Array 2:[]
Stack here, I still cant understand why it doesn't match:
String found = null;
for (String account: all_accounts) {
for (String canceled: cancel_accounts) {
System.out.println(canceled);
found = canceled;
}
System.out.println(found);
if(account.equals(found) ) {
System.out.println(account);
}
}
I need to find the matched element, 77737320 in this case.
Thanks for looking!
+1 for answer from user6904265
However, You need not create a new HashSet. You can use ArrayList.retainAll(). If you want to maintain the all_accounts list, create a new clone and use that instead.
You could implement this as intersection between sets:
Set<String> set_all_account = new HashSet<String>(all_accounts);
Set<String> set_cancel_accounts = new HashSet<String>(cancel_accounts);
set_all_account.retainAll(set_cancel_accounts);
set_all_account.forEach(x -> System.out.println("Element matched: "+x));
Or as said by kartiks in his comment you could call the retainAll method directly on the all_accounts array:
all_accounts.retainAll(cancel_accounts);
all_accounts.forEach(x -> System.out.println("matched element: "+x));
Pay attention with this solution because in this case retainAll applies directly on the ArrayList and modifies it (as you can see the final result is in the all_accounts array). Moreover duplicate elements remain in the result array.
Last implementation (if you want compute intersection and print the result all in one line, also this version keeps duplicate elements):
all_accounts.stream().filter(x -> cancel_accounts.contains(x)).forEach(x -> System.out.println("matched element: "+x));
You can loop through the one list and search the second list for each element in first.
for (String account: all_accounts) {
if (cancel_accounts.contains(account) {
// Match found - do something....
System.out.println(account);
}
}
Just add an equals check to your for - loops (will work even without List#contains method)
for(String account: all_accounts) {
System.out.println(account);
for(String canceled: cancel_accounts){
System.out.println(canceled);
if(account.equals(cancelled)){
//you've found first intersection, cancelled exists in both
System.out.println(canceled + " is the same as " + account);
}
}
}

How to short List having WebElement inside it

I am getting some value from a HTML Table. I am able to print the values but not able to figure out a way by which i can sort them alphabetically.
Code which i used-
List<WebElement> lst = driver.findElements(By.xpath(".//*[#class='user-questions lines']/tbody/tr/td[2]"));
for(WebElement s : lst) {
System.out.println(s.getText()); //This line prints the value
String[] ss = new String[lst.size()];
int i=0;
if(s.getText() != null) {
ss[i]=s.getText();
System.out.println(ss[1]); // But here i am getting null
}
i++;
}
First i tried to sort it using Collections.sort(lst) but got an error message:
Bound mismatch: The generic method sort(List) of type Collections is not applicable for the arguments (List).
Then i tried to put the string value inside a String array and later sort it but here i am getting null value when i tried to put the value into the String array
Today i was stuck in the same thing and found this question asked a way back at the start of this year. When i tried the solutions given i got a NullPointerException.
I wrote a small code and now i am able to short the webelements accoriding to the Natural alphabetical order. Here is the code-
//Here storing the WebElements in the List
List<WebElement> lst = driver.findElements(By.xpath("WebElement XPath"));
//Here creating a String array
String[] str = new String[lst.size()];
for (int i = 0 ; i<str.length ; i++){
str[i] = driver.findElements(By.xpath("WebElement XPath")).get(i).getText();
}//for
Arrays.sort(str);
for(String strr: str){
System.out.println(strr);
}
driver_1.close();
driver_1.quit();
}//try
catch(Exception e){
e.printStackTrace();
driver.close();
driver.quit();
}//catch
First i stored the webelements in the List. Then i created a String array for sorting.In the first for loop i stored the string which i got from the website. After that i sorted them using Arrays.sort method
In the advance for loop i am printing the output.
As i can see the question is very old and if during this time you got much better answer then please share it.
You getting null at System.out.println(ss[1]);
because:
ss[i]=s.getText(); always put in ss[0], because: before if int i=0;.
So ss[1] always empty.
To solve it put int i=0; before for(WebElement s : lst)
Declare i outside foreach loop. and sysout ss[i]. On your first loop execution obviously ss[1] will be null.
Another way around is:
List<WebElement> lst = driver.findElements(By.xpath(".//*[#class='user-questions lines']/tbody/tr/td[2]"));
String[] str = new String[lst.size()];
int i = 0;
for (WebElement ele : lst) {
if(ele.getText()!=null)
str[i] = ele.getText();
i++;
}
Arrays.sort(str); // to sort the array of strings

Categories