'Why can't I access some Salesforce DOM elements with (C#) Selenium By.CssSelector(), when I can 'see' them under their 'distant' ancestor nodes
I am attempting to write E2E UI tests over a Salesforce implementation which has Lightning Components (LWC). I am using Selenium with C# to automate the tests. I have been successful in locating my target elements using XPaths, but have read that CSS Selectors are recommended (for auto-test speed and robustness), so I'm attempting to convert my By calls from By.XPath() to By.CssSelector().
However I am seeing strange behaviour when using CSS Selectors with Salesforce, and I'm finding it impossible to access some elements in the DOM, even though I can 'see' them when I access their (distant) ancestors.
This code...
var cssSel = "body";
var element = wait.Until(ExpectedConditions.ElementExists(By.CssSelector(cssSel)));
Console.WriteLine(element.GetAttribute("outerHTML"));
gives me every element under the <body> node in the DOM. That's all good, I can see the whole DOM structure - including the elements I want to target.
I can then refine the search down to say...
var cssSel = "body>div.desktop div.slds-no-print";
and I get something like...
<div class="slds-no-print oneAppNavContainer" data-aura-rendered-by="299:0;p" data-aura-class="oneAppNavContainer" style="top: 83px;">
<one-appnav data-data-rendering-service-uid="138" data-aura-rendered-by="244:0">
<div class="slds-context-bar">
<div class="slds-context-bar__primary navLeft">
<div class="slds-context-bar__item slds-context-bar_dropdown-trigger slds-dropdown-trigger slds-dropdown-trigger--click slds-no-hover">
<div class="appLauncher slds-context-bar__icon-action" role="navigation" aria-label="App">
<one-app-launcher-header class="slds-icon-waffle_container">
<button class="slds-button slds-show" aria-haspopup="dialog">
<div class="slds-icon-waffle">
<div class="slds-r1"/>
.
.
<div class="slds-r9"/>
<span class="slds-assistive-text">App Launcher</span>
</div>
</button>
</one-app-launcher-header>
</div>
<span class="appName slds-context-bar__label-action slds-context-bar__app-name">
<span class="slds-truncate" title="Critical Indicators – Sales">Critical Indicators – Sales</span>
</span>
</div>
</div>
<one-app-nav-bar class="slds-grid slds-has-flexi-truncate" one-appnavbar_appnavbar-host="">
<span one-appnavbar_appnavbar="" class="slds-assistive-text" id="operationId-5">Press Spacebar to reorder.</span>
<span one-appnavbar_appnavbar="" class="slds-assistive-text keyboardDnd" id="dndAssistiveText-5" aria-live="assertive"/>
<nav one-appnavbar_appnavbar="" class="slds-context-bar__secondary navCenter" role="navigation" aria-label="Global">
<div one-appnavbar_appnavbar="" class="slds-grid slds-has-flexi-truncate navUL" role="list" aria-describedby="operationId-5">
<one-app-nav-bar-item-root one-appnavbar_appnavbar="" class="navItem slds-context-bar__item slds-shrink-none slds-is-active" data-id="home" data-assistive-id="operationId" aria-hidden="false" draggable="true" role="listitem">
<a class="slds-context-bar__label-action dndItem" href="/lightning/page/home" title="Home" tabindex="0" draggable="false" aria-current="page">
<span class="slds-truncate">Home</span>
</a>
</one-app-nav-bar-item-root>
.
.
<!-- hundreds of lines of elements cut out -->
.
.
</div>
</nav>
</one-app-nav-bar>
</div>
</one-appnav>
<!--render facet: 301:0;p-->
</div>
Still good - my target elements are still in that DOM structure. But if I now get the next element down, with any of these...
var cssSel = "body>div.desktop div.slds-no-print>one-appnav";
// or
var cssSel = "body>div.desktop div.slds-no-print one-appnav";
// or
var cssSel = "one-appnav";
I only get that single element back, and it doesn't contain any children...
<one-appnav data-data-rendering-service-uid="138" data-aura-rendered-by="244:0"></one-appnav>
The elements I am targeting are below this <one-appnav> element. But if I try to access them with something like...
var cssSel = "body>div.desktop div.slds-no-print>one-appnav>div.slds-context-bar";
// or
var cssSel = "body>div.desktop div.slds-no-print>one-appnav div.slds-context-bar";
// or
var cssSel = "body>div.desktop div.slds-no-print div.slds-context-bar";
I get an exception...
OpenQA.Selenium.NoSuchElementException: no such element: Unable to locate element:
{"method":"css selector",
"selector":"body>div.desktop div.slds-no-print>one-appnav>div.slds-context-bar"}
Despite the fact that I could see that element in the DOM tree previously (as shown in the example output up above).
I can also see all the target elements in the Chrome Inspector when I inspect Salesforce pages.
So, why can I see my target elements in the DOM tree when accessing their 'distant' ancestors, but not when accessing their more 'recent' ancestors? Again, I have no problems accessing all of these target elements using XPaths. So why not when using CSS Selectors?
Solution 1:[1]
So, no joy in trying to find a solution to this strange scenario. In fact, I've now come across a similar case in a different Salesforce DOM tree.
Again I can see the entire DOM tree below the higher 'distant' ancestors, but as I target further down the tree, at a certain point only the single element is available with apparently nothing accessible below it.
And again this appears to be an issue only when accessing By.CssSelector. When I use By.XPath everything works as expected - I can access any element in the DOM tree.
One interesting point here - when I compare the two 'troublesome' elements from the two DOM trees - the one in the original scenario, and the latest one I just found...
Original...
<one-appnav data-data-rendering-service-uid="138" data-aura-rendered-by="244:0"></one-appnav>
Latest...
<force-aloha-page data-data-rendering-service-uid="1102" data-aura-rendered-by="6404:0" force-alohapage_alohapage-host=""/>
The common factors of these elements are...
- non-standard html tag names used by Salesforce
- identical attributes used inside them:
data-data-rendering-service-uidanddata-aura-rendered-by
My feeling is that is has to do with the rendering/rendered attributes. I'm not sure what they do, or what their associated dynamic values point to... possibly to a javascript function? Questions which are raised here, without any response from Salesforce.
It looks like I'll simply have to revert to using By.XPath instead. Hopefully this will help someone who finds a similar scenario. Let me know if you find a solution.
EDIT:
I have since found references to later releases (Spring 2020) of Salesforce where they recommend using XPaths over CSS Selectors, which states in particular...
*Convert non-XPath locators to XPath*
LWC uses a _polyfill_ to implement shadow DOM rather than a native browser
implementation. This implementation restricts Selenium access by non-XPath
selectors, like By.cssSelector(), but still allows direct access with By.xpath().
The simplest fix is to convert your non-XPath locators to XPath.
For reference I'm using:
- Microsoft.NETCore.App (v3.1.0)
- C# (v8)
- Selenium.Support package (v4.1.0)
- Chrome and Chromedriver (v102.0.5005.61)
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 |
