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

How to test slotted elements in web components with jest in javascript (no Framework)

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


placeholder.js:

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() {
        return this.shadowRoot.querySelector("slot").innerText;
    }
}

window.customElements.define("place-holder", Placeholder);

export default Placeholder;

placeholder.test.js:

import Placeholder from "../src/placeholder.js";

describe("name is 'Lorem Ipsum'", () => {
    let ph;
    
    beforeAll(() => {
        ph = new Placeholder();
        const textNode = document.createTextNode("Lorem Ipsum");
        ph.appendChild(textNode);
    });

    test("if the name is 'Lorem Ipsum'", () => {
        expect(ph.name).toBe("Lorem Ipsum");
    });
});

输出:

name is 'Lorem Ipsum' › if the name is 'Lorem Ipsum'

expect(received).toBe(expected) // Object.is equality

Expected: "Lorem Ipsum"
Received: undefined

  11 |
  12 |     test("if the name is 'Lorem Ipsum'", () => {
> 13 |         expect(ph.name).toBe("Lorem Ipsum");
     |                         ^
  14 |     });
  15 | });

  at Object.<anonymous> (test/placeholder.test.js:13:25)
  at TestScheduler.scheduleTests (node_modules/@jest/core/build/TestScheduler.js:333:13)
  at runJest (node_modules/@jest/core/build/runJest.js:387:19)
  at _run10000 (node_modules/@jest/core/build/cli/index.js:408:7)
  at runCLI (node_modules/@jest/core/build/cli/index.js:261:3)

如您所见,jest 以某种方式无法获取开槽文本和 returns undefined。我该如何解决这个问题?

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

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

有了这个,你就得到了一个驻留在插槽中的节点数组。添加的文本节点将在此数组中。

我修改了你的 name getter 以从分配的节点数组中获取第一个节点和 return 节点的 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);
<place-holder>Hello</place-holder>

旁注:只要您在模板中的槽内添加文本作为占位符,<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);
<example-element></example-element>