• Skip to primary navigation
  • Skip to main content
  • Skip to footer
  • Who we are
    • Our values
    • Our story
    • Our clients
    • Our world presence
  • What we do
    • Solutions
    • Content services platforms
      • What is CSP?
      • Solution Enablement Toolkit (SET) for CSP
      • Nuxeo systems integrator
      • Meet our Nuxeo developers
      • Content Services Platforms resources
    • Robotic process automation
      • What is RPA
      • Solution Enablement Toolkit (SET)
      • Automation center of excellence
      • Journey to intelligent automation
    • Adobe Experience Cloud
    • SharePoint services
      • Microsoft 365 automation
      • Solution Enablement Toolkit (SET) for SharePoint
    • Collaboration & Web CMS
    • Services
    • Accessibility compliance
      • Accessibility development & design
      • Accessibility testing
      • Accessibility training & learning
      • Accessibility file & document remediation
      • Accessibility resources
    • Cloud & application development
    • Data & analytics
    • Support services
  • How we do it
    • Getting started
    • Governance model
    • Project management office
    • QA Framework
    • Insights
    • Case studies
    • Events & community
  • Careers
iSoftStone – IT Services, Software Consulting, Accessibility Consulting

iSoftStone - IT Services, Software Consulting, Accessibility Consulting

A step above: We help you reach the next level in digital transformation

  • Who we are
    • Our values
    • Our story
    • Our clients
    • Our world presence
  • What we do
    • Solutions
    • Content services platforms
      • What is CSP?
      • Solution Enablement Toolkit (SET) for CSP
      • Nuxeo systems integrator
      • Meet our Nuxeo developers
      • Content Services Platforms resources
    • Robotic process automation
      • What is RPA
      • Solution Enablement Toolkit (SET)
      • Automation center of excellence
      • Journey to intelligent automation
    • Adobe Experience Cloud
    • SharePoint services
      • Microsoft 365 automation
      • Solution Enablement Toolkit (SET) for SharePoint
    • Collaboration & Web CMS
    • Services
    • Accessibility compliance
      • Accessibility development & design
      • Accessibility testing
      • Accessibility training & learning
      • Accessibility file & document remediation
      • Accessibility resources
    • Cloud & application development
    • Data & analytics
    • Support services
  • How we do it
    • Getting started
    • Governance model
    • Project management office
    • QA Framework
    • Insights
    • Case studies
    • Events & community
  • Careers

Using Selenium with Nuxeo and the Shadow DOM

Home ‣ Insights ‣ Articles ‣ Using Selenium with Nuxeo and the Shadow DOM

June 24, 2021 by Nicole Marshall

Selenium is a popular tool for user interface testing, and it can be useful for teams that are rolling out Nuxeo.  Essentially, Selenium operates Firefox or Chrome to interact with a website through the browser (rather than through API calls) based on scripts and reports on the results.  You can use it, for example, to automate some of your regression testing to make sure that certain base functionality has not been inadvertently impacted by new features and that the right user roles still have the right permissions. However, you should be aware of Nuxeo's use of the Shadow DOM when using Selenium.

 

Finding elements in the Nuxeo WebUI

Unfortunately, it can be more difficult to get Selenium to find elements in Nuxeo’s WebUI than it would be on a simple HTML web page. In fact, if you have the Selenium IDE record your clicks while you click through the Nuxeo WebUI interface, it will essentially record each click as simply a click on the Nuxeo application without registering what specific element of the application you actually clicked on.

The reason for this is that Nuxeo makes extensive use of web components and what’s known as the Shadow DOM. The Shadow DOM refers to a separate document object model (separate from the overall page’s DOM) that is scoped to an individual web part. Although the more recent versions of web browsers all support the shadow DOM, Selenium’s recorder does not, and it requires some additional knowledge when manually writing Selenium scripts as well.

 

A Shadow DOM within the Shadow DOM

First of all, let’s look at an example of the Shadow DOM in use in a vanilla instance of the Nuxeo WebUI. (For the purpose of this article and its sample scripts, we are using a locally-installed version of Nuxeo with WebUI, accessible at port 80, with the DAM add-on and out-of-the-box sample content installed. A number of different programming languages can be used with Selenium. We will be using Java, and we will also be making some use of the JavaScript Executor as well, as you’ll see below, to click elements in the Shadow DOM.)

If you open up your dev tools and examine Nuxeo’s WebUI, you will see that you very quickly get to the first Shadow DOM.

Partial screenshot from the Elements tab in Chrome dev tools, examining Nuxeo's WebUI.  The head tag is closed.  The body tag is open.  Under the body tag, the nuxeo-connection tag is closed, and the nuxeo-app tag is open.  Under the nuxeo-app tag, the shadow root tag is open.
Figure 1: In this image, we are in the inspector, and we have expanded the <body> tag and the <nuxeo-app…> tag. Right below that, we have expanded the #shadow-root.
 

So the Nuxeo application as a whole has a Shadow DOM, but the Shadow DOM in Nuxeo also contains additional Shadow DOMs within it. See the next image:

Partial screenshot from the Elements tab in Chrome dev tools, examining Nuxeo's WebUI.  A nuxeo-menu-icon tag is open.  It contains properties including a name property of defaultSearch and icon property of nuxeo colon search.  Under this element, the shadow-root tag is open.  Under that, the A tag is open, and under that, there is a paper-icon-button element that contains properties including id of button.
Figure 2: In this image, we are already within the first Shadow DOM, and we have expanded element <nuxeo-menu-icon…>, and we see that there is a #shadow-root for this element. Contained within this Shadow DOM, we see that that there is a <paper-icon-button...> element.
 

That “paper-icon-button” element is the button for Search. If we want to click that button, we need to guide Selenium through a Shadow DOM that is contained within another Shadow DOM.

 

Navigating the Shadow DOM(s) in Selenium

Typically, if we’re using Selenium WebDriver, we can find elements by their className or ID or other identifiers (as shown below), as long as those identifiers are unique.

If you open up your dev tools and examine Nuxeo’s WebUI, you will see that you very quickly get to the first Shadow DOM.

Partial screenshot showing that a developer has typed driver period findElement parentheses by period, and the IDE is providing a variety of options such as className, cssSelector, id, linkText, etc to go after the by period.
 

As of this article’s writing, this method doesn’t work for elements in the Shadow DOM. Instead, we need to use the JavascriptExecutor to run JavaScript in the Java project and get the returned HTML element.

As an example, let’s look at how to find and click on the default search button.

To get default search button, we need to first get the element’s XPath. To do this (in Chrome), open inspect mode, and select an element in the page to inspect it, right click it and copy the full XPath as shown in the image below.

Partial screenshot from the Elements tab in Chrome dev tools, examining Nuxeo's WebUI.  The developer has right-clicked on the paper-icon-button element, which was also shown in a previous screenshot, and has hovered over Copy in the menu, and over Copy full XPath in the submenu.
 

For the paper-icon-button element, its XPath is “/html/body/nuxeo-app//paper-drawer-panel/div/paper-listbox/nuxeo-menu-icon[3]//a/paper-icon-button”.

We can identify each of the shadow roots in the XPath—they are the elements before each of the double forward slashes (“//”). In this case, they are nuxeo-app and nuxeo-menu-icon.

We then need to write a small bit of JavaScript within our Java code to locate the element. To do this, we call JavascriptExecutor and executeScript as shown in the code snippet below. Within this, we use querySelector() to find each of nuxeo-app and nuxeo-menu-icon. The first one, nuxeo-app, is unique, but there are several nuxeo-menu-icon elements, so we add a filter condition (in this case “[name=’defaultSearch’])” to locate it.

We end up with the following code:

WebElement searchbutton = (WebElement)((JavascriptExecutor)driver).executeScript("return document.querySelector(\"nuxeo-app\").shadowRoot.querySelector(\"nuxeo-menu-icon[name='defaultSearch']\").shadowRoot.querySelector(\"#button\")"); 
searchbutton.click();
 

Dealing with page loads without document.readyState

The only challenge at this point is to make sure the Shadow DOM has completely loaded before we have Selenium try to find an element in the Shadow DOM. Unfortunately, waiting for document.readyState to equal “complete” may not be enough – it seems that the Shadow DOM may still be loading sometimes even after this.

You will likely need to create a function in your code to try finding an element in the Shadow DOM for a certain number of seconds before timing out (or for a certain number of times, waiting for a certain amount of time between each attempt). For example, the function below tries for ten seconds before timing out:

public WebElement jsExecute(String str){ 
    WebElement ele = null; 
    WebDriverWait wait = new WebDriverWait (driver, 10); 
    while(ele==null) { 
        wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(str)); 
        ele = (WebElement)((JavascriptExecutor)driver).executeScript(str); 
    } 
    return ele; 
}
 

With this function, you would use the following code to execute the previous example:

WebElement searchbutton = jsExecute("return document.querySelector(\"nuxeo-app\").shadowRoot.querySelector(\"nuxeo-menu-icon[name='defaultSearch']\").shadowRoot.querySelector(\"#button\")"); 
searchbutton.click();
 

A sample script

In a vanilla instance of Nuxeo with the DAM add-on and the default sample content, you could use the following script to log in using the default username and password and to search for an asset that you know exists in the sample content and to confirm that you’ve found it. (Take note that, before running the script, you’ll need to set the path of chromedriver.exe on line 42.)

//login-search button-River-open it
package demo;

import org.openqa.selenium.By; 
import org.openqa.selenium.WebDriver; 
import org.openqa.selenium.WebElement; 
import org.openqa.selenium.chrome.ChromeDriver; 
import org.openqa.selenium.support.ui.ExpectedConditions; 
import org.openqa.selenium.support.ui.WebDriverWait; 
import org.openqa.selenium.JavascriptExecutor;

import org.testng.annotations.Test; 
import org.testng.annotations.BeforeMethod; 
import org.testng.annotations.AfterMethod; 
import org.testng.annotations.BeforeClass;  
import org.testng.Assert; 
import org.testng.annotations.AfterClass;

public class TestCase01 { 
    public WebDriver driver; 
    //For first load 
    public WebElement firstjsExecute(String str){ 
        WebElement ele = null; 
        WebDriverWait wait = new WebDriverWait (driver, 20); 
        while(ele==null) { 
            wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(str)); 
            ele = (WebElement)((JavascriptExecutor)driver).executeScript(str); 
        } 
        return ele; 
    }  	 
    public WebElement jsExecute(String str){ 
        WebElement ele = null; 
        WebDriverWait wait = new WebDriverWait (driver, 10); 
        while(ele==null) { 
            wait.until(ExpectedConditions.javaScriptThrowsNoExceptions(str)); 
            ele = (WebElement)((JavascriptExecutor)driver).executeScript(str); 
        } 
        return ele; 
    }     
    @BeforeClass 
    public void beforeClass() { 
        System.setProperty("webdriver.chrome.driver","[PATH OF chromedriver.exe]"); 
        driver=new ChromeDriver(); 
        driver.manage().window().maximize();	 
    }   
    @BeforeMethod 
    public void beforeMethod() { 
        driver.get("http://localhost:8080/nuxeo"); 
        driver.findElement(By.id("username")).sendKeys("Administrator"); 
        driver.findElement(By.id("password")).sendKeys("Administrator"); 
        driver.findElement(By.name("Submit")).click(); 
        //System.out.println("login!"); 
    } 
    @Test 
    public void f() { 
        WebElement searchbutton =firstjsExecute("return document.querySelector(\"nuxeo-app\").shadowRoot.querySelector(\"nuxeo-menu-icon[name='defaultSearch']\").shadowRoot.querySelector(\"#button\")"); 
        searchbutton.click(); 
        WebElement searchinput =jsExecute("return document.querySelector(\"nuxeo-app\").shadowRoot.querySelector(\"nuxeo-search-form[name='defaultSearch']\").shadowRoot.querySelector(\"nuxeo-search-form-layout\").shadowRoot.querySelector(\"nuxeo-layout\").shadowRoot.querySelector(\"nuxeo-default-search-form\").shadowRoot.querySelector(\"nuxeo-input\").shadowRoot.querySelector(\"paper-input\").shadowRoot.querySelector(\"input\")"); 
        searchinput.sendKeys("River\n"); 
        WebElement searchresult =jsExecute("return document.querySelector(\"nuxeo-app\").shadowRoot.querySelector(\"nuxeo-search-page\").shadowRoot.querySelector(\"nuxeo-results-view\").shadowRoot.querySelector(\"nuxeo-search-results-layout\").shadowRoot.querySelector(\"nuxeo-layout\").shadowRoot.querySelector(\"nuxeo-default-search-results\").shadowRoot.querySelector(\"nuxeo-document-list-item\")"); 
        searchresult.click(); 
        Assert.assertEquals(driver.getCurrentUrl(),"http://localhost:8080/nuxeo/ui/#!/browse/default-domain/workspaces/Sample%20Content/Videos/River.mp4");	   
    } 
    @AfterMethod 
    public void afterMethod() { 
    } 
    //driver.close() is commented out below so that you have time to see the result visually.  Uncomment it to automatically close the browser at the conclusion of the test. 
    @AfterClass 
    public void afterClass() { 
        //driver.close(); 
    } 
}
 
Posted:
June 24, 2021
Reading Time:
6 minutes
Share:
Tags:
Content Services Platforms csp-nuxeo-content Nuxeo

Want more information?

iSoftStone has deep experience around Content Services Platforms. You can learn more about Nuxeo there and on Nuxeo’s own website.

Ready to start your Nuxeo journey? Or are you interested in learning about how iSoftStone can help you implement Nuxeo for your critical business needs? Please reach out!

Articles Content Services Platforms,  csp-nuxeo-content,  Nuxeo

Footer

Get in touch

  • info@isoftstone.com
  • +1 425-216-6300
CONTACT US

iSoftStone

  • North America Headquarters
    5808 Lake Washington Blvd.
    Suite 201
    Kirkland, WA 98033
    USA


    New York
    777 Westchester Avenue
    Suite 101
    White Plains, NY 10604
    USA

Our other channels

Twitter LinkedIn Instagram YouTube Channel

Navigation

Who we are
  • Our values
  • Our story
  • Our clients
  • Our world presence
What we do
  • Content services platform
  • Robotic process automation
  • Adobe Experience Cloud
  • SharePoint services
  • Collaboration & Web CMS
  • Accessibility compliance
  • Cloud & application development
  • Data & analytics
  • Support services
How we do it
  • Getting started
  • Governance model
  • Project management office
  • QA framework
  • Insights
  • Case studies
  • Events & community
Careers
Locations
Privacy
Our Social Responsibilty

Copyright © 2005–2022 iSoftStone, Inc. All rights reserved.

We are using cookies to give you the best experience on our web site.

You can find out more about which cookies we are using or switch them off in settings.

Privacy Overview
iSoftStone - IT Services, Software Consulting, Accessibility Consulting

This web site uses cookies so that we can provide you with the best user experience possible. Cookie information is stored on your computer by your browser and performs functions such as recognizing your browser when you return to our web site and helping our web team to understand which sections of the web site you find most interesting and useful.

Strictly Necessary Cookies

Strictly necessary cookies should be enabled at all times so that we can save your preferences for cookie settings.

If you disable this cookie, we will not be able to save your preferences. This means that every time you visit this web site you will need to enable or disable cookies again.

Google Analytics

This web site uses Google Analytics to collect anonymous information such as the number of visitors to the web site and the most popular pages. Keeping this cookie enabled helps us improve our web site.

Please enable strictly necessary cookies first so that we can save your preferences!

Cookie Policy

More information about this web site's use of cookies can be found in the Privacy Policy.