Stencilでselectを変更したイベントを発火するテストの書き方
Web Components開発ライブラリであるStencilは、他のJavaScriptフレームワークと同様にデフォルトでユニットテスト・E2Eテストの用意があります。
けれど、Web ComponentsでE2Eテストをしようと思うと、ShadowDOMへのアクセスなどでハマることも多く、今回、selectを変更してイベントを発火する方法について悩んだので共有しておきます。
テストするコンポーネント
コンポーネントは、Web Component内に select
DOMがある以下のような形だとします。ここの onChange
をE2Eテスト内で実行させて、 myChange
イベントを発火するようにします。コンポーネント名称は my-select
としましょう。
@Component({
tag: 'my-select',
styleUrl: 'my-select.scss',
shadow: true,
})
export class MySelect {
...
private onChange = event => {
this.myChange.emit({ value: event.target.value });
};
render() {
return (
<Host>
<select onChange={this.onChange}>
{options.map(option => (
<option value={option.value}>{option.label}</option>
))}
</select>
</Host>
);
}
}
テストコード
テストを書く上で注意しないといけないのは、 Puppeteer
の select
メソッドがShadowDOMで利用できないことです。
そのため、 Puppeteer
の evaluateHandle
を利用して、windowオブジェクト、documentオブジェクトにアクセスしながら、DOMの操作を行います。
そうするとテストは以下のようになります。
describe('my-select', () => {
it('change', async () => {
const page = await newE2EPage();
await page.setContent(`<my-select></my-select>`,);
await page.waitForChanges();
const change = await page.spyOnEvent('myChange');
await page.evaluateHandle(() => {
return new Promise(resolve => {
// my-select DOMを取得
const element = document.querySelector('my-select');
// myChangeイベントが発火したら非同期処理を解決
element.addEventListener('myChange', event => {
resolve();
});
// my-select DOM内の select DOMを取得
const select: HTMLSelectElement = element.shadowRoot.querySelector(
'select',
);
// select DOMにイベントをdispatch
select.dispatchEvent(
new Event('change', { bubbles: true, composed: true }),
);
});
});
await page.waitForChanges();
expect(change).toHaveReceivedEvent();
});
});
page.spyOnEvent('myChange')
でイベント myChange
が発火したかをチェックするモックオブジェクトをつくって、変数 change
に格納します。このあと、そしてイベントを発火させた場合、 expect(change).toHaveReceivedEvent()
のテストが通ります。(※イベントが発火していない場合は toHaveReceivedEvent
していないためテストは失敗します)
Puppeteer
の select
メソッドが使えないため、 dispatchEvent
をつかって強制的に onChange
イベントを発火させています。そうすると、 addEventListener
で監視していた myChange
が発火するため、 evaluateHandle
内のPromiseが解決し、後続の処理が行われます。
ShadowDOMという仕様上ちょっと手間取りましたが簡単にテストが書けました。ここでは発火したことしか確認していませんが toHaveReceivedEventDetail
メソッドを利用すると、イベントの中身もテストすることができます。
それではまた。
Discussion