How to select element in span dropdown menu cssSelector webdriver Java - java

I am trying to select these elements in webdriver, after I click a button which opens a drop down menu. I can click the button fine and it drops down with.
WebElement providers = driver.findElement(By.id("providers"));
providers.click();
HTML
<input id="providers" class="providersOff" type="button">
<div id="providers-list" class="">
<ul>
<li ng-click="searchProvider(0)">
<div class="imageContainer">
<span>Google</span> <--TRYING TO SELECT THIS
I'm trying to select the Google element.
I have tried both of these and it doesn't work:
driver.findElement(By.cssSelector(".imageContainer[Google]"));
driver.findElement(By.cssSelector(".providers-list > li[ng-click*= searchProvider(0)]"));
It runs these perfectly fine:
// Assign search-bar & send keys
WebElement searchbar = driver.findElement(By.id("txtSearch"));
searchbar.sendKeys("Pizza");
// Assign provider drop-down & click
WebElement providers = driver.findElement(By.id("providers"));
providers.click();

It should be:
div.imageContainer > span
which basically means:
give me the span element which is a direct child of a div element with class="imageContainer".
To get the actual text, use .text:
WebElement span = driver.findElement(By.cssSelector("div.imageContainer > span"));
System.out.println(span.text);
If you want to match the span by text, you can approach it with xpath:
WebElement google = driver.findElement(By.xpath("//div[#class='imageContainer']/span[. = 'Google']"));
google.click();
Or, you can also rely on ng-click attribute of the li element:
WebElement span = driver.findElement(By.cssSelector("li[ng-click$='(0)'] > div.imageContainer > span"));
where $= is an ends-with selector.

It may be good for you to familiarize yourself with basic CSS selectors. Try playing this game, I've learned a lot from it: https://flukeout.github.io/
Now back to your original question. Based on the part of the code you provided the shortest possible selector is simply span
driver.findElement(By.cssSelector("span")); - it says give me a html tag
But I assume you have a lot of spans on your page so this selector may not be unique.
driver.findElement(By.cssSelector("#providers-list span")); - search for an element with id=providers-list and within this element search for a tag span. This probably will be enough, but in case you have many spans within this particular div you may do:
driver.findElement(By.cssSelector("#providers-list .imageContainer span")); - search for element with id=providers-list, within this element search for a descendant with class attribute containing imageContainer and then for a tag span.
You may also provide the full path to the element:
driver.findElement(By.cssSelector("#providers-list > ul > li > .imageContainer > span")); - '>' says go to direct child while space means go to a descendant (no matter how deep).
EDIT
If ng-click is the only unique attribute then the code will look like this
driver.findElement(By.cssSelector("#providers-list li[ng-click='searchProvider(0)'] > .imageProvider > span"));
EDIT2
You probably need to wait for element to become visible after you expand dropdown (after you do providers.click())
WebDriverWait wait = new WebDriverWait(driver, 10);
String selector = "#providers-list li[ng-click='searchProvider(0)'] > .imageProvider > span";
WebElement element = wait.until(ExpectedConditions.visibilityOfElementLocated(By.cssSelector(selector)));

Related

Selenium | Unable to locate input element

No idea how to address this input text field element with selenium / Java (openjdk 11 2018-09-25).
I tried xpath, cssSelector etc. it never works. Its always "Unable to locate element".
<slot name="input">
<input part="value" tabindex="0" aria-labelledby="vaadin-text-field-input-3">
</slot>
This did NOT work:
driver.findElement(By.xpath("//input[#aria-labelledby='vaadin-text-field-input-3']")).sendKeys("test");
Is there a solution for this?
UPDATE:
This thread solved partly my problem. The solution that worked for me can be found here link.
To send a character sequence to the element you need to induce WebDriverWait for the elementToBeClickable() and you can use either of the following locator strategies:
css_selector:
new WebDriverWait(driver, 20)
.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("div.vaadin-text-field-container div[part=input-field][id^='vaadin-text-field-input'] slot[name='input'] > input[part='value'][aria-labelledby^='vaadin-text-field-input']")
)).sendKeys("pixelhead");
xpath:
new WebDriverWait(driver, 20)
.until(ExpectedConditions.elementToBeClickable(
By.xpath("//div[#class='vaadin-text-field-container']//div[#part='input-field' and starts-with(#id, 'vaadin-text-field-input')]//slot[#name='input']/input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')]")
)).sendKeys("pixelhead");
Xpath:
//div[#class='vaadin-text-field-container']//descendant::input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')]
Please check in the dev tools (Google chrome) if we have unique entry in HTML DOM or not.
Steps to check:
Press F12 in Chrome -> go to element section -> do a CTRL + F -> then paste the xpath and see, if your desired element is getting highlighted with 1/1 matching node.
If this is unique //div[#class='vaadin-text-field-container']//descendant::input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')] then you need to check for the below conditions as well.
Check if it's in any iframe/frame/frameset.
Solution: switch to iframe/frame/frameset first and then interact with this web element.
Check if it's in any shadow-root.
Solution: Use driver.execute_script('return document.querySelector to have returned a web element and then operates accordingly.
Make sure that the element is rendered properly before interacting with it. Put some hardcoded delay or Explicit wait and try again.
Solution: time.sleep(5) or
WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//div[#class='vaadin-text-field-container']//descendant::input[#part='value' and starts-with(#aria-labelledby, 'vaadin-text-field-input')]"))).send_keys("test")
If you have redirected to a new tab/ or new windows and you have not switched to that particular new tab/new window, otherwise you will likely get NoSuchElement exception.
Solution: switch to the relevant window/tab first.
If you have switched to an iframe and the new desired element is not in the same iframe context then first switch to default content and then interact with it.
Solution: switch to default content and then switch to respective iframe.
You can start debugging from step1.
Update:
Solution specific to the problem:
Thread.sleep(2000);
WebElement inputButton = (WebElement) ((JavascriptExecutor)driver).executeScript("return document.querySelector('#TextFieldTitle').shadowRoot.querySelector('#vaadin-text-field-input-3 > slot:nth-child(2) > input')");
inputButton.sendKeys("test");
in the place of paste query selector here you will have to go to dev tools again in goog chrome by pressing F12, and then
Go to that input box
Do a right click
Select copy
Select copy JS path.
Ctrl + v into notepad to see what you've got from the dev tool.
It'd be something like this:
document.querySelector("#vaadin-text-field-input-3 > slot:nth-child(2) > input")
replace this paste query selector here with the stuff that is wrapped inside ""
Per the html provided, this works:
driver.find_element(By.XPATH, "//input[#part='value' and contains(#aria-labelledby, 'vaadin-text-field-input')]"]
x = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, "//input[#part='value' and contains(#aria-labelledby, 'vaadin-text-field-input')]")))
x.send_keys('This is typed here by selenium')
print(f"the typed text in input box is: {x.get_attribute('value')}")
Output:
the typed text in input box is: This is typed here by selenium
Process finished with exit code 0
Looks like part of HTML looks like:
<div class="vaadin-text-field-container">
<label part="label" id="vaadin-text-field-label-3"></label>
<div part="input-field" id="vaadin-text-field-input-3">
<slot name="prefix"></slot>
<slot name="input">
<input part="value" tabindex="0" aria-labelledby="vaadin-text-field-input-3">
</slot>
</div>
</div>
Much better is to build your XPATH locator from some id field:
//div[#id='vaadin-text-field-input-3']//input[contains(#aria-labelledby, 'vaadin-text-field-input')]"]
Also, as mentioned earlier have to check:
if it is unique for this page
if the input element is not dynamic
If you found solved that comment you could move to the code part and use the founded locator with WebDriver:
driver.findElement(
By.xpath("//div[#id='vaadin-text-field-input-3']//input[contains(#aria-labelledby, 'vaadin-text-field-input-3')]"]"
)).sendKeys("test");
Also, keep in mind that you have to know that element is already loaded on a page. As suggested before you could use some explicit wait for your element.

How to write the xpath for an element where ::before tag is involved

I am trying to click a select element which is followed by ::before tag. Can anybody please help me to write the XPath or the CSSselector path for the below HTML
Below is the XPath that I have written to click on the month
driver.findElement(By.xpath("//div[contains(#class,'pika-single is-bound left-aligned bottom-aligned')]/div[contains(#class,'pika-lendar')]/div[contains(#class,'pika-title']/div[1]/select[contains(#class,'pika-select pika-select-month')]"));
Pseudo Elements
A CSS pseudo-element is used to style specified parts of an element. It can be used to:
Style the first letter, or line, of an element
Insert content before, or after, the content of an element
::before
::before is a pseudo element which allows you to insert content onto a page from CSS (without it needing to be in the HTML). While the end result is not actually in the DOM, it appears on the page as if it is.
You can find a couple of relevant detailed discussion in:
How locate the pseudo-element ::before using Selenium Python
Not able to get element by xpath inside div with ::before
This usecase
As the date picker is a html-select menu you have to use the Select Class. Moreover, as you have to interact with the drop-down-menu you have to induce WebDriverWait for the elementToBeClickable().
To select the <option> with text as January from the dropdown you can use you can use either of the following Locator Strategies:
Using cssSelector:
WebElement elem = new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.cssSelector("select.pika-select.pika-select-month")));
Select mySelect = new Select(elem);
//selecting the item January by value
mySelect.selectByValue("0");
Using xpath and in a single line:
//selecting the item January by visible text
new Select(new WebDriverWait(driver, 20).until(ExpectedConditions.elementToBeClickable(By.xpath("//select[#class='pika-select pika-select-month']")))).selectByVisibleText("January");
You can find a couple of relevant detailed discussions in:
How to select an option from a dropdown through Selenium WebDriver
How can I get all elements from drop down list in Selenium WebDriver?

How to create a list of the autocomplete <li> elements when parent <ul> element contains the attribute style="display: none;" through Java Selenium

I'm using Java selenium to create a automation project. Now in our web app, there is address input box and has autocomplete feature. The problem is after input a partial address, we need click one of the option from the list. Here is the html:
Now I have tried to get the list. But failed:
WebElement autoCompelet = driver.findElement(By.xpath("/html/body/ul[1]"));
List<WebElement> options = autoCompelet.findElements(By.tagName("li"));
logger.debug(options.size());
for (WebElement option1 : options) {
logger.debug(option1);
}
I can print out , but list is empty.
I've also tried to use wait method like:
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("/html/body/ul[1]/li[1]")));
Unfortunately, none of them works. Is anyone has any idea on this? Any input is appreciated. Thank you guys.
By.className("li") is invalid,the value should be the class name,need to use By.tagName("li") instead.
Also you can change your xpath to get ul by class and then get li directly
WebElement autoCompelet = driver.findElement(By.xpath("//ul[#class='af_list']"));
List<WebElement> options = autoCompelet.findElements(By.tagName("li"));
logger.debug(options.size());
for (WebElement option1 : options) {
logger.debug(option1);
}
You can try as below.
//wait is added in order to check the presence of autocomplete suggestion
WebDriverWait wait=new WebDriverWait(driver,20);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.className("af_list")));
List<WebElement> options = driver.findElements(By.xpath("//ul[#class='af_list']/li"));
logger.debug(options.size());
for (WebElement option1 : options) {
logger.debug(option1.getText());
}
Note: I have assumed af_list class name is used only in the autocomplete.If it is used in other section as well, then please share the full html detail
You can try something like this , after having all the <li> elements in a list, you can easily get the text of each web element by using getText() method which is available in selenium Java binding.
List<WebElement> options = driver.findElements(By.cssSelector("ul.af_list li"));
System.out.println(options.size());
logger.debug(options.size());
for(WebElement options1 : options)
{
logger.debug(options1.getText());
}
Note that it is always a good practice to use css selector over xpath.
You may want to introduce webDriverWait for your scenario. That would explicitly wait for all <li> elements to be present/available in list.
Hope this will help.
As per the HTML you have shared, the desired elements i.e. the <li> tags are within a <ul> tag which is having the attribute style="display: none;". So to access the <li> tags you can use the following solution:
WebElement autoCompelet = driver.findElement(By.xpath("//ul[#class='af_list']"));
((JavascriptExecutor)driver).executeScript("arguments[0].removeAttribute('style')", autoCompelet);
List<WebElement> options = autoCompelet.findElements(By.xpath("./li[#class='af_item']"));
for (WebElement option1 : options) {
System.out.println(option1.getText());
}
I have a similar feature in our web application. In this case, you can use sendKeys() method with Keys.DOWN to literally go to that element and then Keys.ENTER to select it. Normally once you sendKeys() part of the text, the others start populating below, so you basically type to an extent until which just the first option in the populated dropdown texts is the one which you want. Then do sendKeys(Keys.DOWN) followed by sendKeys(Keys.ENTER)

Locating child nodes of WebElements in selenium

I am using selenium to test my web application and I can successfully find tags using By.xpath. However now and then I need to find child nodes within that node.
Example:
<div id="a">
<div>
<span />
<input />
</div>
</div>
I can do:
WebElement divA = driver.findElement( By.xpath( "//div[#id='a']" ) )
But now I need to find the input, so I could do:
driver.findElement( By.xpath( "//div[#id='a']//input" ) )
However, at that point in code I only have divA, not its xpath anymore... I would like to do something like this:
WebElement input = driver.findElement( divA, By.xpath( "//input" ) );
But such a function does not exist.
Can I do this anyhow?
BTW: Sometimes I need to find a <div> that has a certain decendent node. How can I ask in xpath for "the <div> that contains a <span> with the text 'hello world'"?
According to JavaDocs, you can do this:
WebElement input = divA.findElement(By.xpath(".//input"));
How can I ask in xpath for "the div-tag that contains a span with the
text 'hello world'"?
WebElement elem = driver.findElement(By.xpath("//div[span[text()='hello world']]"));
The XPath spec is a suprisingly good read on this.
If you have to wait there is a method presenceOfNestedElementLocatedBy that takes the "parent" element and a locator, e.g. a By.xpath:
WebElement subNode = new WebDriverWait(driver,10).until(
ExpectedConditions.presenceOfNestedElementLocatedBy(
divA, By.xpath(".//div/span")
)
);
For Finding All the ChildNodes you can use the below Snippet
List<WebElement> childs = MyCurrentWebElement.findElements(By.xpath("./child::*"));
for (WebElement e : childs)
{
System.out.println(e.getTagName());
}
Note that this will give all the Child Nodes at same level ->
Like if you have structure like this :
<Html>
<body>
<div> ---suppose this is current WebElement
<a>
<a>
<img>
<a>
<img>
<a>
It will give me tag names of 3 anchor tags here only . If you want all the child Elements recursively , you can replace the above code with
MyCurrentWebElement.findElements(By.xpath(".//*"));
Hope That Helps !!
I also found myself in a similar position a couple of weeks ago. You can also do this by creating a custom ElementLocatorFactory (or simply passing in divA into the DefaultElementLocatorFactory) to see if it's a child of the first div - you would then call the appropriate PageFactory initElements method.
In this case if you did the following:
PageFactory.initElements(new DefaultElementLocatorFactory(divA), pageObjectInstance));
// The Page Object instance would then need a WebElement
// annotated with something like the xpath above or #FindBy(tagName = "input")
The toString() method of Selenium's By-Class produces something like
"By.xpath: //XpathFoo"
So you could take a substring starting at the colon with something like this:
String selector = divA.toString().substring(s.indexOf(":") + 2);
With this, you could find your element inside your other element with this:
WebElement input = driver.findElement( By.xpath( selector + "//input" ) );
Advantage: You have to search only once on the actual SUT, so it could give you a bonus in performance.
Disadvantage: Ugly... if you want to search for the parent element with css selectory and use xpath for it's childs, you have to check for types before you concatenate...
In this case, Slanec's solution (using findElement on a WebElement) is much better.

WebDriver Specific tags

I'm using WebDriver and I have a WebElement that has a few root tags, each of these div tags has multiple div tags within them. Is there a way to get just the root tags?
Eg.
WebElement grabbed from the "someid" field below contains the following data within. I'd like to grab the root div tags from this element (example below has three) By default it grabs the root, and any div tags within, is there a way to just grab those three div tags? There is not specific names or id to grab them, but if I can get the root div tags, I can loop through them easier to locate the data I need in each. Thanks for any direction, or let me know if I need to get more information. Thanks
<div id="someid">
<div><more html that has div tags></div>
<div><more html that has div tags></div>
<div><more html that has div tags></div>
</div>
WebElement weMain = driver.findElement(By.id("someid"));
// Looking for a way to just grab the root divs to my WebElement
List<WebElement> weSomeElements = weMain.findElements(By.tagName("div"));
Thanks for any direction
Try:
driver.findElements(By.xPath("//div[#id='someid']/div")
I think that should return the 3 divs.
try this
List<WebElement> weSomeElements =
driver.findElements(By.CssSelector("div[id='someid'] div"));
Then you can do something like this:
WebElement element = weSomeElement.select(n).findElements(by.tagName("div"));
var elementText = element.Text();

Categories

Resources