java - 如何使用cssSelector清除Chrome瀏覽器的瀏覽數據時,#shadow-root(打開)中的元素進行交互




selenium css-selectors (3)

我一直在討論 如何使用selenium自動化陰影DOM元素? 使用 #shadow-root (open) 元素。

在通過 Selenium 訪問url chrome://settings/clearBrowserData 出現的 清除瀏覽數據 彈出窗口中找到 清除數據 按鈕的過程中,我無法找到以下元素:

#shadow-root (open)
<settings-privacy-page>

快照:

使用 Selenium 以下是我的代碼試驗和遇到的相關錯誤:

  • 嘗試1:

    WebElement root5 = shadow_root4.findElement(By.tagName("settings-privacy-page"));
    • 錯誤:

      Exception in thread "main" org.openqa.selenium.JavascriptException: javascript error: b.getElementsByTagName is not a function
  • 嘗試2:

    WebElement root5 = shadow_root4.findElement(By.cssSelector("settings-privacy-page"));
    • 錯誤:

      Exception in thread "main" org.openqa.selenium.NoSuchElementException: no such element: Unable to locate element: {"method":"css selector","selector":"settings-privacy-page"}
  • 嘗試3:

    WebElement root5 = (WebElement)((JavascriptExecutor)shadow_root4).executeScript("return document.getElementsByTagName('settings-privacy-page')[0]");
    • 錯誤:

      Exception in thread "main" java.lang.ClassCastException: org.openqa.selenium.remote.RemoteWebElement cannot be cast to org.openqa.selenium.JavascriptExecutor

如果它有用,初始代碼塊(直到上面的行)完美運行:

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() 完美無瑕。

有人可以幫幫我嗎?


@ supputuri的答案是工作和接受的答案,因為 定位器策略 通過 document.querySelector() 完美地通過 google-chrome-devtools google-chrome-devtools

但是,當所需元素從 shadow-dom 打開時,您需要為 elementToBeClickable() 引入 WebDriverWait ,您可以使用以下解決方案:

  • 代碼塊:

    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");
  • 控制台輸出:

    Clear data Button Clicked

如果您正在嘗試獲取“清除數據”元素,那麼您可以使用以下js來獲取元素然後執行。

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')

這是示例腳本。

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();

編輯2:解釋

問題: Selenium沒有為使用 Shadow DOM元素 提供明確的支持,因為它們不在當前的dom中。 這就是我們在嘗試訪問 shadow dom 的元素時會得到 NoSuchElementException 異常的原因。

影子DOM:

注意:我們將參考圖片中顯示的條款。 所以請仔細閱讀圖片以便更好地理解。

解:

為了首先使用 陰影元素, 我們必須找到陰影dom所附著的 shadow host 。 以下是基於shadowHost獲取陰影根的簡單方法。

private static WebElement getShadowRoot(WebDriver driver,WebElement shadowHost) {
    JavascriptExecutor js = (JavascriptExecutor) driver;
    return (WebElement) js.executeScript("return arguments[0].shadowRoot", shadowHost);
}

然後,您可以使用shadowRoot元素訪問陰影樹元素。

// 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"));

為了簡化以上所有步驟,創建了以下方法。

public static WebElement getShadowElement(WebDriver driver,WebElement shadowHost, String cssOfShadowElement) {
    WebElement shardowRoot = getShadowRoot(driver, shadowHost);
    return shardowRoot.findElement(By.cssSelector(cssOfShadowElement));
}

現在,您可以使用單個方法調用獲取shadowTree元素

WebElement shadowHost = driver.findElement(By.cssSelector("shadowHost_CSS_Goes_here));
WebElement shadowTreeElement = getShadowElement(driver,shadowHost,"shadow_tree_element_css");

並像往常一樣執行操作,如 .getText()

shadowTreeElement.click()

當你只有一級shadow DOM時,這看起來很簡單。 但在這裡,在這種情況下,我們有多個級別的陰影doms。 所以我們必須通過到達每個影子主機和root來訪問該元素。

下面是使用上面提到的方法的片段(getShadowElement和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();

您可以在單個js調用中實現上述所有步驟,如答案開頭所述(下面添加以減少混淆)。

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')");

截圖:


我想知道,如果這是一個單獨的子窗口,因為它在前景,這就是為什麼無法在屏幕上找到uielement。 一旦你獲得了子窗口句柄,你應該能夠抓住這個元素嗎?







shadow-dom