Shadow DOM w Selenium

Ten film jest dla wszystkich, co dłubią w Selenium, ale przykłady będą pokazane w C# i Javie. Pokażę Ci jak się dobrać do Shadow DOM w Selenium, a konkretnie do elementów, które się w nim znajdują.

Shadow DOM w Selenium. Co zobaczysz w filmie?

W tym filmie bardzo krótko powiem co to jest Shadow DOM, ale pokażę jak on wygląda w kodzie HTML. Następnie zobaczysz jak zaznaczać w nim elementy oraz jak czekać (gdy potrzeba) na shadow roota.

Dodatkowe linki

W tej lekcji przyda Ci się wiedza dotycząca wykonywania JavaScriptów w Selenium (Java i C#).

Konieczna będzie też wiedza na temat waitów w Selenium (moduł “Czekanie i pobieranie informacji” w Javie i C#).

Jak wydobyć element z Shadow DOM: kroki

Poniżej znajdziesz skrót tego, o czym mówię na filmie, czyli co po kolei trzeba zrobić, żeby móc zaznaczyć jakiś element w Shadow DOM i potem wykonać na nim jakąś akcję.

  1. Namierz hosta. Host jest elementem, do którego przyczepiony jest Shadow DOM. Będzie to więc element znajdujący się zaraz nad shadow root. Zbuduj dla tego elementu lokator i zapisz element w zmiennej.
IWebElement host = driver.FindElement(By.CssSelector("#host"));
WebElement host = driver.findElement(By.cssSelector("#host"));
  1. Za pomocą skryptu JavaScript, do którego przekażemy w parametrze hosta, zaznacz shadow roota. Zapisz go w zmiennej.
IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
IWebElement root = (IWebElement)js.ExecuteScript("return arguments[0].shadowRoot", host);
JavascriptExecutor js = (JavascriptExecutor)driver;
WebElement root =  (WebElement)js.executeScript("return arguments[0].shadowRoot", host);
  1. Namierz element w Shadow DOM i dokończ test. Korzystając z roota pobranego JavaScriptem, wyszukaj interesujący Cię element. W tym celu użyj metody findElement (FindElement w C#) na roocie. Dokończ test wykonując akcje na pobranym elemencie i wykonując asercję.
string text = "Ojeny jestem w shadow DOMie";
root.FindElement(By.CssSelector("#input")).SendKeys(text);
root.FindElement(By.CssSelector("button")).Click();
string displayedText = root.FindElement(By.CssSelector("#output")).Text;
Assert.AreEqual("Wprowadzony tekst: " + text, displayedText);    
String text = "Ojeny jestem w shadow DOMie";
root.findElement(By.cssSelector("#input")).sendKeys(text);
root.findElement(By.cssSelector("button")).click();
String displayedText = root.findElement(By.cssSelector("#output")).getText();
Assertions.assertEquals("Wprowadzony tekst: " + text, displayedText);

A co, gdy musimy poczekać?

Jeżeli musimy poczekać na hosta, czyli na element pod który podpięty jest shadow root, no to właściwie sprawa jest prosta. Trzeba na niego poczekać tak, jak na każdy inny element. W kursach Selenium w Javie i Selenium w C# znajdziesz całe moduły o czekaniu na elementy.

Natomiast jeżeli musisz poczekać na Shadow DOM, to możesz poczekać np. aż JavaScript, którego używaliśmy do złapania shadow roota, zwróci nam tego roota. Dokładnie tłumaczę to na filmie, natomiast kod znajdziesz poniżej.

WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(4));
IWebElement root = wait.Until(d => (IWebElement)js.ExecuteScript("return arguments[0].shadowRoot", host));
WebDriverWait wait = new WebDriverWait(driver, 5);
WebElement root = wait.until(d -> (WebElement)js.executeScript("return arguments[0].shadowRoot", host));

Kod

using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Support.UI;
using System;
using System.Collections.Generic;
using System.Text;
namespace TestowyProjekt.NaRaz
{
    class ShadowDOM
    {
        IWebDriver driver;
        
        [SetUp]
        public void Setup()
        {
            driver = new ChromeDriver();
            driver.Manage().Window.Position = new System.Drawing.Point(8, 30);
            driver.Manage().Window.Size = new System.Drawing.Size(1290, 730);
            driver.Manage().Timeouts().PageLoad = TimeSpan.FromSeconds(10);            
            driver.Navigate().GoToUrl("https://fakestore.testelka.pl/shadow-dom-w-selenium");
            driver.FindElement(By.CssSelector(".woocommerce-store-notice__dismiss-link")).Click();
        }
        [TearDown]
        public void QuitDriver()
        {
            driver.Quit();
        }
        [Test]
        public void ShowTextTest()
        {
            IWebElement host = driver.FindElement(By.CssSelector("#host"));
            IJavaScriptExecutor js = (IJavaScriptExecutor)driver;
            WebDriverWait wait = new WebDriverWait(driver, TimeSpan.FromSeconds(4));
            IWebElement root = wait.Until(d => (IWebElement)js.ExecuteScript("return arguments[0].shadowRoot", host));            
            string text = "Ojeny jestem w shadow DOMie";
            root.FindElement(By.CssSelector("#input")).SendKeys(text);
            root.FindElement(By.CssSelector("button")).Click();
            string displayedText = root.FindElement(By.CssSelector("#output")).Text;
            Assert.AreEqual("Wprowadzony tekst: " + text, displayedText); 
        }
    }
}
package NaRaz;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.WebDriverWait;
import java.util.concurrent.TimeUnit;
public class ShadowDOM {
    WebDriver driver;
    @BeforeEach
    public void setup() {
        System.setProperty("webdriver.chrome.driver", "src/main/resources/chromedriver.exe");
        driver = new ChromeDriver();
        driver.manage().window().setSize(new Dimension(1295, 730));
        driver.manage().window().setPosition(new Point(10, 40));
        driver.manage().timeouts().pageLoadTimeout(15, TimeUnit.SECONDS);
        driver.navigate().to("https://fakestore.testelka.pl/shadow-dom-w-selenium/");
        driver.findElement(By.cssSelector("a[class*='dismiss-link']")).click();
    }
    @AfterEach
    public void driverQuit() {
        driver.quit();
    }
    @Test
    public void ShowTextTest() {
        WebElement host = driver.findElement(By.cssSelector("#host"));
        JavascriptExecutor js = (JavascriptExecutor)driver;
        WebDriverWait wait = new WebDriverWait(driver, 5);
        WebElement root = wait.until(d -> (WebElement)js.executeScript("return arguments[0].shadowRoot", host));
        String text = "Ojeny jestem w shadow DOMie";
        root.findElement(By.cssSelector("#input")).sendKeys(text);
        root.findElement(By.cssSelector("button")).click();
        String displayedText = root.findElement(By.cssSelector("#output")).getText();
        Assertions.assertEquals("Wprowadzony tekst: " + text, displayedText);
    }
}