如何在测试 Web 组件时使用 Cypress 通过 <slot> 进行查询

How To Query Through <slot> Using Cypress While Testing Web Components

经过多年的端到端测试全局 DOM 测试,我发现测试使用插槽的 Web 组件即使不是不可能,也是非常困难的。在我解释这个问题之前,我想说我不能改变生成的标记来改进它们的现状。

<wc-1 attributes-etc="">
  <wc-2 attributes-etc="">
    <slot>
      <wc-3 attributes-etc="">
        <slot>
       ...eventually get to an input...
         <input type="text" name="firstName" />

有大量来自某种表单生成器的嵌套 Web 组件,并且还使用了大量插槽。 web components有属性但是slots没有,所以我用web component名称来查询。

document.querSelector('wc-1')
  .shadowRoot.querySelector('wc-2')
  .shadowRoot.querySelector('slot')

// Yields <slot>...</slot>

到目前为止一切正常,Cypress 有一个我使用的 .shadow() 命令,但我在这里仅使用 devtools 进行测试以查看插槽具有的所有属性。

document.querSelector('wc-1')
  .shadowRoot.querySelector('wc-2')
  .shadowRoot.querySelector('slot')
  .shadowRoot

// Yields "null".
// I don't know how to get to the .lightDOM? of wc-2?

我尝试的任何 属性 最终都为 null 或返回值中有 0 个元素。使用其他前端工具和全局DOM,我总是可以在一个命令中cy.get('div[data-testid="the-nested-element-i-want"]').type('important words')

所以我的主要问题是:一旦 Web 组件开始堆积,人们如何测试这些东西?或者不这样做,只在 isolation/unit 测试中测试 Web 组件,因为很难查询嵌套影子 DOMs?

主要目标是最终获得 cy.get('input[name"firstName"]').type('John') 的表单输入。在我的示例中,有人可以给我链式 docuement.querySelector() 命令以到达 <wc-3> 吗?

您的 <slot> 中将没有 DOM 内容,因为没有 DOM 内容 已移动 到插槽中。

光DOM 内容在插槽中 反射,但 保持 不可见! 光线DOM.
(这就是为什么你也

来自文档:

, . ' ; .

所以要测试某些东西是否在 "in" 插槽

  • 您需要检查 lightDOM 元素的 slot=? 属性
  • 并仔细检查 <slot name=? > 是否确实存在于 shadowDOM

反之亦然

或钩入 slotchange 事件,但那不是测试

伪代码:

反之亦然;可能包含错误..它的伪代码..

function processDOMnode( node ){
  if (node.shadowRoot){

    // query shadowDOM
    let slotnames = [...node.shadowRoot.querySelectorAll("slot")].map(s=>s.name);

    // query lightDOM
    slotnames.forEach( name =>{
      let content = node.querySelectorAll(`[slot="${name}"]`);
      console.log( "slot:" , name , "content:" , content );
    });

    // maybe do something with slotnames in lightDOM that do NOT exist in shadowDOM

    // dive deeper
    this.shadowRooot.children.forEach(shadownode => processDOMnode(shadownode));
  }
}

答案涉及assignedNodes()https://developer.mozilla.org/en-US/docs/Web/API/HTMLSlotElement/assignedNodes

The assignedNodes() property of the HTMLSlotElement interface returns a sequence of the nodes assigned to this slot...

我使用它与 assignedElements() 没有区别。因此,您所要做的就是在查询到您需要的插槽后使用该方法。对于我的例子,答案是:

const wc-3 = document.querySelector('wc-1').shadowRoot
  .querySelector('wc-2').shadowRoot
  .querySelector('slot').assignedNodes()
  .map((el) => el.shadowRoot)[0]

然后你可以继续沿着链向下...我知道我只有一个未命名的插槽,所以这就是我从返回的 .map().

中获取它的原因

此问答为我指明正确方向的支持: