I am stuck with the WebElement which I am trying to access on the Webpage with the below code but still getting mentioned error. The Element allows to autocomplete the subjects and multiple subjects to be entered in the single text box.
WebElement Subjects = driver.findElement(By.xpath("//*[#id='subjectsContainer']"));
JavascriptExecutor executor = (JavascriptExecutor)driver;
executor.executeScript("arguments[0].click();", Subjects);
String subject1 = prop.getProperty("subject1");
String subject2 = prop.getProperty("subject2");
String subject3 = prop.getProperty("subject3");
Subjects.sendKeys(subject1);
Subjects.sendKeys(Keys.ENTER);
Subjects.sendKeys(subject2);
Subjects.sendKeys(Keys.ENTER);
Subjects.sendKeys(subject3);
Subjects.sendKeys(Keys.ENTER);
driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
WebElement accepts the multiple subjects to be entered by user which autocompletes
Selenium WebElement error
Looks like you need to add some delay or element visibility validation before you trying to send text to this element.
Additionally it's not recommended to use JavaScript click instead of WebDriver click unless you have no choice.
Please find below answer for my query as it is now working fine after putting Explict wait condition and locating correct webElement with tagname 'input'.
//Load the data from the Properties file
String subject1 = prop.getProperty("subject1");
String subject2 = prop.getProperty("subject2");
String subject3 = prop.getProperty("subject3");
//WebElement to capture the visibility condition
WebElement Subjects =driver.findElement(By.xpath("//*[#id='subjectsContainer']"));
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until(ExpectedConditions.elementToBeClickable(Subjects)).click();
WebElement Sub1 = driver.findElement(By.xpath("//input[#id='subjectsInput']"));
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//input[#id='subjectsInput']")));
Sub1.sendKeys(subject1); //Send first Subject
Sub1.sendKeys(Keys.ENTER);
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//input[#id='subjectsInput']")));
System.out.println("ENtered subject 1" + subject1);
Sub1.sendKeys(subject2); //Send second subject
Sub1.sendKeys(Keys.ENTER);
System.out.println("ENtered subject 2" + subject2);
wait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//input[#id='subjectsInput']")));
Sub1.sendKeys(subject3); //Send thrid subject
Sub1.sendKeys(Keys.ENTER);
System.out.println("ENtered subject 3" + subject3);
A quick and dirty solution to many not interactable errors is simply this:
Thread.sleep(500);
I find this tactic to be extremely useful in quickly debugging problematic elements before implementing a more performant and elegant wait solution like you mentioned in your update.
Related
I had taken the xPath as below but I am not able to get the value 145666 when I try to print.
WebDriverWait wait = new WebDriverWait(driver, 20);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//span[contains(#class,'mat-content')]//div[#id='demandTrackingID']")));
Now iam getting the below Expception :
org.openqa.selenium.TimeoutException: Expected condition failed: waiting for visibility of element located by By.xpath: //span[contains(#class,'mat-content')]//div[#id='TrackID']
Note: This Html Element is a Non Visible Element I need to inspect the panel and get this element as there is no field for this Element
HTML:
<div _ngcontent-wbh-c179="" id="TrackID" class="disp-none">14566 </div>
Please try this:
WebElement trackid= driver.findElement(By.xpath("//span[contains(#class,'mat-content')]//div[#id='TrackID']"));
System.out.println("getid:"+ trackid.getText());
You will possible need to add an explicit wait before that to make element fully loaded before retrieving it's content
WebDriverWait wait = new WebDriverWait(driver, 30);
wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//span[contains(#class,'mat-content')]//div[#id='TrackID']")));
WebElement trackid= driver.findElement(By.xpath("//span[contains(#class,'mat-content')]//div[#id='TrackID']"));
System.out.println("getid:"+ trackid.getText());
id is ID not id
so use this :
WebElement trackid= driver.findElement(By.id("TrackID"));
System.out.println("getid:"+ trackid.getText());
The element that contains the text you want has an ID, TrackID, not to be confused with the "ID" text on the page. You can use that to locate the element more easily than the XPath you are currently using. Once you have the element, you can use WebDriverWait to wait for the element to contain text. Code is below.
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until((ExpectedCondition<Boolean>) d -> d.findElement(By.id("TrackID")).getText().length() != 0);
Given the class class="disp-none" on the element, I'm guessing that the element might not be visible. If this is the case, you'll have to use JavaScript to get the invisible text. Selenium was designed to only interact with visible elements and will throw an exception if you try to interact with elements that are not visible.
WebDriverWait wait = new WebDriverWait(driver, 10);
wait.until((ExpectedCondition<Boolean>) d -> d.findElement(By.id("TrackID")).getText().length() != 0);
WebElement element = driver.findElement(By.id("TrackID"));
String trackID = (JavascriptExecutor)driver.executeScript("return arguments[0].text", element);
I am having problem with locating WebElement using different locators. In the below html tag I tried locating the "write a review" WebElement with different locators like linkText,xpath,classname but still getting NoSuchElementException
-->url https://www.tripadvisor.in/-->search for Club Mahindra-->click on Club Mahindra-->click on write a review.
<a href="/UserReview-g641714-d1156207-Club_Mahindra_Madikeri_Coorg-
Madikeri_Kodagu_Coorg_Karnataka.html" target="_blank" class="ui_button
primary">Write a review</a>
Locators used
By.xpath("//*[#id="component_12"]/div/div[2]/div/div[2]/div/div[1]/a")
By.xpath("//a[#href='/UserReview-g641714-d1156207-
Club_Mahindra_Madikeri_Coorg-Madikeri_Kodagu_Coorg_Karnataka.html']")
By.className("ui_button primary")
By.linkText("Write a review")
I am really confused. What am I doing wrong?
I have tired to analyse and implement the same. Below are my findings:
-> Application waiting time is more as there are lots of dynamic loads applicable for the page.
-> Proper waits needs to be implemented
-> Check whether all the pages are getting opened in the same tab or clicking on each link is redirecting to new tabs, if so then we have to switch to that particular window.
-> Below code works like a pro for me.
driver.get("https://www.tripadvisor.in/");
WebDriverWait wait = new WebDriverWait(driver, 120);
WebElement ele1 =
wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[text()='Where to?']")));
ele1.click();
WebElement ele2= wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[#placeholder='Where to?']")));
ele2.sendKeys("club mahindra, india");
WebElement ele3= wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//span[contains(text(),'Search for ')]")));
ele3.click();
WebElement ele4= wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//span[contains(text(),'Club Mahindra Madikeri, Coorg')]")));
ele4.click(); //this click leads to a new tab
Set<String> winHandles = driver.getWindowHandles();
for(String str : winHandles) {
driver.switchTo().window(str);
}
System.out.println(driver.getTitle());
WebElement ele;
int i=1;
while(true) {
try {
ele = wait.until(ExpectedConditions.elementToBeClickable(By.xpath("//*[text()='Write a review']")));
break;
}catch(Exception e) {
System.out.print(i++);
}
}
System.out.println();
Actions action = new Actions(driver);
action.moveToElement(ele);
ele.click();
System.out.println("Clicked on the 'Write a review button'");
you can try
//a[contains(text(),'Write a review')]
I had been following the discussion How to automate shadow DOM elements using selenium? to work with #shadow-root (open) elements.
While in the process of locating the Clear data button within the Clear browsing data popup, which appears while accessing the url chrome://settings/clearBrowserData through Selenium I am unable to locate the following element:
#shadow-root (open)
<settings-privacy-page>
Snapshot:
Using Selenium following are my code trials and the associated errors encountered:
Attempt 1:
WebElement root5 = shadow_root4.findElement(By.tagName("settings-privacy-page"));
Error:
Exception in thread "main" org.openqa.selenium.JavascriptException: javascript error: b.getElementsByTagName is not a function
Attempt 2:
WebElement root5 = shadow_root4.findElement(By.cssSelector("settings-privacy-page"));
Error:
Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"settings-privacy-page"}
Attempt 3:
WebElement root5 = (WebElement)((JavascriptExecutor)shadow_root4).executeScript("return document.getElementsByTagName('settings-privacy-page')[0]");
Error:
Exception in thread "main" java.lang.ClassCastException: org.openqa.selenium.remote.RemoteWebElement cannot be cast to org.openqa.selenium.JavascriptExecutor
Incase if it is helpful the initial code block (till the above line) works perfect:
driver.get("chrome://settings/clearBrowserData");
WebElement root1 = driver.findElement(By.tagName("settings-ui"));
WebElement shadow_root1 = expand_shadow_element(root1);
WebElement root2 = shadow_root1.findElement(By.cssSelector("settings-main#main"));
WebElement shadow_root2 = expand_shadow_element(root2);
WebElement root3 = shadow_root2.findElement(By.cssSelector("settings-basic-page[role='main']"));
WebElement shadow_root3 = expand_shadow_element(root3);
WebElement root4 = shadow_root3.findElement(By.cssSelector("settings-section[page-title='Privacy and security']"));
WebElement shadow_root4 = expand_shadow_element(root4);
PS: expand_shadow_element() works flawless.
If you are trying to get 'Clear Data' element then you can use the below js to get the element and then perform.
return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')
Here is the sample script.
driver.get("chrome://settings/clearBrowserData");
driver.manage().window().maximize();
JavascriptExecutor js = (JavascriptExecutor) driver;
WebElement clearData = (WebElement) js.executeScript("return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')");
// now you can click on clear data button
clearData.click();
Edit 2: Explanation
Problem: Selenium does not provide explicit support to work with Shadow DOM elements, as they are not in the current dom. That's the reason why we will get NoSuchElementException exception when try to access the elements in the shadow dom.
Shadow DOM:
Note: We will be referring to the terms shown in the picture. So please go through the picture for better understanding.
Solution:
In order to work with shadow element first we have to find the shadow host to which the shadow dom is attached. Here is the simple method to get the shadow root based on the shadowHost.
private static WebElement getShadowRoot(WebDriver driver,WebElement shadowHost) {
JavascriptExecutor js = (JavascriptExecutor) driver;
return (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
}
And then you can access the shadow tree element using the shadowRoot Element.
// get the shadowHost in the original dom using findElement
WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS"));
// get the shadow root
WebElement shadowRoot = getShadowRoot(driver,shadowHost);
// access shadow tree element
WebElement shadowTreeElement = shadowRoot.findElement(By.cssSelector("shadow_tree_element_css"));
In order to simplify all the above steps created the below method.
public static WebElement getShadowElement(WebDriver driver,WebElement shadowHost, String cssOfShadowElement) {
WebElement shardowRoot = getShadowRoot(driver, shadowHost);
return shardowRoot.findElement(By.cssSelector(cssOfShadowElement));
}
Now you can get the shadowTree Element with single method call
WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS_Goes_here));
WebElement shadowTreeElement = getShadowElement(driver,shadowHost,"shadow_tree_element_css");
And perform the operations as usual like .click(), .getText().
shadowTreeElement.click()
This Looks simple when you have only one level of shadow DOM. But here, in this case we have multiple levels of shadow doms. So we have to access the element by reaching each shadow host and root.
Below is the snippet using the methods that mentioned above (getShadowElement and getShadowRoot)
// Locate shadowHost on the current dom
WebElement shadowHostL1 = driver.findElement(By.cssSelector("settings-ui"));
// now locate the shadowElement by traversing all shadow levels
WebElement shadowElementL1 = getShadowElement(driver, shadowHostL1, "settings-main");
WebElement shadowElementL2 = getShadowElement(driver, shadowElementL1,"settings-basic-page");
WebElement shadowElementL3 = getShadowElement(driver, shadowElementL2,"settings-section > settings-privacy-page");
WebElement shadowElementL4 = getShadowElement(driver, shadowElementL3,"settings-clear-browsing-data-dialog");
WebElement shadowElementL5 = getShadowElement(driver, shadowElementL4,"#clearBrowsingDataDialog");
WebElement clearData = shadowElementL5.findElement(By.cssSelector("#clearBrowsingDataConfirm"));
System.out.println(clearData.getText());
clearData.click();
You can achieve all the above steps in single js call as at mentioned at the beginning of the answer (added below just to reduce the confusion).
WebElement clearData = (WebElement) js.executeScript("return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')");
Screenshot:
I had to do a similar test which required clearing browsing the chrome history. A minor difference was that I was clearing the data after going to the advanced section of the pop-up. As you are struggling to click only the "Clear data" button, I'm quite sure that you've missed one or two hierarchy elements mistakenly. Or got confused between sibling and parent elements probably. As per seeing your code, I assume that you already know that to access a particular shadow DOM element you need proper sequencing and it has been explained also quite nicely above.
Coming right at your problem now, here is my code snippet which is working correctly. The code waits until the data is cleaned and then will proceed to your next action-
public WebElement expandRootElement(WebElement element) {
WebElement ele = (WebElement) ((JavascriptExecutor) driver).executeScript("return arguments[0].shadowRoot",
element);
return ele;
}
public void clearBrowsingHistory() throws Exception {
WebDriverWait wait = new WebDriverWait(driver, 15);
driver.get("chrome://settings/clearBrowserData");
// Get shadow root elements
WebElement shadowRoot1 = expandRootElement(driver.findElement(By.xpath("/html/body/settings-ui")));
WebElement root2 = shadowRoot1.findElement(By.cssSelector("settings-main"));
WebElement shadowRoot2 = expandRootElement(root2);
WebElement root3 = shadowRoot2.findElement(By.cssSelector("settings-basic-page"));
WebElement shadowRoot3 = expandRootElement(root3);
WebElement root4 = shadowRoot3
.findElement(By.cssSelector("#advancedPage > settings-section > settings-privacy-page"));
WebElement shadowRoot4 = expandRootElement(root4);
WebElement root5 = shadowRoot4.findElement(By.cssSelector("settings-clear-browsing-data-dialog"));
WebElement shadowRoot5 = expandRootElement(root5);
WebElement root6 = shadowRoot5
.findElement(By.cssSelector("cr-dialog div[slot ='button-container'] #clearBrowsingDataConfirm"));
root6.click();
wait.until(ExpectedConditions.invisibilityOf(root6));
}
It should work properly in your case too if you don't intend to change any of the options selected by default in the pop-up (In that case, you will have to add a few more codes regarding selecting those checkboxes). Please tell me if this solves your issue. Hope this is helpful
I've added a snapshot of the the screen here too-
image
The Locator Strategy in #supputuri's answer using document.querySelector() works perfect through google-chrome-devtools
However, as the desired element opens from the shadow-dom you need to induce WebDriverWait for the elementToBeClickable() and you can you the following solution:
Code Block:
driver.get("chrome://settings/clearBrowserData");
new WebDriverWait(driver, 5).until(ExpectedConditions.elementToBeClickable((WebElement) ((JavascriptExecutor)driver).executeScript("return document.querySelector('settings-ui').shadowRoot.querySelector('settings-main').shadowRoot.querySelector('settings-basic-page').shadowRoot.querySelector('settings-section > settings-privacy-page').shadowRoot.querySelector('settings-clear-browsing-data-dialog').shadowRoot.querySelector('#clearBrowsingDataDialog').querySelector('#clearBrowsingDataConfirm')"))).click();
System.out.println("Clear data Button Clicked");
Console Output:
Clear data Button Clicked
I was getting InvalidArgumentEXception when trying to identify shadowRoot element in DOM using Selenium 4.3.0 and Chrome Version 103.0.5060.134
The solution to this is
SearchContext se= driver.findElment(By.locator("...").getShadowRoot(); return type is SearchContext
in the above line try using locator as xpath
and secondly trying to locate element using SearchContext reference e.g.
WebElement we= se.findElement(By.locator("....."));
use locater as cssSelector
And boom it works like charm
Didn't find this solution available and took me half a day to figure out
Hope this helps!!!
I've just begun using selenium web-driver. I'm trying to use it login and navigate/scrape.
public static void main(String[] args) {
java.util.logging.Logger.getLogger("com.gargoylesoftware.htmlunit").setLevel(java.util.logging.Level.OFF);
java.util.logging.Logger.getLogger("org.apache.http").setLevel(java.util.logging.Level.OFF);
WebDriver driver = new HtmlUnitDriver(BrowserVersion.CHROME);
driver.get("my_Site_I_Reference");
#SuppressWarnings("unused")
WebDriverWait wait = new WebDriverWait(driver, 4000);
WebElement name = driver.findElement(By.id("LoginUsername"));
name.sendKeys("exampleName");
name.submit();
System.out.println("Page title is: " + driver.getTitle());
driver.quit();
}
I'm using the code above to try and enter a username into the username field but I am constantly met with this error:
"org.openqa.selenium.NoSuchElementException: Unable to locate element with ID: LoginUsername"
I'm almost certain the element is called LoginUsername as shown in the picture:
Any help would be very much appreciated, thanks!
It would be still a guess, but I suspect the element is inside an iframe. If this is the case, switch to the iframe before looking for the element:
driver.switchTo().frame("frame_id_or_name");
WebElement name = driver.findElement(By.id("LoginUsername"));
Can you please share HTML code in detail ? Meanwhile, You can also give a try by changing it to below and share what do you see?
WebElement name = driver.findElement(By.xpath(“//*[#id="LoginUsername"]”));
OR
WebElement name = driver.findElement(By.xpath(“//input[#id="LoginUsername"]”));
Turns out that I had Javascript disabled by default I found this out by printing the page source my code loaded
((HtmlUnitDriver) driver).setJavascriptEnabled(true);
Once enabled selenium behaved like I expected
I'm testing a web app, in which after opening a page I need to select a radio button after that a pop-up will appear. In that pop-up I'm selecting an ID. Once selected, it will come to main window, in which I'm trying to select a text field with ID. But I receive the following error:
Element not found in the cache - perhaps the page has changed since it was looked up
Command duration or timeout: 50.14 seconds
driver.findElement(By.xpath("html/body/section/div/div[2]/div[3]/div/div/div/div/ul/li[1]/a")).click();
driver.findElement(By.xpath("html/body/section/div/div[12]/div[1]/div/div[2]/div[3]/div/div/div/div/button[1]")).click();
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
String mainWindowHandle=driver.getWindowHandle();
driver.findElement(By.xpath("html/body/div[3]/div/div")).click();
driver.manage().timeouts().implicitlyWait(60, TimeUnit.SECONDS);
Set s = driver.getWindowHandles();
Iterator ite = s.iterator();
while(ite.hasNext())
{
String popupHandle=ite.next().toString();
if(!popupHandle.contains(mainWindowHandle))
{
driver.switchTo().window(popupHandle);
}
}
driver.findElement(By.xpath("html/body/div[3]/div/div/div[3]/div/div/div[1]/div[2]/div[4]/div[2]/a")).click();
driver.manage().timeouts().implicitlyWait(50, TimeUnit.SECONDS);
driver.switchTo().window( mainWindowHandle );
driver.findElement(By.id("Title")).sendKeys("auto title");
Problem is I cannot find the textfield with ID: Title" for inserting "auto title.
Thanks in Advance.
Instead of using sleep() with hardcoded time intervals, use an explicit wait:
WebDriverWait wait = new WebDriverWait(driver, 30);
WebElement title = wait.until(ExpectedConditions.visibilityOfElementLocated(By.id("Title")));
title.sendKeys("auto title");
Finally found a solution by adding
Thread.sleep(5000);
before finding the element and it solved.