Xpaths are changing dynamically - selenium

Im currently auotmating an web application using a selenium framework.For locating the elements im using xpath. For particular drop downs(with a filter in it) in the application the xpaths are getting changed frequently(for the available options in the drop down).The options in the drop down are inside the span section. Is there are any way to handle the dynamic xpath? Currently im using firebug to get the xpaths.

You can always use XPATH text matching if you know what to expect.
//*[text()='DropdownValue']
if you have list elements in the dropdown:
//li[text()='DropdownValue']
If you could provide example of the dropdown structure or just the HTML code of your case, it would be much easier to find a suitable solution.

You could get every element inside your dropdown, get the index of the element which contains the text and then click it:
List<WebElement>elems = driver.findElements(By.xpath("xpath from every element"));
elems.get(getIndex(elems, "Abereen District")).click();
private int getIndex(List<WebElement>elems, String elemText) {
for(int i = 0; i < elems.size(); i++)
{
if(elems.get(i).getText().contains(elemText))
return i;
}
return -1;
}

Related

Selenium Web driver - loop over elements and click if matches

i'm in day 2 of my selenium class, need help in finding an efficient way of looping over elements and if matches click the link address.
I want to navigate from classFrame to navList and loop over to find the match and click.
public void switchFrames() {
driver.navigate().to("https://seleniumhq.github.io/selenium/docs/api/java/");
driver.switchTo().frame("classFrame");
/* List<WebElement> elements = driver.findElements(By.className("navList"));
for (WebElement element : elements) {
System.out.println(element.findElement(By.xpath(".//li/a")).getText());
}
*/
List<WebElement> items = driver.findElements(By.cssSelector("ul li"));
if ( items.size() > 0 ) {
for ( WebElement we: items ) {
we.findElement(By.linkText("Deprecated")).click();
}
}
driver.findElement(By.linkText("Deprecated")).click();
driver.close();
}
The main part you are missing and the reason you can't find the element you are looking for is because it's in a frame. In order to access elements in a frame with Selenium, you need to switch the driver context to the frame. You do that using driver.switchTo().frame(). Once you are done interacting with the frame, switch back to the default context using driver.switchTo().defaultContent().
Having said that... let me offer you some more advice since you are just starting out. There are several ways to do this. One way is like what you attempted... grab an element, find a child, loop through those children looking for the link you want. I prefer the more direct approach since we can search for the exact link using an XPath. What you want to do is to click the DEPRECATED link on the navbar. You could just use the By.linkText() locator and that will work but you want to be careful, especially with a page like this that has so many links, to not click on a link you didn't intend to. The way you do that is to narrow the search to the specific area you expect the link to be in, the navbar. Once you narrow the search there, you can quickly and safely find the link you are looking for. I prefer to do it in a single search using an XPath but you could use say a CSS selector to find the navbar area and then use By.linkText() to find the link, e.g.
driver.findElement(By.cssSelector("ul[title='Navigation']").findElement(By.linkText("Deprecated").click();
In that case, you will be scraping the page twice. It's not likely a big performance hit, I just prefer to use a single locator when it makes sense. I would suggest that since you are likely to use this code over and over that you put it in a function and pass it the link name, e.g.
public void clickNavbar(String linkName)
{
driver.switchTo().frame(driver.findElement(By.cssSelector("frame[name='classFrame']")));
driver.findElement(By.xpath("//ul[#title='Navigation']//a[.='" + linkName + "']")).click();
driver.switchTo().defaultContent();
}
Then you can call it like, clickNavbar("Deprecated");

Is there a way to click plain text in Codeception acceptance tests without using XPath?

Using Codeception acceptance test (with WebDriver), I would like to know if there is a way to click an element that contains a specific text, without that element being a link or a button. I know it can be done using XPath, but I'm looking for a more readable solution that uses CSS-selectors for example.
Without specific examples, probably the best you could do is to look for a group of elements using a CSS selector then loop through that collection looking for contained text. Here's a contrived example where I'm looking for a TD that contains the text "Click here".
List<WebElement> cells = driver.findElements(By.cssSelector("td.someclass"));
for (WebElement cell : cells)
{
if (cell.getText().contains("Click here"))
{
cell.click();
break; // found it, don't need to keep looping
}
}
If you want your search to look for the text, then XPath is your only option.

How to identify individual controls within a search result using Selenium Webdriver?

I have a list of search results in the following link and would like to know on how can I identify the individual controls using dynamic xpath
http://www.bigbasket.com/cl/fruits-vegetables/?nc=nb
I'm able to get the list of product names displayed using the below line
List<WebElement> productResults = browser.findElements(By.xpath("//*[contains(#id,'product')]/div[2]/span[2]/a"));
I'm able to print the product names displayed in Page 1 using the below code, but however the list size is not matching with the list of results displayed in Page 1 so which I see blank lines in between when printing
System.out.println(productResults.size());
for(int i=0;i<productResults.size();i++){
System.out.println(productResults.get(i).getText());
}
Also I tried to locate the individual controls such as Qty text box, Add button in a similar like how I located the product names but the list count is not matching so which I cannot specify the quantity, add the required product to the cart.
Could you please help me with this one?
The first step is get only the visible itens (that is displayed), sou you can use this xpath:
"//*[contains(#id,'product')][not(contains(#style,'display:none'))]/div[2]/span[2]/a"
Now, you need to return the main iten div, that allows you to acess other functions. You can get the tag parents in this way:
"//*[contains(#id,'product')][not(contains(#style,'display:none'))]/div[2]/span[2]/a/../../.."
The elements that you recieve in this last XPath have all html itens that you want, as set quantity, select the dropdown etc. You can acess each using a findElement() in each IWebElement of the list. Example:
List<WebElement> productResults = browser.findElements(By.xpath("//*[contains(#id,'product')][not(contains(#style,'display:none'))]/div[2]/span[2]/a/../../.."));
for(WebElement element : productResults ){
IWebElement quantityInput = element.findElement(By.XPath("//input[contains(#id, '_qty')]"));
string quantityValue = quantityInput.getAttribute("value"); // if you want to know the current value. YOu can also parse it in an int
IWebElement addButton = element.findElement(By.XPath("//a[contains(#class, 'add-button')]"));
// etc to all elements inside element.
// Remember: Element is yout complete card of the item, that contains Value, name, image, buttons and all it.
}
Sorry for some Java syntax error. I am not a Java developer / tester. My piece of cake is C#.

How to Detect Dynamic frame in selenium webdriver?

I am trying to detect frame with dynamic target in selenium webdriver.
In selenium IDE i am getting details of frame like command "selectframe", Target="CitrixMainFrameWI_hghjghjhj355", In the target dropdown no other value is present ,
I tried to detect this frame using command
driver.switchto().frame("CitrixMainFrameWI_hghjghjhj355"). But this target value is dynamically generated so i got error .
Can you please suggest me any solution for this
Try it with frame(index) or by naming the frames and only searching for CitrixMainFrame
(contains("CitrixMainFrame").
More Details in this link:
http://joychester.blogspot.com/2010/09/switch-frame-and-windows-sample-code.html
Use this :
// First find the frame.
WebElement element = driver.findElement(By.cssSelector("div[id^= 'CitrixMainFrameWI_']"));
// id^ means the id starts with the given value.
// You din't specify the tags, so i assumed as div tag. change it according to your code.
driver.switchto().frame(element);
String expectedFrameID="abc";
List<WebElement> lst=d.findElements(By.tagName("iframe"));
System.out.println(lst.size());
int flag=0;
for(int i=0;i<lst.size();i++){
String actualFrameID=lst.get(i).getAttribute("id");
System.out.println(lst.get(i).getAttribute("id"));
if(expectedFrameID.equals(actualFrameID)){
flag=1;
break;
}
}
if(flag==1){//perform operation on frame}

Selenium Api, Selenium locators and regex

In my page I have some ids like "foo:searchResults12345" that are composed of a static part ("foo:searchResults" and a dynamic rendered one - one or more numbers.
I want to retrieve every id that contains the static part "foo:searchResults", ignoring the dynamic one.
I am thinking at using regex for the pattern text, but the problem is that I cannot find any method in Selenium Api to help me with this, such as selenium.getAllIdsInPage().
Anybody had the same problem and figured out a solution?
I would recommend combining getXpathCount with a loop to iterate through each match. For example the following xpath should return the total number of matching nodes when used with getXpathCount
//*[starts-with(#id, 'foo:searchResults')]
You should then be able to loop through each match using the following example XPath:
/descendant::*[starts-with(#id, 'foo:searchResults')][1]
Where 1 would be replaced by the current count in the loop.
Below is an example in Java that should print the matching IDs to system.out:
#Test
public void outputDynamicIds() {
selenium.open("http://www.example.com/");
int elementCount = selenium.getXpathCount("//*[starts-with(#id, 'foo:searchResults')]").intValue();
for (int i = 1; i < elementCount+1; i++) {
System.out.println(selenium.getAttribute("/descendant::*[starts-with(#id, 'foo:searchResults')][" + i + "]#id"));
}
}
Another way to go is to use a custom Location Strategy. I am doing this in my current project and it works fine.
In your particular case you can write a custom strategy that checks if the id attribute of each element starts with the locator string (the static part in your example) and return this element if that is so.
I think this approach is much cleaner.