├── .gitignore
├── pom.xml
├── readme.md
├── src
├── main
│ └── java
│ │ └── setup
│ │ └── DriverManager.java
└── test
│ └── java
│ ├── BaseTest.java
│ ├── ShadowDomTests.java
│ ├── ShadowRootTests.java
│ └── pages
│ ├── google
│ └── DownloadPage.java
│ ├── htmlelements
│ └── HomePage.java
│ ├── seleniumplayground
│ └── ShadowDomPage.java
│ ├── theinternet
│ └── ShadowDom.java
│ └── watir
│ └── HomePage.java
└── testng.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | # IntelliJ IDEA
2 | /.idea/
3 | *.iml
4 |
5 | # Eclipse IDE
6 | /.settings/
7 | /.classpath
8 | /.project
9 |
10 | # Other output folders
11 | /target/
12 | /bin/
13 | /test-output/
14 | /logs/
15 | /reports/
16 |
17 | # Mac OSX
18 | .DS_Store
19 |
20 | #VS code
21 | /.vscode
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | io.github.mfaisalkhatri
8 | shadowdom-selenium
9 | 1.0-SNAPSHOT
10 |
11 |
12 | UTF-8
13 | 4.16.1
14 | 7.9.0
15 | 3.12.1
16 | 3.2.3
17 | 17
18 | UTF-8
19 | testng.xml
20 | -Dfile.encoding=UTF-8 -Xdebug -Xnoagent
21 |
22 |
23 |
24 |
25 |
26 | org.seleniumhq.selenium
27 | selenium-java
28 | ${selenium.java.version}
29 |
30 |
31 |
32 | org.testng
33 | testng
34 | ${testng.version}
35 | test
36 |
37 |
38 |
39 |
40 |
41 |
42 | org.apache.maven.plugins
43 | maven-compiler-plugin
44 | ${maven.compiler.version}
45 |
46 | ${java.release.version}
47 | ${maven.source.encoding}
48 | true
49 |
50 |
51 |
52 | org.apache.maven.plugins
53 | maven-surefire-plugin
54 | ${surefire-version}
55 |
56 |
57 |
58 | test
59 |
60 |
61 |
62 |
63 | false
64 |
65 |
66 | usedefaultlisteners
67 | false
68 |
69 |
70 |
71 | ${suite-xml}
72 |
73 | ${argLine}
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | ## Don't forget to give a :star: to make the project popular.
4 |
5 | ## :question: What is this Repository about?
6 |
7 | - This repo contains automation tests for Shadow DOM elements using Selenium WebDriver 4.
8 | - This repo uses Maven as build tool and TestNG testing framework to run the tests.
9 | - Google Chrome's Download Page has been used as a demo page to automate Shadow DOM elements.
10 | - [Watir.com][watirwebsite] has been used for automating the shadow DOM elements.
11 |
12 | ## Running the Tests
13 |
14 | By default, tests would be running on LambdaTest Platform.
15 | If you want to run it on your local machine, please update the browser value in `testng.xml`for test name `Shadow DOM Tests on Watir Website` to
16 | `chrome` or `firefox` and you should be able to run it in local chrome or firefox respectively.
17 |
18 | - To run the test from command line use the following command:
19 |
20 | `mvn clean install -Dusername= -DaccessKey`
21 |
22 | - To run the tests using TestNG:
23 | 1. In the Run Configuration add the LambdaTest username and access key in the VM option as follows:
24 | `-Dusername= -DaccessKey`
25 | 2. Right-click on the `testng.xml` file and select `Run '...\testng.xml'`
26 |
27 | 🧬 Need Assistance?
28 |
29 | - Discuss your queries by writing to me @ [mohammadfaisalkhatri@gmail.com][mail], or you can ping me on the following
30 | social media sites:
31 | - Twitter: [mfaisal_khatri][twitter]
32 | - LinkedIn: [Mohammad Faisal Khatri][linkedin]
33 |
34 | ## :thought_balloon: Checkout the blogs related to Testing on my [website][] and [medium-account][medium]
35 |
36 | [mail]: mohammadfaisalkhatri@gmail.com
37 |
38 | [linkedin]: https://www.linkedin.com/in/faisalkhatri/
39 |
40 | [twitter]: https://twitter.com/mfaisal_khatri
41 |
42 | [lambdawebsite]:https://www.lambdatest.com/
43 |
44 | [the-internet]: http://the-internet.herokuapp.com/
45 |
46 | [website]: https://mfaisalkhatri.github.io
47 |
48 | [medium]: https://medium.com/@iamfaisalkhatri
49 |
50 | [watirwebsite]: http://watir.com/examples/shadow_dom.html
--------------------------------------------------------------------------------
/src/main/java/setup/DriverManager.java:
--------------------------------------------------------------------------------
1 | package setup;
2 |
3 | import org.openqa.selenium.WebDriver;
4 | import org.openqa.selenium.chrome.ChromeDriver;
5 | import org.openqa.selenium.chrome.ChromeOptions;
6 | import org.openqa.selenium.firefox.FirefoxDriver;
7 | import org.openqa.selenium.firefox.FirefoxOptions;
8 | import org.openqa.selenium.remote.RemoteWebDriver;
9 |
10 | import java.net.MalformedURLException;
11 | import java.net.URL;
12 | import java.time.Duration;
13 | import java.util.HashMap;
14 |
15 | import static java.text.MessageFormat.format;
16 |
17 | public class DriverManager {
18 | private static WebDriver driver;
19 | private static final String LT_USERNAME = System.getProperty("LT_USERNAME");
20 | private static final String LT_ACCESS_TOKEN = System.getProperty("LT_ACCESSKEY");
21 | private static final String GRID_URL = "@hub.lambdatest.com/wd/hub";
22 |
23 | public static void quitDriver() {
24 | if (null != getDriver()) {
25 | getDriver().quit();
26 | }
27 | }
28 |
29 | public static void createDriver(final String browser) {
30 | if (browser.equalsIgnoreCase("chrome")) {
31 | setupChromeDriver();
32 | } else if (browser.equalsIgnoreCase("firefox")) {
33 | setupFirefoxDriver();
34 | } else if (browser.equalsIgnoreCase("remote-chrome")) {
35 | setupRemoteChromeDriver();
36 | } else {
37 | System.out.println("Browser driver is not available!");
38 | }
39 | setupBrowserTimeouts();
40 | }
41 |
42 | public static WebDriver getDriver () {
43 | return driver;
44 | }
45 |
46 | private static void setupChromeDriver () {
47 | final ChromeOptions options = new ChromeOptions ();
48 | options.addArguments ("--no-sandbox");
49 | options.addArguments ("--disable-dev-shm-usage");
50 | options.addArguments ("--window-size=1050,600");
51 | options.addArguments("--safebrowsing-disable-download-protection");
52 | driver = new ChromeDriver(options);
53 | }
54 |
55 | private static void setupFirefoxDriver () {
56 |
57 | final FirefoxOptions options = new FirefoxOptions ();
58 | options.addArguments ("--no-sandbox");
59 | options.addArguments ("--disable-dev-shm-usage");
60 | options.addArguments("--window-size=1050,600");
61 | driver = new FirefoxDriver(options);
62 | }
63 |
64 | private static void setupRemoteChromeDriver () {
65 | final ChromeOptions browserOptions = new ChromeOptions();
66 | browserOptions.setPlatformName ("Windows 10");
67 | final HashMap ltOptions = new HashMap();
68 | ltOptions.put ("username", LT_USERNAME);
69 | ltOptions.put ("accessKey", LT_ACCESS_TOKEN);
70 | ltOptions.put ("selenium_version", "4.0.0");
71 | ltOptions.put ("build", "Shadow DOM Selenium Chrome Tests");
72 | ltOptions.put ("name", "Shadow DOM Selenium Tests");
73 | ltOptions.put ("w3c", true);
74 | ltOptions.put ("plugin", "java-testNG");
75 | browserOptions.setCapability ("LT:Options", ltOptions);
76 | try {
77 | driver = new RemoteWebDriver(
78 | new URL(format("https://{0}:{1}{2}", LT_USERNAME, LT_ACCESS_TOKEN, GRID_URL)), browserOptions);
79 | } catch (final MalformedURLException e) {
80 | throw new Error(e);
81 | }
82 | }
83 |
84 | private static void setupBrowserTimeouts () {
85 | driver.manage ()
86 | .timeouts ()
87 | .implicitlyWait (Duration.ofSeconds (30));
88 | }
89 |
90 | }
91 |
--------------------------------------------------------------------------------
/src/test/java/BaseTest.java:
--------------------------------------------------------------------------------
1 |
2 | import org.testng.annotations.AfterClass;
3 | import org.testng.annotations.BeforeClass;
4 | import org.testng.annotations.Parameters;
5 |
6 | import static setup.DriverManager.createDriver;
7 | import static setup.DriverManager.quitDriver;
8 |
9 | public class BaseTest {
10 |
11 |
12 | @BeforeClass(alwaysRun = true)
13 | @Parameters({"browser"})
14 | public void setupTest (final String browser) {
15 | createDriver(browser);
16 | }
17 |
18 | @AfterClass(alwaysRun = true)
19 | public void tearDown () {
20 | quitDriver ();
21 | }
22 |
23 |
24 | }
25 |
--------------------------------------------------------------------------------
/src/test/java/ShadowDomTests.java:
--------------------------------------------------------------------------------
1 | import org.testng.annotations.Test;
2 | import pages.google.DownloadPage;
3 | import pages.seleniumplayground.ShadowDomPage;
4 | import pages.theinternet.ShadowDom;
5 | import pages.watir.HomePage;
6 |
7 | import static org.testng.Assert.assertEquals;
8 | import static setup.DriverManager.getDriver;
9 |
10 | public class ShadowDomTests extends BaseTest {
11 |
12 | @Test
13 | public void testShadowDom () {
14 | getDriver ().get ("https://the-internet.herokuapp.com/shadowdom");
15 | final ShadowDom shadowDom = new ShadowDom ();
16 | assertEquals (shadowDom.shadowTextOne (), "Let's have some different text!");
17 | System.out.println (shadowDom.shadowTextTwo ());
18 | }
19 |
20 | @Test
21 | public void testShadowDomRootOnChromeDownloadPage () {
22 | getDriver ().get ("chrome://downloads/");
23 | final DownloadPage downloadPage = new DownloadPage ();
24 | assertEquals (downloadPage.downloadPageHeaderText (), "Downloads");
25 | assertEquals (downloadPage.googleDownloadPageHeaderText (), "Downloads");
26 | assertEquals (downloadPage.pageHeaderTextUsingJScripExecutor (), "Downloads");
27 | }
28 |
29 | @Test
30 | public void testShadowDomWatir () {
31 | getDriver ().get ("http://watir.com/examples/shadow_dom.html");
32 | final HomePage homePage = new HomePage ();
33 | // assertEquals (homePage.getSomeText(), "some text");
34 | assertEquals (homePage.getShadowDomText (), "some text");
35 | assertEquals (homePage.getNestedShadowText (), "nested text");
36 | assertEquals (homePage.getNestedText (), "nested text");
37 | assertEquals (homePage.getNestedTextUsingJSExecutor (), "nested text");
38 | }
39 |
40 | @Test
41 | public void testShadowDomSeleniumPlayground() {
42 | getDriver().get("https://www.lambdatest.com/selenium-playground/shadow-dom");
43 | ShadowDomPage shadowDomPage = new ShadowDomPage();
44 | String name = "faisal";
45 | String email = "faisal.k@gmail.com";
46 | shadowDomPage.updateDetails(name, email);
47 | assertEquals(shadowDomPage.getNameText(), name);
48 | assertEquals(shadowDomPage.getEmailText(), email);
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/test/java/ShadowRootTests.java:
--------------------------------------------------------------------------------
1 | import org.testng.annotations.BeforeClass;
2 | import org.testng.annotations.Test;
3 | import pages.htmlelements.HomePage;
4 |
5 | import static org.testng.Assert.assertEquals;
6 | import static setup.DriverManager.getDriver;
7 |
8 | public class ShadowRootTests extends BaseTest {
9 |
10 | @BeforeClass
11 | public void navigateToWebsite() {
12 | getDriver().get("https://www.htmlelements.com/demos/menu/shadow-dom/index.htm");
13 | }
14 |
15 | @Test
16 | public void testFileMenuShadowRootElement() {
17 | final HomePage homePage = new HomePage();
18 | assertEquals(homePage.getFileMenuText(), "File");
19 | }
20 |
21 | @Test
22 | public void testNewMenuShadowRootElement() {
23 | final HomePage homePage = new HomePage();
24 | assertEquals(homePage.getNewMenuText(), "New");
25 | }
26 |
27 | @Test
28 | public void testEditMenuShadowRootElement() {
29 | final HomePage homePage = new HomePage();
30 | assertEquals(homePage.getEditMenuText(), "Edit");
31 | }
32 |
33 | @Test
34 | public void testUndoMenuShadowRootElement() {
35 | final HomePage homePage = new HomePage();
36 | assertEquals(homePage.getUndoMenuText(), "Undo");
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/test/java/pages/google/DownloadPage.java:
--------------------------------------------------------------------------------
1 | package pages.google;
2 |
3 | import org.openqa.selenium.By;
4 | import org.openqa.selenium.JavascriptExecutor;
5 | import org.openqa.selenium.SearchContext;
6 | import org.openqa.selenium.WebElement;
7 |
8 | import static setup.DriverManager.getDriver;
9 |
10 | public class DownloadPage {
11 |
12 | public SearchContext expandRootElement (WebElement element) {
13 | SearchContext shadowRoot = (SearchContext) ((JavascriptExecutor) getDriver ()).executeScript (
14 | "return arguments[0].shadowRoot", element);
15 | return shadowRoot;
16 | }
17 |
18 | public String pageHeaderTextUsingJScripExecutor () {
19 | WebElement downloadsManagerTag = getDriver ().findElement (By.tagName ("downloads-manager"));
20 | SearchContext shadowRootOne = expandRootElement (downloadsManagerTag);
21 | WebElement toolbar = shadowRootOne.findElement (By.id ("toolbar"));
22 | SearchContext shadowRootTwo = expandRootElement (toolbar);
23 | WebElement crToolbar = shadowRootTwo.findElement (By.cssSelector ("cr-toolbar"));
24 | SearchContext shadowRootThree = expandRootElement (crToolbar);
25 | return shadowRootThree.findElement (By.cssSelector ("#leftSpacer > h1"))
26 | .getText ();
27 |
28 | }
29 |
30 | public String downloadPageHeaderText () {
31 |
32 | WebElement downloadsManager = getDriver ().findElement (By.tagName ("downloads-manager"));
33 | SearchContext shadowRoot = downloadsManager.getShadowRoot ();
34 | WebElement toolbar = shadowRoot.findElement (By.id ("toolbar"));
35 | SearchContext shadowRootTwo = toolbar.getShadowRoot ();
36 | WebElement crToolbar = shadowRootTwo.findElement (By.cssSelector ("cr-toolbar"));
37 | SearchContext shadowRootThree = crToolbar.getShadowRoot ();
38 | WebElement downloadsHeader = shadowRootThree.findElement (By.cssSelector ("#leftSpacer > h1"));
39 | return downloadsHeader.getText ();
40 | }
41 |
42 | public String googleDownloadPageHeaderText () {
43 | WebElement pageHeader = getDriver ().findElement (By.tagName ("downloads-manager"))
44 | .getShadowRoot ()
45 | .findElement (By.id ("toolbar"))
46 | .getShadowRoot ()
47 | .findElement (By.cssSelector ("cr-toolbar"))
48 | .getShadowRoot ()
49 | .findElement (By.cssSelector ("#leftSpacer > h1"));
50 | return pageHeader.getText ();
51 | }
52 |
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/src/test/java/pages/htmlelements/HomePage.java:
--------------------------------------------------------------------------------
1 | package pages.htmlelements;
2 |
3 | import org.openqa.selenium.By;
4 | import org.openqa.selenium.JavascriptExecutor;
5 | import org.openqa.selenium.SearchContext;
6 | import org.openqa.selenium.WebElement;
7 |
8 | import static setup.DriverManager.getDriver;
9 |
10 | public class HomePage {
11 |
12 | public WebElement fileMenu() {
13 | final WebElement shadowHost = getDriver().findElement(By.cssSelector(".smart-ui-component"));
14 | final SearchContext shadowRoot = shadowHost.getShadowRoot();
15 | return shadowRoot.findElement(By.cssSelector(".smart-element .smart-menu-main-container .smart-element"));
16 | }
17 |
18 | public String getFileMenuText() {
19 | return fileMenu().getAttribute("label");
20 | }
21 |
22 | public void openFileMenu() {
23 | fileMenu().click();
24 | }
25 |
26 | public String getNewMenuText() {
27 | openFileMenu();
28 | return fileMenu().findElement(By.cssSelector(".smart-menu-drop-down div smart-menu-item.smart-element"))
29 | .getAttribute("label");
30 | }
31 |
32 | public SearchContext expandRootElement(final WebElement element) {
33 | return (SearchContext) ((JavascriptExecutor) getDriver()).executeScript(
34 | "return arguments[0].shadowRoot", element);
35 | }
36 |
37 | public WebElement editMenu() {
38 | final WebElement shadowHost = getDriver().findElement(By.cssSelector(".smart-ui-component"));
39 | final SearchContext shadowRoot = expandRootElement(shadowHost);
40 | return shadowRoot.findElement(By.cssSelector(".smart-element .smart-menu-main-container smart-menu-items-group:nth-child(2)"));
41 | }
42 |
43 | public String getEditMenuText() {
44 | return editMenu().getAttribute("label");
45 | }
46 |
47 | public void openEditMenu() {
48 | editMenu().click();
49 | }
50 |
51 | public String getUndoMenuText() {
52 | openEditMenu();
53 | return editMenu().findElement(By.cssSelector(".smart-menu-drop-down div smart-menu-item.smart-element"))
54 | .getAttribute("label");
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/test/java/pages/seleniumplayground/ShadowDomPage.java:
--------------------------------------------------------------------------------
1 | package pages.seleniumplayground;
2 |
3 | import org.openqa.selenium.By;
4 | import org.openqa.selenium.SearchContext;
5 | import org.openqa.selenium.WebElement;
6 |
7 | import static setup.DriverManager.getDriver;
8 |
9 | public class ShadowDomPage {
10 |
11 | private SearchContext getShadowRoot() {
12 | final WebElement shadowHost = getDriver().findElement(By.id("shadow_host"));
13 | return shadowHost.getShadowRoot();
14 | }
15 |
16 | private WebElement nameField() {
17 | return getShadowRoot().findElement(By.cssSelector("input[type=text][placeholder=Name]"));
18 | }
19 |
20 | private WebElement emailField() {
21 | return getShadowRoot().findElement(By.cssSelector("input[type=email][placeholder=Email]"));
22 | }
23 |
24 | public void updateDetails(String name, String email) {
25 | nameField().sendKeys(name);
26 | emailField().sendKeys(email);
27 | }
28 |
29 | public String getNameText() {
30 | return nameField().getAttribute("value");
31 | }
32 |
33 | public String getEmailText() {
34 | return emailField().getAttribute("value");
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/pages/theinternet/ShadowDom.java:
--------------------------------------------------------------------------------
1 | package pages.theinternet;
2 |
3 | import org.openqa.selenium.By;
4 | import org.openqa.selenium.SearchContext;
5 | import org.openqa.selenium.WebElement;
6 |
7 | import static setup.DriverManager.getDriver;
8 |
9 | public class ShadowDom {
10 |
11 |
12 | public String shadowTextOne() {
13 | return getDriver().findElement(By.cssSelector("#content > my-paragraph:nth-child(4) > span")).getText();
14 | }
15 |
16 | public String shadowTextTwo() {
17 | return getDriver().findElement(By.cssSelector("#content > my-paragraph:nth-child(5) > ul:nth-child(1) > li")).getText();
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/test/java/pages/watir/HomePage.java:
--------------------------------------------------------------------------------
1 | package pages.watir;
2 |
3 | import static setup.DriverManager.getDriver;
4 |
5 | import org.openqa.selenium.By;
6 | import org.openqa.selenium.JavascriptExecutor;
7 | import org.openqa.selenium.SearchContext;
8 | import org.openqa.selenium.WebElement;
9 |
10 | /**
11 | * @author Faisal Khatri
12 | * @since 8/19/2022
13 | **/
14 | public class HomePage {
15 |
16 | public SearchContext expandRootElement (final WebElement element) {
17 | return (SearchContext) ((JavascriptExecutor) getDriver ()).executeScript (
18 | "return arguments[0].shadowRoot", element);
19 | }
20 |
21 | public String getSomeText () {
22 | return getDriver ().findElement (By.cssSelector ("#shadow_content > span"))
23 | .getText ();
24 | }
25 |
26 | public String getShadowDomText () {
27 | final WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
28 | final SearchContext shadowRoot = shadowHost.getShadowRoot ();
29 | return shadowRoot.findElement (By.cssSelector ("#shadow_content > span"))
30 | .getText ();
31 | }
32 |
33 | public String getNestedShadowText () {
34 | final WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
35 | final SearchContext shadowRoot = shadowHost.getShadowRoot ();
36 | final WebElement shadowContent = shadowRoot.findElement (By.cssSelector ("#nested_shadow_host"));
37 | final SearchContext shadowRootTwo = shadowContent.getShadowRoot ();
38 | return shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div"))
39 | .getText ();
40 | }
41 |
42 | public String getNestedText () {
43 | final WebElement nestedText = getDriver ().findElement (By.id ("shadow_host"))
44 | .getShadowRoot ()
45 | .findElement (By.cssSelector ("#nested_shadow_host"))
46 | .getShadowRoot ()
47 | .findElement (By.cssSelector ("#nested_shadow_content > div"));
48 | return nestedText.getText ();
49 | }
50 |
51 | public String getNestedTextUsingJSExecutor () {
52 | final WebElement shadowHost = getDriver ().findElement (By.id ("shadow_host"));
53 | final SearchContext shadowRootOne = expandRootElement (shadowHost);
54 | final WebElement nestedShadowHost = shadowRootOne.findElement (By.cssSelector ("#nested_shadow_host"));
55 | final SearchContext shadowRootTwo = expandRootElement (nestedShadowHost);
56 | return shadowRootTwo.findElement (By.cssSelector ("#nested_shadow_content > div"))
57 | .getText ();
58 |
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/testng.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------