Find if next sibling exist - selenium

I try to write some robot test cases and I got stuck. I need to extract some links from a page. The links are stored inside an <li> tag which is also inside an <ul>. The problem is that after I extract the first link I need to go to the next <li> (the first <li> next sibling) but how can I find if there is a next sibling without getting an error or the application stop. I know, I can use Element Should Contain keyword but in case if it not contain I get an error.
I want to do something like this:
if next sibling exist do something
else go do something else
The html should look like this.
<ul id="top_menu" role="menubar" class="">
<li class="menu_accordion_section" id="cat_1">
</li>
<li class="menu_accordion_section" id="cat_2">
</li>
</ul>

There is direct message, you can use execute script to create a varaible.
so i created a keyword
Validate child element exists
it takes two argument parent(Webelement) and child(Xpath) to search for child inside the parent.
Validate child element exists ${parent} ./h3
Assuming parent xpath to as : //ul[#id="top_menu"] , the above code will search for equalent xpath //ul[#id="top_menu"]/./h3 note taht context is parent and not root
so this will be equalent to xpath
*** Test Cases ***
Google Search
[Tags] you probably do not have this many tags in real life
Wait Until Element Is Visible CSS=[id="introduction-container"]
${parent}= Get webelement xpath=//*[#id="introduction-container"]
${result}= Validate child element exists ${parent} ./h3
Should Be True ${result}
*** Keywords ***
Validate child element exists
[Arguments] ${parent} ${child}
${child}= Execute Javascript return window.document.evaluate( arguments[1], arguments[0], null, XPathResult.FIRST_ORDERED_NODE_TYPE, null ).singleNodeValue; ARGUMENTS ${parent} ${child}
[Return] ${child}

one solution is iterate over the menu, follow a code example(this code has not been tested, may need some adjustments):
List<WebElement> menuItens = getDriver().findElement(By.id("top_menu")).findElements(By.tagName("li"));
List<String> links = new ArrayList<>();
menuItens.forEach(item -> {
links.add(item.findElement(By.tagName("a")).getAttribute("href"));
});
return links;

Related

Selenium finding elements returns incorrect elements

I'm using Selenium to try and get some elements on a web page but I'm having trouble getting the ones I want. I'm getting some, but they're not the ones I want.
So what I have on my page are five divs that look like this:
<div class="membershipDetails">
Inside each one is something like this:
<div class="membershipDetail">
<h3>
VIP Membership
</h3>
</div>
They DO all have this same link, but they don't have the same text ('VIP Membership' would be replaced by something else)
So the first thing was to get all the divs above in a list. This is the line I use:
listElementsMembership = driver.find_elements_by_css_selector(div[class^='membershipDetail'])
This gives me five elements, just as I would expect. I checked the 'class' attribute name and they are what I would expect. At this point I should say that they aren't all EXACTLY the same name 'membershipDetail'. Some have variations. But I can see that I have all five.
The next thing is to go through these elements and try and get that element which contains the href ('VIP Membership').
So I did that like this:
for elem in listElementsMembership:
elemDetailsLink = elem.find_element_by_xpath('//a[contains(#href,"EditMembership")]')
Now this does return something, but it always got me the element from the FIRST of the five elements. It's as if the 'elem.find_element_by_xpath' line is going up a level first before finding these hrefs. I kind of confirmed this by switching this to a 'find_elements_by_xpath' (plural) and getting, you guessed it, five elements.
So is this line:
elemDetailsLink = elem.find_element_by_xpath('//a[contains(#href,"EditMembership")]')
going up a level before getting its results? If it is, now can I make it not do that and just restrict itself to the children?
If you are trying to find element with in an element use a . in the xpath like below:
listElementsMembership = driver.find_elements_by_css_selector(div[class^='membershipDetail'])
for elem in listElementsMembership:
elemDetailsLink = elem.find_element_by_xpath('.//a') # Finds the "a" tag with respect to "elem"
Suppose if you are looking for VIP Membership:
listElementsMembership = driver.find_elements_by_css_selector(div[class^='membershipDetail'])
for elem in listElementsMembership:
value = elem.find_element_by_xpath('.//a').get_attribute("innerText")
if "VIP Membership" in value:
print(elem.find_element_by_xpath('.//a').get_attribute("innerText"))
And if you dont want iterate over all the five elements try to use xpath like below: (As per the HTML you have shared)
//div[#class='membershipDetail']//a[text()='VIP Membership']
Or
//div[#class='membershipDetail']//a[contains(text(),'VIP Membership')]
You've few mistake in that css selector.
Quotes are missing.
^ is for starts-with, not sure if you really need that. In case it's partial matching please use * instead of ^
Also, I do not see any logic for the below statement in your code attempt.
The next thing is to go through these elements and try and get that
element which contains the href ('VIP Membership').
Code :
listElementsMembership = driver.find_elements_by_css_selector("div[class*='membershipDetail']")
for ele in listElementsMembership:
e = ele.find_element(By.XPATH, ".//descendant::a")
if "VIP Membership" in e.get_attribute('href'):
print(e.text, e.get_attribute('href'))
You can give an index using a square bracket like this.
elemDetailsLink = elem.find_element_by_xpath('(//a[contains(#href,"EditMembership")])[1]')
If you are trying to get an element using XPath, the index should start with 1, not 0.

How to locate an element using selenium webdriver which doesn't have unique identifier like Name, Id, title etc?

I am new to Selenium. Not sure how to handle this scenario. I am working on a website which has several buttons with following code,
<a class="Some big class name" datacommunication="SelectItem" token="some token number" model-id="Id1" element="button">
<i class="classname">Book Ticket</i>
</a>
<a class="Some big class name" datacommunication="SelectItem" token="some token number" model-id="Id2" element="button">
<i class="classname">Book Ticket</i>
</a>
I tried to click on it using following commands,
ele = driver.FindElement(By.ClassName("Some big class name")); but it fails with following message, Compound class names are not supported. Consider searching for one class name and filtering the results.
ele = driver.FindElement(By.CssSelector("a[model-id='Id1']")); fails with 'Test method TestBot.HomeTest.bookTicket threw exception:
OpenQA.Selenium.WebDriverTimeoutException: Timed out after 10 seconds'
Tried using XPATH,
ele = driver.FindElement(By.XPath("\\\a[#model-id='Id1']")); doesn't work either.
I have no control on html. Can't change it.
Please let me know how to identify elements in such scenarios.
You can't have spaces in class names. Those are actually multiple classes separated by a space. You can find the above elements using a css selector
var ele = driver.FindElements(By.CssSelector(".Some.big.class.name"))
Of course, this will find both elements. To find just the first, you could use
var ele = driver.FindElement(By.CssSelector("a[model-id='Id1']"))
You can find help on css selectors here: http://www.w3schools.com/cssref/css_selectors.asp
Update:
I just noticed your XPath appears to have the slashes the wrong way around. If you wish to use XPath, try
//a[#model-id='Id1']
Note, however, that css selectors perform better than XPath.
There are multiple number of ways to locate your WebElement in Selenium WebDriver.
But always remember all are based on you attribute or combination of HTML tags so case could be any of them
1- First way is using id
2- 2nd one is Name
3- Class Name
4- Some time you can used Tagname
5- Some time linkText
6- Some time partial link text
7- Using xpath
8- Using css selector
So in you case we need to take help of Xpath and Css Selector
So xpath of you elements
Syntax : //[#attribute ='value of selected tag']
Example
id1: //a[#model-id='Id1']
id2: //a[#model-id='Id2']
For both element following are the css Selector
Syntax [attribute ='value']
id1:
a[model-id='Id1']
id2:
a[model-id='Id2']
http://www.slideshare.net/weekendtesting/css-selector-29312437
http://www.slideshare.net/weekendtesting/locators-in-selenium-bnt-09
Thanks a lot for help. I have used following code to overcome above mentioned issue,
WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(10));
wait.Until(ExpectedConditions.ElementIsVisible(By.CssSelector("a[data-model-id='c5']"))).Click();
With above code, I am able to click on the button.
Thanks again for your help and knowledge sharing.
Amit
You can locate by using xpath.
WebElement ele = driver.findElement(By.xpath("//*[#class='Some big class name']"));
there is difference between findElements and findElement.
FindElement: findElement returns a single element.
FindElements : returns a list of same element.As in this example there are multiple classes with same class name , so use driver.findElements method .
driver.findElements will return a list of all elements with that class name .
Now , you have list of all elements but you want only one of the element.
So iterate over list to get a single element out of a list.
List<WebElement> elementList= driver.FindElement(By.ClassName("Some.big.class.name"));
Iterator itr = elementList.iterator();
while(itr.hasNext())
{
WebElement element = itr.next();
if(element.getAttribute("model-id").equals("Id1")){
element.click();
break;
}//if block ends here
}//while loop ends here
You can also use XPATH , if nothing works
To identify the elements in selenium there are multiple ways.
To see the details please refer BY Class.
Try to find the way which can identify the element uniquely. Start with id if available and if nothing works go for XPATH. XPATH is slower than id and CSS selector.

webdriver (c#) - Assert text from a specific UL tagname

I'm trying to assert that specific UL tags within my webpage contain certain text. I'm not sure how to get for example, the third UL tag within a class and then assert it's text. Here is an example of the HTML code from my webpage:
<td class="def"><ul>
<li>1 -- test 1</li>
<li>2 -- test 2</li>
<li>3 -- test 3</li>
<li>4 -- test four</li>
</ul></td>
I'm taking a different (and likely naive) approach to get this working but it's not really ideal for me - and I'm only doing it this way because I'm not sure how to get it working the way I would like. I'm asserting that the length of text from the outer html of the "def" class contains a specific count:
(from memory)
String Def = utility.driver.findelement(By.ClassName("def").GetAttribute.("outerHTML");
int Length = Def.Length;
Assert.IsTrue(Length.equals("36"));
You can either directly find that element using an XPath selector that matches on text (example selector below, not tested):
//td[contains(#class, 'def')]//ul/li[text()='4 -- test four')]
(find me a td that contains a class of def, get the ul underneath it, and get the li underneath that which also has a text of 4 -- test four)
You can also harness the power of LINQ in C# to do some leg work for you. The thing you are missing is finding child elements of a given element. This is done by chaining .FindElement commands:
Driver.FindElement(By.ClassName("def")).FindElement(By.TagName("ul"));
The above would get the first ul element that is a direct child of the td element in your example. WebDriver will be able to figure out the child & parent relationships for you.
So to put this into your situation a bit better, here's how you would do it.
Driver.FindElement(By.ClassName("def"));
Get the table we want.
Driver.FindElement(By.ClassName("def")).FindElement(By.TagName("ul"));
Get the ul we want that's a direct child of that table.
Driver.FindElement(By.ClassName("def")).FindElement(By.TagName("ul")).FindElements(By.TagName("li"));
Get the li elements of that ul. Note the FindElements (extra s!)
Driver.FindElement(By.ClassName("def")).FindElement(By.TagName("ul")).FindElements(By.TagName("li")).First(i => i.Text.Equals("4 -- test four"));
Since it's an IList that returns, LINQ can then take it and return the first one that has a .Text property equal to what you need.

How to get All Text in robot framework ?

Consider the following source code,
<div id="groupContainer" class="XXXXXX">
<ul id="GroupContactListWrapper" class="list-wrapper">
<li class="contactNameItemContainer">
<div class="contactNameItem">
<span class="name">Name1</span>
</div>
</li>
<li class="contactNameItemContainer">
<div class="contactNameItem">
<span class="name">Name2</span>
</div>
</li>
</ul>
</div>
How do i retreive the two names (Name1,Name2) in a list variable ?
I tried the following xpath for a "Get Text" keyword, but only returns the first one.
//div[#id='groupContainer']//li[#class='contactNameItemContainer']//span
Please suggest
You could iterate over the elements as below:
${xpath}= Set Variable //div[#id='groupContainer']//li[#class='contactNameItemContainer']//span
${count}= Get Matching Xpath Count ${xpath}
${names}= Create List
:FOR ${i} IN RANGE 1 ${count} + 1
\ ${name}= Get Text xpath=(${xpath})[${i}]
\ Append To List ${names} ${name}
This works but is rather slow when there are many matches.
You could extend Selenium2Library and write your own keyword for this purpose. Save the following as Selenium2LibraryExt.py
from Selenium2Library import Selenium2Library
class Selenium2LibraryExt(Selenium2Library):
def get_all_texts(self, locator):
"""Returns the text value of elements identified by `locator`.
See `introduction` for details about locating elements.
"""
return self._get_all_texts(locator)
def _get_all_texts(self, locator):
elements = self._element_find(locator, False, True)
texts = []
for element in elements:
if element is not None:
texts.append(element.text)
return texts if texts else None
Then you can use your new Get All Texts keyword in your tests like this:
*** Settings ***
library Selenium2LibraryExt
*** Test Cases ***
Get All Texts Test
Open Browser http://www.example.com chrome
#{texts} Get All Texts css=.name
Log Many ${texts}
Though the top-rated answer is fully working - and the most xpath-ish :), let me add an option I don't see proposed yet - using the Get Webelements keyword. E.g.:
#{locators}= Get Webelements xpath=//div[#id='groupContainer']//li[#class='contactNameItemContainer']//span
${result}= Create List
:FOR ${locator} in #{locators}
\ ${name}= Get Text ${locator}
\ Append To List ${result} ${name}
It'll generate and return a list of all matching elements, on which you just iterate on. It might be a tad faster than xpath's [index] reference because the dom is evaluated once - but don't hold me accountable if that's not fully true :)
Get Text will return content of the first element that matches the locator. When using XPATH you can specify the index of the element you want to get, like this:
${name1} Get Text xpath=//div[#id='groupContainer']//li[#class='contactNameItemContainer'][0]//span
${name2} Get Text xpath=//div[#id='groupContainer']//li[#class='contactNameItemContainer'][1]//span
#{names} Create List ${name1} ${name2}
#Velapanur
I had another similar requirement where in i have to enter the texts into textareas in a page.
The below i wrote with the idea in reference to what Todor had suggested, and it worked. Many thanks to Todor, Velapanur
#{Texts}= Get WebElements ${AllTextboxes}
:FOR ${EachTextarea} in #{Texts}
\ Input Text ${EachTextarea} ${RandomTextdata}
Here is the logic to get all elements in Java. You can adopt it to your need.
You have to use findElements() and not findElement() to get all elements.
List<WebElement> items = driver.findElements(By.cssSelector("ul#GroupContactListWrapper div.contactNameItem"));
foreach(item in items){
System.out.println(item.getText();
}
If you want a particular element from the list you can use items.get(1)

selenium webdriver Findelement confused

I have looked around forum and haven't find right answer for my misunderstanding.
Let's say we have html code:
<h3 id="param_search_0" class="mygray open" data-searchparam="any42" data-id="0">Text here</h3>
So if I get element by tag "h3" like this:
IWebElement get_element = Element_from_above.FindElement(By.Tag("h3"));
What element will I get (select for later use), just text? If yes so how can I select full line element, or seperate, text and inside parameters, from <h3></h3> loop.
Thank you and sorry for this maybe stupid question.
Giving you an answer in Java, as you have not specified the language that you're using:
Suppose that you've created a web-driver instance:
WebDriver driver = new FirefoxDriver();
First, you need to access the web-page in which you are interested, using the URL of that web-page:
driver.get(url);
Then, you can either get the first <h3> element in the web-page:
WebElement element = driver.findElement(By.tagName("h3"));
Or you can get a list of all the <h3> elements in the web-page:
List<WebElement> elements = driver.findElements(By.tagName("h3"));
Then, you can get various attributes of that element.
For example, in order to get "the entire line" as you call it:
String line = element.getAttribute("outerHTML");
For example, in order to get "just the text" as you call it:
String line = element.getAttribute("innerHTML");
And in a similar manner, you can get the value of each and every attribute within the element...