'Protractor: Unable select input element inside a shadow DOM (Polymer) using by.deepCss('input')

Environment: Angular (v5 Application with Polymer Web Components. Protractor for running e2e tests.

Angular CLI: 1.6.4
Node: 6.10.0
Angular: 5.2.0
@angular/cli: 1.6.4
typescript: 2.5.3

Below given is my polymer web component shadow root expanded in chrome. You could see input type = "text" inside this custom element.

I am unable to access input element inside custom polymer component using protractor by.deepCss.

var polymerFirstName = element(by.className('polyFName'));

var inputs = polymerFirstName.element(by.deepCss('input')); // returns nothing.

enter image description here

I need to access the inner input element so that I can perform UI Automation tasks like.

element(by.deepCss('input')).clear();

element(by.deepCss('input')).sendKeys('Ritchie');

If I try to invoke .clear or .sendKeys directly on Polymer components it will fail with "Failed: invalid element state: Element must be user-editable in order to clear it". Basically I cannot call .clear or .sendKeys on to custom input element created using Polymer.

How can I access inner input element inside shadow DOM from a protractor test?

Thanks

Basanth



Solution 1:[1]

As a quick fix you might want to use something like this:

async function findShadowDomElement(shadowHostSelector, shadowElementSelector): Promise<WebElement> {
  let shadowHost = browser.findElement(by.css(shadowHostSelector));
  let shadowRoot: any = await browser.executeScript("return arguments[0].shadowRoot", shadowHost);
  return shadowRoot.findElement(by.css(shadowElementSelector));
}

Use can use this function like this:

let e: WebElement = await findShadowDomElement('ion-toast', '.toast-button');

Note that you now have a WebElement not an ElementFinder! So you have to use WebElement functions:

expect<any>(await e.isDisplayed()).toBeTruthy();
await e.click();

Solution 2:[2]

With the help of the node module "query-selector-shadow-dom", we can identify the Web elements in the Shadow DOM with ease. No need to traverse through all the Shadow roots. Using element(by.shadowDomCss('')), we can point to the Web element inside the Shadow Root at any level.

Solution 3:[3]

you can you : async expandUsingQuerySelector(sRoot,sElement){ var script="document.querySelector('"+sRoot+"').shadowRoot.querySelector('"+sElement+"')"; try{ return await browser.executeScript("return "+script ); } catch( error ) { throw error; } }

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Linus
Solution 2 Alagappan Annamalai
Solution 3 Raju Kshetri