I need to find an element in Selenium by CSS - selenium

I want to find the element of this link "us states" in <h5>. I am trying this on Craigslist. How can I do it?
Here is the URL: http://auburn.craigslist.org/
<html class="">
<head>
<body class="homepage w1024 list">
<script type="text/javascript">
<article id="pagecontainer">
<section class="body">
<table id="container" cellspacing="0" cellpadding="0"
<tbody>
<tr>
<td id="leftbar">
<td id="center">
<td id="rightbar">
<ul class="menu collapsible">
<li class="expand s">
<li class="s">
<li class="s">
<h5 class="ban hot">us states</h5>
<ul class="acitem" style="display: none;">
</li>
<li class="s">
<li class="s">

Only using class names is not sufficient in your case.
By.cssSelector(".ban") has 15 matching nodes
By.cssSelector(".hot") has 11 matching nodes
By.cssSelector(".ban.hot") has 5 matching nodes
Therefore you need more restrictions to narrow it down. Option 1 and 2 below are available for CSS selector, 1 might be the one that suits your needs best.
Option 1: Using list items' index (CssSelector or XPath)
Limitations
Not stable enough if the site's structure changes
Example:
driver.FindElement(By.CssSelector("#rightbar > .menu > li:nth-of-type(3) > h5"));
driver.FindElement(By.XPath("//*[#id='rightbar']/ul/li[3]/h5"));
Option 2: Using Selenium's FindElements, then index them. (CssSelector or XPath)
Limitations
Not stable enough if a site's structure changes
Not the native selector's way
Example:
// Note that By.CssSelector(".ban.hot") and //*[contains(#class, 'ban hot')] are different, but doesn't matter in your case
IList<IWebElement> hotBanners = driver.FindElements(By.CssSelector(".ban.hot"));
IWebElement banUsStates = hotBanners[3];
Option 3: Using text (XPath only)
Limitations
Not for multilanguage sites
Only for XPath, not for Selenium's CssSelector
Example:
driver.FindElement(By.XPath("//h5[contains(#class, 'ban hot') and text() = 'us states']"));
Option 4: Index the grouped selector (XPath only)
Limitations
Not stable enough if the site's structure changes
Only for XPath, not CssSelector
Example:
driver.FindElement(By.XPath("(//h5[contains(#class, 'ban hot')])[3]"));
Option 5: Find the hidden list items link by href, then traverse back to h5 (XPath only)
Limitations
Only for XPath, not CssSelector
Low performance
Tricky XPath
Example:
driver.FindElement(By.XPath(".//li[.//ul/li/a[contains(#href, 'geo.craigslist.org/iso/us/al')]]/h5"));

By.cssSelector(".ban") or By.cssSelector(".hot") or By.cssSelector(".ban.hot") should all select it unless there is another element that has those classes.
In CSS, .name means find an element that has a class with name. .foo.bar.baz means to find an element that has all of those classes (in the same element).
However, each of those selectors will select only the first element that matches it on the page. If you need something more specific, please post the HTML of the other elements that have those classes.

You can describe your CSS selection like cascading style sheet rows:
protected override void When()
{
SUT.Browser.FindElements(By.CssSelector("#carousel > a.tiny.button"))
}

Related

How to create an Xpath in a tricky section of document (for me) for the purpose of using with Selenium Basic in VBA

OK, so I mentioned Selenium Basic as that is the use of the XPath and I believe Selenium Basic uses Selenium version 2 so maybe it won't be able to understand some/all answers that might require the latest Selenium. But someone might take that into account if necessary.
There are dynamic classes at play here.
Criteria for selection.
1. Class starting with 'NextToJump__eventWrapper' (the outer one) must be used.
2. Class starting with 'NextToJump__venue' must contain text = 'Ballarat'
3. Class starting with 'NextToJump__race' (and/or span) must contain text = 'Race 2'
I need to be able to click on the <a> tag that contains Points 2 and 3.
The best that I've been able to do (and checked) using ChroPath in Chrome Devtools is...
//div[starts-with(#class,'NextToJump__eventWrapper')]//descendant::*[contains(text(),'Ballarat')]
But note that there are 2 cases of Point 2 in the HTML but only 1 case that satisfies Points 2 and 3.
Thanks
<div class="NextToJump__eventWrapper--13zZJ">
<div>
<div class="NextToJump__raceEvent--bfMON" data-testid="next-to-jump-item">
<a class="Link__link--9x4YY" href="/racing-betting/greyhound-racing/crayford-am/20200708/race-1-1801951-58544404">
<div class="NextToJump__iconWrapper--1yG60"></div>
<div class="NextToJump__eventDetail--CUzdX">
<div class="NextToJump__venue--1jwWA">Ballarat</div>
<div class="NextToJump__race--3JydR"><span>Race 1</span></div>
</div>
<div class="NextToJump__countdown--EG8mR"><span class="Countdown__countdown--4vRpD Countdown__imminent--2yc2K">52s</span></div>
</a>
</div>
<div class="NextToJump__raceEvent--bfMON" data-testid="next-to-jump-item">
<a class="Link__link--9x4YY active" href="/racing-betting/greyhound-racing/rockhampton/20200708/race-4-1799474-58466521" aria-current="page">
<div class="NextToJump__iconWrapper--1yG60"></div>
<div class="NextToJump__eventDetail--CUzdX">
<div class="NextToJump__venue--1jwWA">Rockhampton</div>
<div class="NextToJump__race--3JydR"><span>Race 4</span></div>
</div>
<div class="NextToJump__countdown--EG8mR"><span class="Countdown__countdown--4vRpD Countdown__imminent--2yc2K">2m 52s</span></div>
</a>
</div>
<div class="NextToJump__raceEvent--bfMON" data-testid="next-to-jump-item">
<a class="Link__link--9x4YY" href="/racing-betting/greyhound-racing/ballarat/20200708/race-4-1799454-58465201">
<div class="NextToJump__iconWrapper--1yG60"></div>
<div class="NextToJump__eventDetail--CUzdX">
<div class="NextToJump__venue--1jwWA">Ballarat</div>
<div class="NextToJump__race--3JydR"><span>Race 2</span></div>
</div>
<div class="NextToJump__countdown--EG8mR"><span class="Countdown__countdown--4vRpD Countdown__imminent--2yc2K">5m 52s</span></div>
</a>
</div>
</div>
</div>
The xpath expression you need to use to select your target <a> tag is long and convoluted, but that's life....
[formatted for ease of reading, but you can use that in one line]
//a
[ancestor::div[starts-with(#class,'NextToJump__eventWrapper')]]
[.//div[.="Ballarat"]
[starts-with(#class,'NextToJump__venue-')]
[./following-sibling::div[.="Race 2"]
[starts-with(#class,'NextToJump__race-')]
]
]
Edit:
In "plain English":
Find an <a> node which meets ALL these conditions (i) has an ancestor (not a parent) node which is a <div>, which <div> has a class attribute with an attribute name which starts with NextToJump__eventWrapper; and (ii) it has <div>descendant (not just a child) node, which has Ballarat as a text node AND which has a class attribute with an attribute name which starts with NextToJump__venue-, where that <div>descendant itself has a following sibling which is a <div> which itself has a Race 2 text node AND which has a class attribute with an attribute name which starts with NextToJump__race-...
Yes, the word "plain" doesn't really fit here, but that's the closest I could get. I like xpath, and it's very powerful, but sometimes it's very hard to follow... As an aside, it would have been somewhat less cryptic if xquery was used instead of straight xpath.

How to find Label of a input field

Looking for a generic way to find text before an input field to know what to fill in the field. Using xpath, css selector or any other way possible.
<div>
<span>Full Name</span>
<input name="xddadN">
</div>
<div>
<span>Email</span>
<input name="xedadN">
</div>
Or
<div>
<div><label>Full Name</label></div>
<div><input name="xddadN"></div>
<div><label>Email</label></div>
<div><input name="xedadN"></
</div>
Or
<div>
<label>Full Name<br>
<span><input name="xddadN"></span>
</label>
</div>
<div>
<label>Full Name<br>
<span><input name="xddadN"></span>
</label>
</div>
You can try below XPath expression to get preceding text node:
//input/preceding::*[1]
or more specific for Full Name
//input[#name="xddadN"]/preceding::*[1]
and Email:
//input[#name="xedadN"]/preceding::*[1]
For full name use this Xpath : //input[#name='xddadN']/preceding-sibling::span
code :
String fullName = driver.findElement(By.Xpath(//input[#name='xddadN']/preceding-sibling::span)).getText();
String Email = driver.findElement(By.Xpath(//input[#name='xedadN']/preceding-sibling::span)).getText();
You haven't mentioned any Selenium Language Binding Art so I will be using Java for the example.
First the Answer
Yes, you can use a generic way to find text before an input field as follows :
As per the HTML :
<div>
<span>Full Name</span>
<input name="xddadN">
</div>
<div>
<span>Email</span>
<input name="xedadN">
</div>
To retrieve the text Full Name from the <span> tag with respect to the <input> tag you can use :
String myText = driver.findElement(By.xpath("//input[#name='xddadN']//preceding::*[1]")).getAttribute("innerHTML");
Now the Pitfall
Without any visibility to your usecase in my opinion the generic way would be a pitfall which will induce much chaos and uncertanity for the following reasons :
As per the xpath we are straightway jumping into the previous element, a small change in the HTML DOM (e.g. inclusion of a <span> tag) will make your Testcases to Fail.
In general, while constructing a Locator Strategy through css-selectors or xpath it will be benificial to include the <tagName> to optimize the element search process. If <tagName> are not included your Tests will require more time to locate the elements and perform action on them. In this process you are compromising some of the advantages of Test Automation.
Conclusion
Hence as a conclusion as per the Best Practices always include the <tagName> while constructing a Locator Strategy through css-selectors or xpath.

Any suggesstions to locate the below elements in Selenium

Example HTML below. I want to locate the elements which contains the text Person Attributes and Age.
<div id="ext-gen210" class="x-tool x-tool-toggle"></div>
<span id="ext-gen214" class="x-panel-header-text">Person Attributes</span>
</div>
<div id="ext-comp-1071" class=" DDView ListView" style="height: auto;">
<p class="dragItem "><i class="icon-Gen"></i>Age</p>
</div>
Note: I am looking for a solution without using xpath or id or className as they might change with every new release of my website.
I tried to locate them using
'name' --> By.name("Person Attributes") and By.name("Age") but both failed.
By.name would check for the name attribute. What you need is to check the text of an element using By.xpath:
By.xpath('//div[span/text() = "Person Attributes"]')
Or, you can also check that an id element starts with ext-gen:
By.xpath('//div[starts-with(#id, "ext-gen")]')

Selenium webdriver-java: how to get javascript tooltip

I want to get tooltip information enabling on JavaScript. I have tried with below code but its shown null every time.
Java Code:
Actions action = new Actions(driver);
WebElement element = driver.findElement(By.xpath("//*[#id='content']/div/div/div[1]/div/div[2]/div[2]/div/div[3]/div[1]/table/tbody/tr[1]/td[2]/span/div/a"));
action.moveToElement(element).build().perform();
System.out.println(driver.findElement(By.xpath("//*[#id='content']/div/div/div[1]/div/div[2]/div[2]/div/div[3]/div[1]/table/tbody/tr[1]/td[2]/span/div/a")).getAttribute("data-original-title"));
HTML Code is:
<tr class="single" name="Tasks_59c777d9-8d16-694a-7307-52caad36d751">
<td>
</td>
<td data-type="custom_task_name">
<span class="list" sfuuid="3840" data-original-title="">
<div class="ellipsis_inline" data-original-title="">
Task Testing
<br/>
Toney Harber
</div>
</span>
</td>
I have tried with span tag but this also shows null.
I am providing you 2 options try both.
Option 1
Please try below. It will return you a list. Loop through it. If there is only one span then you can get the element and then get the attribute value.
driver.findElements(By.xpath("//span[#data-original-title]"))
Option 2
Instead of
driver.findElement(By.xpath("//*[#id='content']/div/div/div[1]/div/div[2]/div[2]/div/div[3]/div[1]/table/tbody/tr[1]/td[2]/span/div/a"))
use findElements
driver.findElements(By.xpath("//*[#id='content']/div/div/div[1]/div/div[2]/div[2]/div/div[3]/div[1]/table/tbody/tr[1]/td[2]/span/div/a"))
It will return a list of web elements. Loop thorough it you might get more than one element.

Hot to find in dojo element when I know id of parent and I know type and style class of element which I looking fo

Hot to find in dojo element when I know id of parent and I know type and style class of element which I looking for ?
For example, I want find and change (span style=tabLabel) ALARMS into Mga alarma
<div dojoattachpoint="focusNode" role="tab" style="-moz-user-select: none;" id="tab_div_tablist_dijit_layout_ContentPane_1" tabindex="-1" title="" aria-selected="false">
<img dojoattachpoint="iconNode" class="dijitIcon dijitTabButtonIcon dijitNoIcon" alt="" src="dojoroot/dojo/resources/blank.gif">
<span class="tabLabel" dojoattachpoint="containerNode" style="-moz-user-select: none;">Alarms</span>
<span role="presentation" dojoattachevent="onclick: onClickCloseButton" dojoattachpoint="closeNode" class="dijitInline dijitTabCloseButton dijitTabCloseIcon" style="display: none;">
<span class="dijitTabCloseText" dojoattachpoint="closeText">[x]</span></span>
</div>
In this case it is pretty easy. If you look at the span element you refer to it has a dojoattachpoint attribute specified. That means that the node can be accessed from the widget directly with that name.
Now I assume that the widget is called "tab_div_tablist_dijit_layout_ContentPane_1" from the id in your code so to get the widget:
var widget = dijit.byId("tab_div_tablist_dijit_layout_ContentPane_1");
And the dojoattachpoint on the span has the value containerNode so:
widget.containerNode.innerHTML = "Mga alarma";
I think that should work.
If you're creating a custom widget template and wish to localize a string, there is a mechanism to do this. Simply use a substitution pattern like ${alarm} and define a javascript property on your widget with that name. That property can then be populated with a localization bundle using dojo.i18n. You can look at some of the dijits like dijit.Dialog.postMixInProperties to see how this is done.