Q:

Can I wait for a subelement appear in selenium-webdriver

I want to locate a deep nested element with selenium-webdiver.js
How can I make sure, a specific element (in this special case lazy loaded in a shadow dom) is already present?
I found this:
driver.wait(until.elementLocated(By.id('foo')), 10000);

But this searches in the whole DOM.
I'm in shadow DOM, and have the expanded the shadow tree as a new WebElement, using this function:
const expandShadowElement = function(driver, element) {
  return driver.executeScript('return arguments[0].shadowRoot', 
  element);
};
const elShadowContent = expandShadowElement(driver, elShadowRoot);

So I think, I would need something, starting it's search (and wait) as subelement of elShadowContent.

A:

I came up with the following solution:
const defaultTimeout = 30000;

const waitForElement = function(css, startElem = driver){
  return driver.wait( () =>
    startElem.findElements(By.css(css))
      .then(elements => {
        if(elements.length === 0){
          return false; // element not found
        } else {
          return elements[0].isDisplayed()
            .then( displayed => displayed ? elements[0] : false);
        }
      })
    , defaultTimeout);
};

const expandShadowElement = function(css) {
  const element = waitForElement(css);
  return driver.executeScript('return arguments[0].shadowRoot',
    element);
};

driver.get(someUrl)
  .then(() => expandShadowElement('my-shadow-container'))
  .then(expanded => waitForElement("[name='input_1']", expanded))
  .then(el => el.getAttribute('value'))
  .then(val => console.log(val));

This example logs a controls value (selector is the name attribute) inside a shadow dom container element 'my-shadow-container'.

