├── .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 | ![Open Source Love](https://badges.frapsoft.com/os/v1/open-source.svg?v=103) 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 | --------------------------------------------------------------------------------