Selenium select each div separately that have the same class - java

I am trying to select all the divs withclass tile-consultation in (http://raspored.finki.ukim.mk/Home/Consultations), click each of them and extract some data from each div, I tried using:
List<WebElement> professors = driver.findElements(By.className("tile-consultation"));
ListIterator<WebElement> theListOfProfessors = professors.listIterator();
Thread.sleep(1000);
int i = 1;
while(theListOfProfessors.hasNext()) {
WebElement professorI = driver.findElement(By.cssSelector(".tile-consultation:nth-of-type(2)"));
professorI.click();
Thread.sleep(1000);
close = driver.findElement(By.cssSelector("button.btn-close"));
close.click();
Thread.sleep(1000);
}
but how can I change 1 to the 2nd, 3d and so on in a while loop?

You've already got the work done. You've already found the web elements and made a listiterator here:
List<WebElement> professors = driver.findElements(By.className("tile-consultation"));
ListIterator<WebElement> theListOfProfessors = professors.listIterator();
The findElements method will return a collection of elements matching the selector. There's no need for you to retrieve the elements again like you're trying to with driver.findElement(By.cssSelector(".tile-consultation:nth-of-type(x)" inside that loop. You merely need to iterate over the listiterator theListOfProfessors that you already created. E.g. something to the effect of
while(theListOfProfessors.hasNext()) {
WebElement elem = theListOfProfessors.next()
// do something with elem
}

Related

Searching for the solution to make selenium scrape products

I'm working on my pet project. As for now, I want to parse product name and its' price.
Visit shoppingSite, press button under the map, then press it once again on the right side. Choose upper delivery option and you will be redirected to the shop. Near the search section the menu bar located. Click and choose any category. When you"ll reach the page with products, you can open developers' tool.
So, I don't know how to make selenium give me names and prices.
What I have tried:
for (int i = 0; i < 6; i++) {
Thread.sleep(1000);
JavascriptExecutor javascriptExecutor = (JavascriptExecutor) driver;
javascriptExecutor.executeScript("window.scrollBy(0, 200)");
Thread.sleep(1000);
}
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("div.category.page__content")));
WebElement element = driver.findElement(By.cssSelector("div.category.page__content"));
System.out.println(element.getText());
Selenium tells me that
Exception in thread "main" org.openqa.selenium.TimeoutException:
Expected condition failed: waiting for visibility of element
Scrolled, waited for the elements' visibility. Did not help me.
Will be grateful for any tip.
To get the products names you can do this:
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("a.product-title")));
Thread.sleep(300);
List<WebElement> productNames= driver.findElements(By.cssSelector("a.product-title"));
List<WebElement> productPrices= driver.findElements(By.cssSelector("div.current-price"));
If you want to get these names and prices as a List of Strings you can use this method:
public List<String> getElementsListTexts(By element){
List<String> texts = new ArrayList<>();
List<WebElement> list = driver.findElements(element);
for (WebElement el : list ){
texts.add(el.getText());
}
return texts;
}
So it will be
webDriverWait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("a.product-title")));
Thread.sleep(300);
List<String> names = getElementsListTexts(By.cssSelector("a.product-title"));
List<String> prices = getElementsListTexts(By.cssSelector("div.current-price"));

Selenium java, wait for element by xpath and matching text in case of xpath matching multiple elements

I'm trying to wait for an element with certain css and inner text.
I have multiple elements satisfying the css condition (element could be visible/invisible) and selenium ExpectedConditions is not behaving the way I want.
If I try to find elements by css first and then filter out myself, I sometimes miss the intended element because some elements might not have loaded.
By cssBy = By.css("CSS_HERE");
wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(cssBy);
List<WebElement> tabs = driver.findElements(cssBy);
WebElement element =
tabs.stream().filter(tab -> tab.getText().contains("TARGET_TEXT")).findAny().get();
the above snippet sometimes misses the indented elements which satisfy the CSS but have not loaded when selenium checked for it. This results in me getting no matching element in second part.
I tried with textMatches and locator
By cssBy = By.css("CSS_HERE");
wait.until(ExpectedConditions.textMatches(cssBy, "TARGET_TEXT");
....
But I think the above snippet is selecting the first element it can find matching CSS and waits for its text to be TARGET_TEXT which is not my intention.
Any suggestions to wait for text match in case of multiple elements matching the locator?
Instead of presenceOfAllElementsLocatedBy() you need to induce WebDriverWait for the visibilityOfAllElementsLocatedBy() and you can use the following Locator Strategy:
By cssBy = By.css("CSS_HERE");
wait.until(ExpectedConditions.visibilityOfAllElementsLocatedBy(cssBy);
List<WebElement> tabs = driver.findElements(cssBy);
WebElement element =
tabs.stream().filter(tab -> tab.getText().contains("TARGET_TEXT")).findAny().get();
You could write your own Expected Conditions method here is an example for textMatches()
public static ExpectedCondition<List<WebElement>> textMatches(final By locator, final Pattern pattern) {
return new ExpectedCondition<List<WebElement>>() {
#Override
public List<WebElement> apply(WebDriver driver) {
String currentValue;
List<WebElement> elements = driver.findElements(locator);
List<WebElement> matchingElements = new ArrayList();
for(WebElement element : elements){
currentValue = element.getText();
if (pattern.matcher(currentValue).find()){
matchingElements.add(element);
}
}
return matchingElements;
}
#Override
public String toString() {
return "matching elements for " + locator;
}
};
}

Selenium WebDriver JAVA a loop or other way to get results

If you want to solve the question, when you want, one should write out the pesel numbers on the form screen.
My code looks like this:
WebElement element = driver.findElement(By.id("pesel"));
element.sendKeys("74091943656");
Simple loop:
String[] values = {"88010901", "827272", "28272"};
for (String value: values) {
// code for loading the form
WebElement element = driver.findElement(By.id("pesel"));
element.sendKeys(value);
// continue with saving the form?
}

Difference WebElements Vs WebElement in selenium

I am fetching date webelements from facebook and I am looping it by using the below code.
public class select_facebook {
public static void main(String[] args) throws Exception {
WebDriver driver = new FirefoxDriver();
driver.get("http://www.facebook.com");
List<WebElement> days = driver.findElements(By.xpath("//select[#id='day']"));
System.out.println(days.size());
for (int i=0;i<days.size();i++) {
System.out.println(days.get(i));
}
}
}
But I get output as
1
[[FirefoxDriver: firefox on XP (58765a0e-31a0-40bc-8565-3418ae54682c)] -> xpath: //select[#id='day']]
But same code in for loop if I use System.out.println(days.get(i).gettext());
It list all the dates 1 to 31.
My question is then why should I call this as
List<WebElement> days = driver.findElements(By.xpath("//select[#id='day']"));
Because even the size of the webElements is just :1
System.out.println(days.size());
instead I could have called it as
WebElement days = driver.findElement(By.xpath("//select[#id='day']"));
You will get list of Elements in return of
List<WebElement> days = driver.findElements(By.xpath("//select[#id='day']"));
because there could be more than 1 element by same id ('day').
You should have to focus on what you exactly need. If you want more elements use FindElements() and when you are interested in only one element then use FindElement().
If there are more number of elements and if you use FindElement() it returns you very first element and rest others are get neglected. So make sure about your requirement.
This is because an id value can be given to only one HTML element. In other words, multiple elements cannot have the same id value on the same page in valid HTML DOM only.
FindElement() it returns you WebElement:
WebElement SINGLEELEMENT= driver.findElement(By.SELECTOR("//TAGNAME[#ATTRIBUTENAME='ATTRIBUTEVALUE']"));
FindElements() it returns you WebElements i.e List<WebElement> of multiple elements. It return 1 if only one element present in it or multiple if presents more:
List<WebElement> LISTOFELEMENTS= driver.findElements(By.SELECTOR("//TAGNAME[#ATTRIBUTENAME='ATTRIBUTEVALUE']"));
You can put looping on it to get each one element and work on each individually.

Selenium WebDriver- FindElements returns elements which are not displayed

Am using Eclipse, TestNG and Selenium 2.32.
List <<f>WebElement> elementOption = driver.findElements(By.xpath("//li[#role='option']"));
The code driver.findElements(By.xpath("//li[#role='option']")); returns all the elements which are not displayed as well. The above 'elementOption' now contains all the elements, even the elements that are not displayed in the webpage. We can use IsDisplayed along with findElement method which will return only the element which is displayed in the webpage. Is there anything similar to IsDisplayed that can be used with findElements which will return only the elements that are displayed?
In C#, you can create WebDriver extension method like this:
public static IList<IWebElement> FindDisplayedElements<T>(this T searchContext, By locator) where T : ISearchContext {
IList<IWebElement> elements = searchContext.FindElements(locator);
return elements.Where(e => e.Displayed).ToList();
}
// Usage: driver.FindDisplayedElements(By.xpath("//li[#role='option']"));
Or use Linq when you call FindElements:
IList<IWebElement> allOptions = driver.FindElements(By.xpath("//li[#role='option']")).Where(e => e.Displayed).ToList();
However, I am aware of that extension methods and Linq don't exist in Java. So you probably need to create you own static method/class using the same logic.
// pseudo Java code with the same logic
public static List<WebElement> findDisplayedElements(WebDriver driver, By locator) {
List <WebElement> elementOptions = driver.findElements(locator);
List <WebElement> displayedOptions = new List<WebElement>();
for (WebElement option : elementOptions) {
if (option.isDisplayed()) {
displayedOptions.add(option);
}
}
return displayedOptions;
}
If the elements which you are trying to retrieve contains attributes like style having display values, then you might need to just change your XPATH to get only displayed elements.
List <WebElement> elementOption = driver.findElements(By.xpath("//li[#role='option'][contains(#style,'display: block;')]"));
or
List <WebElement> elementOption = driver.findElements(By.xpath("//li[#role='option'][not(contains(#style,'display: none'))]"));

Categories