Skip to content

Shadow DOM のテスト

Shadow DOM 内部の要素のテストを行うには、通常の DOM と異なる注意点があります。Shadow DOM は外部から直接アクセスできないため、テストには特別な方法が必要です。

1️⃣ 基本的なテスト方法

以下の例では、Shadow DOM 内の要素を正しく取得し、値を検証しています。

sample-component.ts

typescript
export class SampleComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot!.innerHTML = `
      <p id="message">Hello, Shadow DOM!</p>
    `;
  }
}
customElements.define('sample-component', SampleComponent);

sample-component.test.ts

typescript
import { describe, it, expect, beforeEach } from 'vitest';
import type { SampleComponent } from './sample-component';

describe('SampleComponent', () => {
  let element: SampleComponent;

  beforeEach(() => {
    document.body.innerHTML = `<sample-component></sample-component>`;
    element = document.querySelector('sample-component') as SampleComponent;
  });

  it('Shadow DOM 内の要素が正しく取得できること', () => {
    const p = element.shadowRoot!.querySelector('#message');
    expect(p?.textContent).toBe('Hello, Shadow DOM!');
  });
});

2️⃣ Shadow DOM のスコープ検証

Shadow DOM 内のスタイルやスコープもテストできます。例えば、スタイルの適用が正しいかどうかを確認します。

styled-component.ts

typescript
export class StyledComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot!.innerHTML = `
      <style>
        p {
          color: red;
        }
      </style>
      <p id="styled-message">Styled Text</p>
    `;
  }
}
customElements.define('styled-component', StyledComponent);

styled-component.test.ts

typescript
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { StyledComponent } from './styled-component';

// カスタム要素が登録済みかをチェック
if (!customElements.get('styled-component')) {
  customElements.define('styled-component', StyledComponent);
}

describe('StyledComponent', () => {
  let element: StyledComponent;

  beforeEach(() => {
    // 直接インスタンス化する
    element = new StyledComponent();
    document.body.appendChild(element);
    
    // コンポーネントの初期化を待つ
    return new Promise(resolve => setTimeout(resolve, 0));
  });

  it('Shadow DOMが存在すること', () => {
    expect(element.shadowRoot).not.toBeNull();
  });

  it('Shadow DOM内の要素が正しいこと', () => {
    const p = element.shadowRoot?.querySelector('#styled-message');
    expect(p).not.toBeNull();
    expect(p?.textContent).toBe('Styled Text');
  });

  // スタイルについては直接検証が難しいので、構造のみ検証
  it('スタイル要素が存在すること', () => {
    const style = element.shadowRoot?.querySelector('style');
    expect(style).not.toBeNull();
  });

  afterEach(() => {
    document.body.removeChild(element);
  });
});

3️⃣ Slots のテスト

Shadow DOM 内に <slot> を使ってスロット化された要素も検証できます。

slotted-component.ts

typescript
export class SlottedComponent extends HTMLElement {
  constructor() {
    super();
    this.attachShadow({ mode: 'open' });
    this.shadowRoot!.innerHTML = `
      <slot></slot>
    `;
  }
}
customElements.define('slotted-component', SlottedComponent);

slotted-component.test.ts

typescript
import { describe, it, expect, beforeEach } from 'vitest';
import './slotted-component';

describe('SlottedComponent', () => {
  let element: SlottedComponent;

  beforeEach(() => {
    document.body.innerHTML = `<slotted-component><p>Slot Content</p></slotted-component>`;
    element = document.querySelector('slotted-component') as SlottedComponent;
  });

  it('スロット内の要素が正しく表示されること', () => {
    const slotContent = element.querySelector('p');
    expect(slotContent?.textContent).toBe('Slot Content');
  });
});

✅ まとめ

  • Shadow DOM 内の要素は通常の DOM 操作とは異なり、shadowRoot を経由してアクセス。
  • Scoped CSS の検証も可能。
  • Slots の内容もテスト対象に含められる。

参考:

Released under the CC-BY-4.0 license.