If you need to access the 'Clear Data' element, you can utilize the following JavaScript code to locate the element and then take action on it.
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 an example script to demonstrate this:
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 the clear data button
clearData.click();
Edit 2: Explanation
Issue: Selenium does not directly support interacting with Shadow DOM elements due to them not being in the current DOM structure. This results in a NoSuchElementException
when trying to access elements within the shadow dom
.
Shadow DOM:
https://i.sstatic.net/nHa2j.png
Note: For better comprehension, refer to the terms depicted in the image.
Solution:
To work with a shadow element, initially identify the shadow host
to which the shadow DOM is attached. The following method can be used to obtain 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);
}
Then, navigate through the shadow tree element using the shadowRoot Element.
// locate the shadowHost in the original DOM via findElement
WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS"));
// acquire the shadow root
WebElement shadowRoot = getShadowRoot(driver, shadowHost);
// access shadow tree element
WebElement shadowTreeElement = shadowRoot.findElement(By.cssSelector("shadow_tree_element_css"));
To streamline the above processes, the subsequent method has been devised.
public static WebElement getShadowElement(WebDriver driver, WebElement shadowHost, String cssOfShadowElement) {
WebElement shadowRoot = getShadowRoot(driver, shadowHost);
return shadowRoot.findElement(By.cssSelector(cssOfShadowElement));
}
Henceforth, the shadowTree Element can be fetched with just a single method invocation
WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS_Goes_here));
WebElement shadowTreeElement = getShadowElement(driver, shadowHost, "shadow_tree_element_css");
Subsequently, standard operations like .click()
, .getText()
can be performed as usual.
shadowTreeElement.click()
This procedure appears uncomplicated for scenarios having only one level of shadow DOM. However, in this instance, there are multiple levels of shadow DOMs necessitating access to each element by traversing through every shadow host and root.
https://i.sstatic.net/UvWec.png
The ensuing snippet showcases usage of the aforementioned methods (getShadowElement and getShadowRoot)
// Identify shadowHost on the prevailing DOM
WebElement shadowHostL1 = driver.findElement(By.cssSelector("settings-ui"));
// subsequently pinpoint the shadowElement while navigating all levels of shadow
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();
All the preceding steps can be consolidated into a single JavaScript call as elucidated at the outset of the answer (included below chiefly to minimize 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:
https://i.sstatic.net/k8PAO.png