Thursday, September 21Be an Automation Engineer

Working with Shadow DOM elements using protractor

What is shadow DOM?

Shadow DOM is nothing but a sub tree of the actual DOM structure which will not effect other areas of the webpage. That means shadow DOM will have separate CSS/Javascript to serve it’s purpose.

Lets see a small element which used shadow dom.

<input id="foo" type="range">

Lets insert the above code to any WebKit-powered browser. It will appear as below.
slider-image

When you observe the above slider, it has two elements, one is slider track and the other one is movable circle. Thats means in single input tag the browser is able to provider two elements.

What browser might have done is just populated two elements with <div>s and <span>s and make them move with javascript. They have handled it in a Shadow DOM so that the stylings and Javascript fuctions of the slider does not impact other elements of the page.

You can also look at the Downlaods page of Chrome browser. It has lot of Shadow roots.
shadow-dom

Why shadow DOM is a special case in Selenium based automation tools?

By default in selenium can not identify elements under shadow tree. It will throw NoSuchElementException. We should execute a Javascript to bring shadow tree to visibility of the selenium and then we need to identify elements under it.
Selenium Java code to expand shadow root:

public WebElement expandShadowRoot(WebElement shadowRootElement){
WebElement shdowTreeParent = (WebElement) ((JavascriptExecutor)driver)
.executeScript("return arguments[0].shadowRoot", shadowRootElement);
    return shdowTreeParent;
}

How to handle in Protractor?

As protractor is also a selenium based automation tool, it should also do the same to interact with Shadow DOM elements. But protractor has provided an inbuilt element locating strategy to deal with shadow dom elements.

by.deepCss

by.deepCss can identify elements in shadow DoM. lets execute a small Example.

config.js


exports.config = {
    seleniumAddress: 'http://localhost:4444/wd/hub',
    capabilities: {
        'browserName': 'chrome'
    },
    framework: 'jasmine',
    specs: ['./ShadowDOMExample.js'],
    jasmineNodeOpts: {
        defaultTimeoutInterval: 30000
    },
    onPrepare: function () {
        browser.manage().window().maximize();
        browser.manage().timeouts().implicitlyWait(5000);
    }
};

ShadowDOMExample.js
describe('angularjs homepage todo list', function () {
    it('should add a todo', function () {
      browser.ignoreSynchronization = true;
      //Opening downloads page in chrome
      browser.get('chrome://downloads/');
      browser.wait(function(){
        return element(by.deepCss('div[id=leftContent]>h1')).isPresent();
      },20000);
      var heading = element(by.deepCss('div[id=leftContent]>h1')).getText();
      expect(heading).toBe('Downloads');
    });
});

Chrome’s downloads page is not developed in angular js. So we need to set browser.ignoreSynchronization to “true” to inform protractor that the current page is not an AngularJs page.

Lets Run:

Make sure you have started webdriver-manager.

webdriver-manager start

Run protractor in a new terminal:

protractor config.js

%d bloggers like this: