Difference WebElements Vs WebElement in selenium - java

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.

Related

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

How to handle year and month drop-downs using Selenium?

Can anyone guide me, how to select a year and month drop-down using Java in Selenium?
Had used the code mentioned below, But it didn't work.
Java Code:
{
List<WebElement> NomDOBYear = driver.findElements(By.className("ui-datepicker-year"));
Select selectYear= new Select((WebElement) NomDOBYear);
selectYear.selectByVisibleText("1991");
WebElement NomDOBMonth = (WebElement) driver.findElements(By.className("ui-datepicker-year"));
Select selectMonth= new Select(NomDOBMonth);
selectMonth.selectByVisibleText("Nov");
}
Use
driver.findElement(By.className("ui-datepicker-year"));
instead of
driver.findElements(By.className("ui-datepicker-year"));
When you use driver.findElements(By.className("ui-datepicker-year")) you are storing return elements in a list(I am expecting that there are 2 or more elements with the same className as "ui-datepicker-year").
So, if that is the case then you should note that the constructor "Select" class of selenium takes a 'WebElement' as a parameter which can be the first or any element stored inside the list.
Then your code should be written like -
List<WebElement> NomDOBYear = driver.findElements(By.className("ui-datepicker-
year"));
Select selectYear= new Select(NomDOBYear.get(0));//first element of the list
selectYear.selectByVisibleText("1991");
WebElement NomDOBMonth = driver.findElement(By.className("ui-
datepicker-month"));// you got the class name wrong here
Select selectMonth= new Select(NomDOBMonth);
selectMonth.selectByVisibleText("Nov");
Otherwise if there is only 1 element with className "ui-datepicker-year" or "ui-datepicker-month" in the entire DOM then your code should be written like -
WebElement NomDOBYear = driver.findElement(By.className("ui-datepicker-year"));
Select selectYear= new Select(NomDOBYear);
selectYear.selectByVisibleText("1991");
WebElement NomDOBMonth = driver.findElement(By.className("ui-datepicker-month"));
Select selectMonth= new Select(NomDOBMonth);
selectMonth.selectByVisibleText("Nov");
I would still recommend using XPath for finding elements.

How to get the number of elements (size) from List of WebElements

I am trying to verify the number of elements (in this case links) in the sub menu of the webpage with an Assert of great than 3 elements before clicking one of the links, but the Assert errors with a size of zero.
#Test
public void top3NewsStoriesHoverTest()
{
WebElement hoverNews = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector("[class^='_3EBT0 _2m2om uVwkS']")));
Actions action = new Actions(driver);
action.moveToElement(hoverNews).build().perform();
List<WebElement> subMenuTopStories = driver.findElements(By.cssSelector("[class^='_1yCjv'] [class^='dHmN_']"));
//System.out.println(subMenuTopStories.size());
assertEquals(subMenuTopStories.size(), greaterThan(3));
WebElement subMenu = wait.until(ExpectedConditions.elementToBeClickable(By.cssSelector("[class^='dHmN_']")));
subMenu.click();
}
You're comparing for equality between an integer and a matcher. What you probably want is Hamcrest's assertThat, not assertEquals (assuming you're using hamcrest, which it looks like you're doing)

Call Individual Index of WebElement List

If anyone needs reference or background here was my first question asked
Retrieving a list of WebElements and Identifying them
At this point I have retrieved a list of WebElements
#FindBy(css = "td[id^=ctl00_SomeGridData_ucControlList_trgControlList_ctl00__]")
List<WebElement> allGridData;
At this point in my code I can use the web Element to call the index for example
allGridData.get(0).click
however the list is not strictly integers for example if I access at the row level <tr> it would be:
ctl00_SomeGridData_ucControlList_trgControlList_ctl00__0
but if I were to call a link within row they are table data <td> broken into divs that look like this:
ctl00_SomeGridData_ucControlList_trgControlList_ctl00__ctl04_lbView
ctl00_SomeGridData_ucControlList_trgControlList_ctl00__ctl04_hlTestPlan
or this
ctl00_SomeGridData_ucControlList_trgControlList_ctl00__ctl07_lbView
ctl00_SomeGridData_ucControlList_trgControlList_ctl00__ctl07_hlTestPlan
Since all the WebElements start with a common css selector
#FindBy(css = "td[id^=ctl00_SomeGridData_ucControlList_trgControlList_ctl00__]")
List<WebElement> allGridData;
how can identify a specific index that is a char value (ie ctl107) vs just an integer?
Assuming you want two lists, one for the View Details and one for the View Test Plans, you need the $ (ends with):
#FindBy(css = "a[id$=lbView]")
List<WebElement> allDetailViewLinks;
#FindBy(css = "a[id$=hlTestPlan]")
List<WebElement> allTestPlanLinks;
But my best guess is that you want to click on a link in a specific row rather than based on index in a list of Web Elements. For instance based on the text in the td in <tr id="ctl00_SoxMain_ucControlList_trgControlList_ctl00__0" class="rgRow">
<td class="rgExpandCol" valign="top"/><td valign="top">AL-01</td>.
You need a method to get the row with specific text in the td.
WebElement getRow(String specificValue) {
return driver.findElement(By.xpath("//td[text()='"+specificValue+"']"))
.findElement(By.xpath(".."));
}
Then you can make the methods for detail view and test plan view.
public void openDetailsView(String specificValue) {
getRow(specificValue)
.findElement(By.cssSelector("a[id$=lbView]"))
.click();
}
public void openTestPlanView(String specificValue) {
getRow(specificValue)
.findElement(By.cssSelector("a[id$=hlTestPlan]"))
.click();
}

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