关于shadow-root影子控件的selenium ui自动化

首先这个控件和iframe有异曲同工之妙,也是嵌套的一个html,所以定位不能像普通定位一样

下面实践一下

首先准备一个root.html

<!DOCTYPE html>
<html>
<head>
  <title>带有shadow-root的页面</title>
</head>
<body>
  <h1 class="test">带有shadow-root的页面</h1>

  <template id="shadowRootTemplate">
    <style>
      /* 在shadow root中定义样式 */
      .message {
        color: blue;
        font-size: 18px;
      }
    </style>
    <div class="message">
      这是一个带有shadow-root的页面。
    </div>
  </template>

  <script>
    // 获取template元素
    const template = document.querySelector('#shadowRootTemplate');

    // 在<body>中创建shadow root
    const shadowRoot = document.body.attachShadow({ mode: 'open' });

    // 将template内容复制到shadow root中
    const templateContent = template.content.cloneNode(true);
    shadowRoot.appendChild(templateContent);
  </script>
</body>
</html>

这里偷个懒,使用python http服务启动一个临时的页面

python -m http.server

成功之后访问 http://localhost:8000/root.html

这样我们就具备了一个临时测试用的页面了

正题

首先 selenium 给元素对象提供了一个shadow_root的方法

我们先来定位到shadow-root元素的父级节点

driver.find_element(By.TAG_NAME, 'body')

然后根据父级节点的元素对象找到该元素下面的shadow-root

root_ele = driver.find_element(By.TAG_NAME, 'body')

ele = root_ele.shadow_root

此时,我们就可以使用ele元素对象去定位ele元素对象下的元素

例如:

ele.find_element(By.CSS_SELECTOR, '.message').get_attribute('innerText')

这样就可以对元素进行操作了

完整代码

from selenium import webdriver
from selenium.webdriver.common.by import By

driver = webdriver.Edge()
driver.get('http://localhost:8000/root.html')
driver.implicitly_wait(10)
# 定位shadow-root的父元素
root_ele = driver.find_element(By.TAG_NAME, 'body')
# 获取shadow-root对象
ele = root_ele.shadow_root
# 使用css 定位 shadow-root 下的元素
print(ele.find_element(By.CSS_SELECTOR, '.message').get_attribute('innerText'))

shadow-root多层嵌套的场景

from selenium import webdriver


class TestDemo:
    def test_demo(self):
        self.driver = webdriver.Chrome()
        self.driver.get('chrome://settings/clearBrowserData')
        ele = self.driver.execute_script('return document.querySelector("settings-ui").shadowRoot.querySelector("#main").shadowRoot.querySelector(".cr-centered-card-container").shadowRoot.querySelector("settings-privacy-page").shadowRoot.querySelector("settings-clear-browsing-data-dialog").shadowRoot.querySelector("#clearBrowsingDataConfirm")')
        ele.click()