为什么我不能使用 (C#) Selenium By.CssSelector() 访问某些 Salesforce DOM 元素,而我可以在其 'distant' 祖先节点下 'see' 它们

Why can't I access some Salesforce DOM elements with (C#) Selenium By.CssSelector(), when I can 'see' them under their 'distant' ancestor nodes

我正在尝试在具有 Lightning 组件 (LWC) 的 Salesforce 实施上编写 E2E UI 测试。我在 C# 中使用 Selenium 来自动化测试。我已经成功地使用 XPaths 定位了我的目标元素,但是我读到建议使用 CSS 选择器(为了自动测试速度和稳健性),所以我试图将我的 By 调用从 By.XPath()By.CssSelector().

然而,当我将 CSS 选择器与 Salesforce 一起使用时,我看到了奇怪的行为,我发现无法访问 DOM 中的某些元素,尽管我可以 'see'当我访问他们的(遥远的)祖先时他们。

此代码...

var cssSel = "body";
var element = wait.Until(ExpectedConditions.ElementExists(By.CssSelector(cssSel)));
Console.WriteLine(element.GetAttribute("outerHTML"));

给出 DOM 中 <body> 节点下的每个元素。很好,我可以看到整个 DOM 结构 - 包括我想要定位的元素。

然后我可以将搜索细化为...

var cssSel = "body>div.desktop div.slds-no-print";

我得到类似...

<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>

仍然很好 - 我的目标元素仍在 DOM 结构中。但是,如果我现在使用这些中的任何一个来获取下一个元素...

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";

我只得到那个单个元素,它不包含任何子元素...

<one-appnav data-data-rendering-service-uid="138" data-aura-rendered-by="244:0"></one-appnav>

我定位的元素在此 <one-appnav> 元素下方。但是,如果我尝试使用类似...

的方式访问它们
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";

我遇到异常...

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"}

尽管我之前可以在 DOM 树中看到该元素(如上面的示例输出所示)。

当我检查 Salesforce 页面时,我还可以在 Chrome 检查器中看到所有目标元素。

那么,为什么我在访问它们的 'distant' 祖先时可以在 DOM 树中看到我的目标元素,但在访问它们的更多 'recent' 祖先时却看不到?同样,我可以使用 XPath 访问所有这些目标元素。那么为什么不使用 CSS 选择器呢?

因此,尝试为这种奇怪的情况找到解决方案并不愉快。事实上,我现在在不同的 Salesforce DOM 树中遇到过类似的案例。

再次,我可以看到更高 'distant' 祖先下方的整个 DOM 树,但是当我瞄准树的更下方时,在某个点上只有单个元素可用,显然无法访问在它下面。

而且这似乎只有在访问 By.CssSelector 时才会出现问题。当我使用 By.XPath 时,一切都按预期工作 - 我可以访问 DOM 树中的任何元素。

这里有一个有趣的点 - 当我比较来自两棵 DOM 树的两个 'troublesome' 元素时 - 原始场景中的一个和我刚刚找到的最新一个...

原来...

<one-appnav data-data-rendering-service-uid="138" data-aura-rendered-by="244:0"></one-appnav>

最新...

<force-aloha-page data-data-rendering-service-uid="1102" data-aura-rendered-by="6404:0" force-alohapage_alohapage-host=""/>

这些元素的公因数是...

  • non-standard html Salesforce 使用的标签名称
  • 它们内部使用的相同属性:data-data-rendering-service-uiddata-aura-rendered-by

我的感觉是这与 rendering/rendered 属性有关。我不确定他们做什么,或者他们相关的动态值指向什么......可能指向 javascript 函数?提出的问题 here,Salesforce 没有任何回复。

看来我只需要恢复使用 By.XPath 即可。希望这会帮助发现类似情况的人。如果您找到解决方案,请告诉我。

编辑:

从那以后,我发现了他们推荐使用的 Salesforce 更高版本 (Spring 2020) 的参考资料 XPaths over CSS Selectors,其中特别指出...

*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.
作为参考,我正在使用:
- Microsoft.NETCore.App (v3.1.0)
- C# (v8)
- Selenium.Support package (v4.1.0)
- Chrome and Chromedriver (v102.0.5005.61)