如何在 javascript 中使用 jest 测试 Web 组件中的开槽元素(无框架)

Ich*_*Ich 5 javascript web-component shadow-dom jestjs native-web-component

我想测试我的自定义组件之一中插槽的内容。\n如果我在 html 文件中使用我的组件并在浏览器中打开它,一切都会按预期运行。但是,如果我想用玩笑来自动化测试,它就会失败。下面是一个输出形式为 jest 的最小工作示例:

\n
\n

占位符.js:

\n
const template = document.createElement("template");\ntemplate.innerHTML = `\n    <p>\n        <slot></slot>\n    </p>\n`;\n\n\nclass Placeholder extends HTMLElement {\n    constructor() {\n        super();\n\n        this.attachShadow({ mode: "open" });\n        this.shadowRoot.appendChild(template.content.cloneNode(true));\n    }\n\n    get name() {\n        return this.shadowRoot.querySelector("slot").innerText;\n    }\n}\n\nwindow.customElements.define("place-holder", Placeholder);\n\nexport default Placeholder;\n
Run Code Online (Sandbox Code Playgroud)\n
\n

占位符.test.js:

\n
import Placeholder from "../src/placeholder.js";\n\ndescribe("name is 'Lorem Ipsum'", () => {\n    let ph;\n    \n    beforeAll(() => {\n        ph = new Placeholder();\n        const textNode = document.createTextNode("Lorem Ipsum");\n        ph.appendChild(textNode);\n    });\n\n    test("if the name is 'Lorem Ipsum'", () => {\n        expect(ph.name).toBe("Lorem Ipsum");\n    });\n});\n
Run Code Online (Sandbox Code Playgroud)\n
\n

输出:

\n
name is 'Lorem Ipsum' \xe2\x80\xba if the name is 'Lorem Ipsum'\n\nexpect(received).toBe(expected) // Object.is equality\n\nExpected: "Lorem Ipsum"\nReceived: undefined\n\n  11 |\n  12 |     test("if the name is 'Lorem Ipsum'", () => {\n> 13 |         expect(ph.name).toBe("Lorem Ipsum");\n     |                         ^\n  14 |     });\n  15 | });\n\n  at Object.<anonymous> (test/placeholder.test.js:13:25)\n  at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)\n  at runJest (node_modules/@jest/core/build/runJest.js:387:19)\n  at _run10000 (node_modules/@jest/core/build/cli/index.js:408:7)\n  at runCLI (node_modules/@jest/core/build/cli/index.js:261:3)\n
Run Code Online (Sandbox Code Playgroud)\n
\n

正如您所看到的,jest 不知何故无法获取插入的文本并返回undefined。我怎么解决这个问题?

\n

Emi*_*ier 6

文本节点不会成为<slot>元素内部的一部分。它只是文本节点的包装。要获取放置在槽内的节点,您必须使用HTMLSlotElement.assignedNodes()方法。

HTMLSlotElement 接口的 allocateNodes() 方法返回分配给该槽的节点序列。

这样,您就可以获得驻留在槽中的节点数组。添加的文本节点将位于该数组中。

我已经修改了您的namegetter,以从分配的节点数组中获取第一个节点并返回textContent该节点的值。

const template = document.createElement("template");
template.innerHTML = `
  <p>
    <slot></slot>
  </p>
`;


class Placeholder extends HTMLElement {
  constructor() {
    super();

    this.attachShadow({
      mode: "open"
    });
    
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
  
  get name() {
    const slot = this.shadowRoot.querySelector("slot");
    const [name] = slot.assignedNodes();
    
    if (!name) {
      return ''
    }
    
    return name.textContent
  }
  
  connectedCallback() {
    console.log(this.name)
  }
}

window.customElements.define("place-holder", Placeholder);
Run Code Online (Sandbox Code Playgroud)
<place-holder>Hello</place-holder>
Run Code Online (Sandbox Code Playgroud)

旁注:每当您在模板的槽内添加文本作为占位符时,该<slot>元素都会有。innerText

class ExampleElement extends HTMLElement {
  constructor() {
    super();
    
    this.attachShadow({ mode: "open" });
    this.shadowRoot.innerHTML = `
      <slot>Placeholder text</slot>
    `;
  }
  
  get placeholder() {
    const slot = this.shadowRoot.querySelector('slot');
    return slot.innerText;
  }
  
  connectedCallback() {
    console.log(this.placeholder)
  }
}

customElements.define('example-element', ExampleElement);
Run Code Online (Sandbox Code Playgroud)
<example-element></example-element>
Run Code Online (Sandbox Code Playgroud)