I have a List of Webelements out which a random element is selected.
Now having a random webelement, i need to locate all the child elements in it. How can i acheive this in pagefactory.
In selenium, this was working:
List<WebElement> listOfElements = driver.findElements(By.xpath("//locator"));
WebElement randomElement = // Code to get a random element out of this list.
String title = randomElement.findElement(By.xpath(".//[#id='title']"));
In page factory, i have tried:
#FindBy(xpath="//locator")
List<WebElement> listOfElements;
#FindBy(id="title");
WebElement title;
WebElement randomElement = // Code to get a random element out of this list.
Stuck at how to fetch title in the random element using page factory annotations.
I know this would work:
String title = randomElement.findElement(By.xpath(".//[#id='title']"));
As i am using page factory annotations in the entire project, is there a way to achieve the same.
You make a public method in your page object that returns a list of webelements.
public List<WebElement> getTitles(WebElement randomElement) {
return randomElement.findElements(by.id("title"));
}
Probably too late for you, but in case its helpful to anyone else:
It is possible to do this, but not simple and requires customising some of WebDriver's inner classes.
I discussed how I went about solving this problem using 'block' classes in a blog post earlier this year. This not not something trivial, but there is a github project containing the code that I used if you want to dig in to to use yourself.
In essence I use each of the located WebElement to generate a new 'PageObject' class called a block. A long as each of the random elements is the same, you can use the PageFactory notation to find the child WebElements.
Do note, there is significant overhead in all of this, so if you are merely trying to get the title you would be much better off using the stream API with a chained findElements() call.
Related
As far as my understanding goes, FindBys Annotation in pagefactory returns you elements which satisfies all the condition mentioned inside. The code below always returns 0 elements.
Similarly,If I'm using FindAll annotation with same id and Xpath attribute it is returning me 2 webelements. Can anyone help me in understanding the results.
#FindBys
(
{
#FindBy(xpath="//*[#id='ctl00_ctl00_divWelcome']"),
#FindBy(id="ctl00_ctl00_divWelcome")
}
)
public List<WebElement> allElementsInList;
Your understanding is wrong.
The documentation for #FindBy says:
Used to mark a field on a Page Object to indicate that lookup should use a series of #FindBy tags in a chain as described in org.openqa.selenium.support.pagefactory.ByChained
Further, the documentation for ByChained says:
Mechanism used to locate elements within a document using a series of other lookups. This class will find all DOM elements that matches each of the locators in sequence, e.g. driver.findElements(new ByChained(by1, by2)) will find all elements that match by2 and appear under an element that matches by1.
So in your example, you are looking for an element by XPath with a specific ID, and then its child element by the same ID ... which, of course, is not going to return anything.
Can I use #Findby and pass certain values as parameter?
#FindBy("//div[contains(#class,'gallery_grid_image_caption gallery_grid_image_caption_padding')]"[$INDEX])
I know I can do this while using findElement. Kindly let me know if there is a solution/work around.
What I want to do it is let's say there is a for loop and there is a list of elements in a page. Now let's say the only thing that is changing among these fields is the bit of the xpath. //div/1, //div/2 .... What I want to do is represent one element for all these elements and pass the ending values as parameter.
I believe what you are trying to do is something like this :
#FindBy(xpath = "//div[contains(#class,'gallery_grid_image_caption gallery_grid_image_caption_padding')]")
public WebElement yourElement;
SO-9028757 should provide you more context.
Example code is something like this, (this was an interview question asked for me recently)
List linkElements = driver.findElements(By.tagName("a"));
I would say, through List you can dynamically add, access and remove objects of the same type. Also, it won't mind even if it has No contents.
Generally you choose a Data Structure according to your needs. Here while doing a findElements() search we are saying that we want all the elements with the given structure in DOM and we can't be always sure about it's size in advance. Using a fixed sized array in such conditions won't make much sense.
I hope it helps :)
List represents an ordered list of objects, meaning you can access the elements of a List in a specific order, and by an index too. You can also add the same element more than once to a List. List allows null elements and you can have many null objects in a List
You will get all the result in a particular order one by one. It is also allow you to add duplicates. Our result can have duplicates which mainly we need in automation , but if your requirement is different and you do not need duplicates then you can use other collection type.If you use set then it will not allow duplicates and it's an unorder presentation of object.
We use List because when we are using findElements() instead of findElement() then we are expecting that locator will return us more than 1 element(Not in every case or scenario). So it's a good practice to use List so our data is saved in list in a ordered manner so we can use them properly.
Generally I am using List in below manner:-
List<WebElement> allOptions = dropDown.findElements(By."OUR Locator");
for ( WebElement we: allOptions) {
dropDown.sendKeys( Keys.DOWN ); //simulate visual movement
sleep(250);
if ( we.getText().contains( text ) ) select.selectByVisibleText("Value1");
}
You can also do it in many ways refer below:-
https://sqa.stackexchange.com/questions/8029/how-to-iterate-a-list-of-webelements-and-print-the-values-one-by-one-to-perform
Here is more detail version which help you to identify that when to use list:-
http://java67.blogspot.in/2013/01/difference-between-set-list-and-map-in-java.html
Adding and Accessing Elements
To add elements to a List you call its add() method. This method is inherited from the Collection interface. Here are a few examples:
List listA = new ArrayList();
listA.add("element 1");
listA.add("element 2");
listA.add("element 3");
You can access them via index as below:-
String element0 = listA.get(0);
String element1 = listA.get(1);
String element3 = listA.get(2);
System.out.println(element0+" "+element1+" "+element3);
Hope it will help you :)
So I have a variable that uses a method. Something like this:
By locator = By.id("something-"+getDynamicId());
Usually the id changes numbers like this: something-1 to something-2 (not those exact numbers, always different). I have a way of getting the number it changes to by calling the getDynamicId() method. The problem is, when I start my test the id is set at the start and whenever I click on a specific button, the id changes but my variable does not. Is it possible for the locator variable to call getDynamicId() everytime locator is called? Maybe everytime I click on the specific button the locator reloads?
I have looked up ClassLoaders but I do not know how to use it nor do I know if it can even do what I want.
Instead of doing that, i've gotten around dynamic ID's by using CSS.
driver.findElement(By.cssSelector("element[id^='something']"));
This means "find an <element> tag, that has an id attribute that starts with "something"
If there are multiple elements that match this, then you need to increase your specificity. You can do this by selecting some parent.
div.some-div [id^='something']
^ the space here meaning "a descendent of.."
this page may help with formulating CSS selectors like this for Selenium.
Also, in addition to what #sircapsalot has done, you can use xpath for working with dynamic ids as below (As you said in comment, there are multiple element having ids that contains "something"):
List<WebElement> elements = driver.findElements(By.xpath("//tag[contains(#id,'something-')]"));
//Say you want to click on 4th element now then use the below code(assuming the list has more than or equal to 4 elements in it)
elements.get(3).click();
Here, it will locate all the elements with tag as "tag" and has "id" that contains the text "something-" and then click on the 4th element in the list.
But, if you want to retrieve the dynamic ID and use it for clicking on the specific element, you can create a new method like this :
public static void clickOnDynamicElementById(String partial_locator_id){
//Code for getting dynamic id and then storing the retrieved dynamic ID in a string, say "dynaID";
String locator_id_to_click = partial_locator_id + dynaID;
driver.findElement(By.id("locator_id_to_click")).click();
}
Then, you can call this method directly in your main method as below:
clickOnDynamicElement("something-"); //because main method is also static, it can call the method directly.
I need to get all elements with a class inside a wrapper div. I have done this before with php and the css selector would look something like this:
$this->elements($this->using('css selector')->value('div.active tr[class="theRow"]'));
Now this would give me all foo elements inside the wrapper active but I dont know how to do it with Java. I want a list with all the webElements like so:
List<WebElement> list = driver.findElements(By.cssSelector(".active,.theRow"));
This however will give me all theRow elements, eaven those outside the active wrapper. any sugestions?
the code below also give all theRow elements as expected:
List<WebElement> list = driver.findElements(By.className("theRow"));
but this gives me a empty list
List<WebElement> list = driver.findElements(By.cssSelector("tr[class='row-hover']"));
Is there a chance the class attribute of the span contains multiple values?
If so, then you may have problems using 'class=' as it would not match elements which have two or more classes.
If so, then try this;
List<WebElement> list = driver.findElements(By.cssSelector("div.active span.foo"));
When ever I access class outside of ".", I would always use "[class*='foo']" because you cannot guarantee the order in which multiple values appear in the class attribute, but would always use the "." notation where possible. However, whenever I use "class" in Xpath I always use "[contains(#class,'foo')] because Xpath always treats "class" as a string literal, where as CSS "." can cope with multiple values