Как взаимодействовать с элементами внутри # shadow-root (open) при очистке данных браузера браузера Chrome с помощью cssSelector - PullRequest
4 голосов
/ 30 мая 2019

Я следил за обсуждением Как автоматизировать теневые элементы DOM с использованием селена? для работы с #shadow-root (open) элементами.

В процессе поиска Очистить данныеКнопка во всплывающем окне Очистить данные о просмотре , которое появляется при обращении к URL chrome://settings/clearBrowserData через Селен Я не могу найти следующий элемент:

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

Снимок:

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() работает безупречно.

Кто-нибудь может мне помочь, пожалуйста?

Ответы [ 4 ]

12 голосов
/ 30 мая 2019

Если вы пытаетесь получить элемент «Очистить данные», вы можете использовать приведенные ниже 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 не предоставляет явной поддержки для работы с Теневыми DOM-элементами , поскольку их нет в текущем dom. По этой причине мы получим исключение NoSuchElementException при попытке доступа к элементам в shadow dom.

Shadow DOM: enter image description here

Примечание: мы будем ссылаться на термины, показанные на картинке. Пожалуйста, пройдите по картинке для лучшего понимания.

Решение:

Чтобы работать с теневым элементом , сначала мы должны найти 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");

И выполняйте операции как обычно, как .click(), .getText().

shadowTreeElement.click()

Это выглядит просто, когда у вас есть только один уровень DOM тени. Но здесь, в этом случае у нас есть несколько уровней теневых домов. Таким образом, мы должны получить доступ к элементу, достигнув каждого теневого хоста и корня. enter image description here

Ниже приведен фрагмент кода с использованием методов, упомянутых выше (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')");

Скриншот: enter image description here

0 голосов
/ 25 июня 2019

Мне пришлось сделать аналогичный тест, который потребовал очистки просмотра истории Chrome.Небольшое отличие заключалось в том, что я очищал данные после перехода в расширенный раздел всплывающего окна.Поскольку вы пытаетесь нажать только кнопку «Очистить данные», я совершенно уверен, что вы ошибочно пропустили один или два элемента иерархии.Или запутался между родным и родным элементами, вероятно.Что касается просмотра вашего кода, я предполагаю, что вы уже знаете, что для доступа к конкретному теневому DOM-элементу вам нужно правильное упорядочение, и это также было довольно хорошо объяснено выше.
Теперь перейдем прямо к вашей проблеме, вот мой фрагмент кода, которыйработает правильно.Код ждет, пока данные не будут очищены, и затем перейдет к следующему действию -

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

. Он должен работать правильно и в вашем случае, если вы не собираетесь изменять какие-либо параметры, выбранные по умолчанию ввсплывающее окно (в этом случае вам нужно будет добавить еще несколько кодов для выбора этих флажков).Пожалуйста, скажите мне, если это решит вашу проблему.Надеюсь, что это полезно. Я также добавил здесь снимок экрана - image

0 голосов
/ 19 июня 2019

@ supputuri ответ является рабочим и принятым ответом как Стратегия локатора через document.querySelector() отлично работает через

Однако, когда нужный элемент открывается из , вам нужно вызвать WebDriverWait для elementToBeClickable(), и вы можете найти следующее решение:

  • Кодовый блок:

    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
    
0 голосов
/ 30 мая 2019

Мне интересно, если это отдельное дочернее окно, так как оно находится на переднем плане и поэтому не может найти элемент на экране. Как только вы получите ручку дочернего окна, вы сможете захватить элемент?

...