Test automation with polymer 2 piercing the shadow dom (/deep/ deprecated)
Asked Answered
C

2

6

Currently my org is on Polymer 1 with Selenium, Protractor and Mocha and we use the shady dom. For acceptance tests we use the /deep/ combinator to pierce the DOM. With polymer 2 the /deep/ combinator is deprecated.

My Question: When writing acceptance tests, how am I supposed to pierce the shadow DOM if the use of the /deep/ combinator is deprecated?

Catima answered 28/9, 2017 at 15:58 Comment(4)
The piercing selector is being dropped (/deep/ and >>>). There's currently no other alternative to search through the whole DOM with a single query. The current solution is to implement a custom selector taking multiple CSS segments to search each context.Unhook
I also found several explanations on how to handle this scenario. Directly from polymer2 docs - "There's no direct substitute for shadow-piercing selectors... custom CSS properties are probably the best option."Roodepoortmaraisburg
@FlorentB. Any chance you could reform this into an answer with an explanation of the work around solution in the protractor git issue?Maxama
@FlorentB I too would love to see an example of this. I appreciate the help and direction, there is not much out there regarding this issueCatima
H
0

After a lot of research over internet, I came up with one solution that worked fine for me.

Steps I followed:

  1. Using chrome developer tool find JS path.
  2. Create an array of String containing all desired shadow to traverse till target node; 3. The reusable function will return the parent element having the target element as a child.
  3. Perform the actions using the CSS path of the target element.

See the below code:

String[] shadowRootCalender = {"tickets-spa","tickets-config-page","#calendar"};
String date="2019-04-16";
FindShadowRootElement(driver, shadowRootCalender).findElement(By.cssSelector("wdat-date[slot='"+date+"']")).click();


public static WebElement FindShadowRootElement(WebDriver driver, String[] shadowRootSelector) {
    WebElement root = null;
    for(int i=0; i<shadowRootSelector.length; i++) {
        if(i==0) {
            isjQueryLoaded(driver);
            root = (WebElement)((JavascriptExecutor)driver).executeScript("return document.querySelector(arguments[1]).shadowRoot", root, shadowRootSelector[i]);
        }else {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            root = (WebElement)((JavascriptExecutor)driver).executeScript("return arguments[0].querySelector(arguments[1]).shadowRoot", root, shadowRootSelector[i]);
        }
    }
    return root;
}


public static void isjQueryLoaded(WebDriver driver) {
   // System.out.println("Waiting for ready state complete");
    (new WebDriverWait(driver, 30)).until(new ExpectedCondition<Boolean>() {
        public Boolean apply(WebDriver driver) {
            JavascriptExecutor js = (JavascriptExecutor) driver;
            String readyState = js.executeScript("return document.readyState").toString();
            //System.out.println("Ready State: " + readyState);
            return (Boolean) js.executeScript("return !!window.jQuery && window.jQuery.active == 0");
        }
    });
}       
Haileyhailfellowwellmet answered 14/4, 2019 at 12:48 Comment(0)
M
0

A simplified version of @Dilip Meghwal's answer:

public static WebElement pierceShadowRoot(String... selectors) {
    WebElement element = null;
    WebElement shadowRoot = null;
    for (String selector : selectors) {
        if (element == null) {
            element = getDriver().findElement(By.cssSelector(selector));
        } else {
            shadowRoot = (WebElement) getDriver().executeScript("return arguments[0].shadowRoot", element);
            element = (WebElement) shadowRoot.findElement(By.cssSelector(selector));
        }
    }
    return element;
}

As an example, here's how I used it to locate Google Chrome's Clear cache button, where other ways didn't work because of Polymer framework used in Chrome:

WebElement targetElement = pierceShadowRoot("body > settings-ui", "#main", "settings-basic-page",
                "#advancedPage > settings-section:nth-child(1) > settings-privacy-page", "settings-clear-browsing-data-dialog",
                "#clearBrowsingDataConfirm");
Mowery answered 15/5, 2019 at 16:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.