-
-
Save diervo/7ce4437bde4a382679b22306af9b5b6c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# WebDriver + Shadow DOM traversal code samples | |
## Page Object Shadow DOM traversal methods | |
Accepts a list of CSS selectors and iterates over them, querying off of the shadowRoot after each new element is found: | |
``` | |
public WebElement findElementInShadowRoot(WebElement element, By... selectors) { | |
WebElement curr = expandShadowIfPresent(element); | |
for (int i = 0; i < selectors.length; i++) { | |
curr = curr.findElement(selectors[i]); | |
if (i != selectors.length -1) { | |
curr = expandShadowIfPresent(curr); | |
} | |
} | |
return curr; | |
} | |
``` | |
To determine if the element has a shadowRoot or not we need to execute Javascript on the client since there is no native Selenium support: | |
``` | |
public WebElement expandShadowIfPresent(WebElement element) { | |
String checkShadowRoot = | |
"var shadowRoot = arguments[0].shadowRoot;" | |
+ "if (shadowRoot && shadowRoot.nodeType === 11 && !!shadowRoot.host) {" | |
+ " return true;" | |
+ "}" | |
+ "return false;"; | |
Boolean shadowRoot = (Boolean)((JavascriptExecutor)getCurrentWebDriver()) | |
.executeScript(checkShadowRoot, element); | |
boolean hasShadowRoot = shadowRoot.booleanValue(); | |
return hasShadowRoot ? new ShadowRootWebElement(element) : element; | |
} | |
``` | |
If we know the element has a shadowRoot, save the overhead of executing a script on the client: | |
``` | |
public ShadowRootWebElement expandShadow(WebElement element) { | |
return new ShadowRootWebElement(element); | |
} | |
``` | |
Create our own WebElement object that represents a DOM elements shadowRoot. This saves a reference to the parent so when the WebDriver `findElement` API is called we drop down to the client to do a Javascript query off of the `shadowRoot`. This is a big reason why only CSS selectors are supported since they can almost directly translate to a `querySelector` call. | |
``` | |
public class ShadowRootWebElement implements WebElement, WrapsElement, WrapsDriver { | |
// The host element of the shadowRoot. Needed to execute queries off of. | |
private WebElement rootElement; | |
public ShadowRootWebElement(WebElement root) { | |
this.rootElement = root; | |
WrapsDriver parentDriver = (WrapsDriver)rootElement; | |
this.executor = (JavascriptExecutor)parentDriver.getWrappedDriver(); | |
} | |
// ... lots of other code emitted ... | |
public WebElement findElement(By by) { | |
String byString = by.toString(); | |
if (!byString.startsWith("By.cssSelector")) { | |
throw new InvalidArgumentException("Must search for subelements of a | |
shadowRoot element with By.ByCssSelector. Instead got: " + byString); | |
} | |
String queryString = byString.trim().replace("By.cssSelector: ", ""); | |
WebElement ret = (WebElement)executor | |
.executeScript("return arguments[0].shadowRoot.querySelector('" | |
+ escapeForQuery(queryString) + "')", rootElement); | |
if (ret == null) { | |
throw new NoSuchElementException("Unable to locate element: " + byString); | |
} | |
return ret; | |
} | |
private String escapeForQuery(String queryString) { | |
return queryString.replace("\\", "\\\\").replace("'", "\\'") | |
.replace("\"", "\\\""); | |
} | |
} | |
``` | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment