PlaywrightでE2Eテストを書いているとき、時々Locator#dragToが上手く動作しない事がある。確実な発生条件はまだ分かっていないが、要素のvisibleやdraggableを確認していてもダメで、ドラッグ動作のイベントハンドラーが正常に動作していないようではある。
テスト対象は、例えばこんな感じのコード。(※React風の疑似コード。雰囲気が伝われば)
<div class="app">
<div id="c1" class="container">
<div draggable="true" class="item">
...
</div>
<div draggable="true" class="item">
...
</div>
</div>
<div id="c2" class="container" onDragEnd={ (e) => {... }}>
</div>
</div>
ここでclass="item"
のdiv
要素をid="c2", class="container"
のdiv
要素にドラッグするという動作を、下記のようにPlaywrightで動作させたところ、onDragEndなどドラッグアンドドロップ関連のイベントハンドラーが正常に動作しない事があった。
const source = page.locator('.item', { hasText: "some text"})
await expect(source).toBeVisible()
expect(await source.getAttribute('draggable')).toBe('true')
const dest = page.locator('#c2')
await expect(dest).toBeVisible()
await source.dragTo(dest) // ここが何故か期待通りに動作しない
色々と考えた結果、mouseオブジェクトのmove, down, upでドラッグアンドドロップを行わせることで対応した。そのためには各要素の座標が必要だが、これはLocator#evaluateで取得できるようだ。なお、ここでは要素の中心座標を取得している。
const centerCoordinatesSource = await source.evaluate(element => {
const rect = element.getBoundingClientRect();
return {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
};
});
const centerCoordinatesDest = await dest.evaluate(element => {
const rect = element.getBoundingClientRect();
return {
x: rect.left + rect.width / 2,
y: rect.top + rect.height / 2
};
});
await page.mouse.move(centerCoordinatesSource.x, centerCoordinatesSource.y)
await page.mouse.down()
await page.waitForTimeout(200)
await page.mouse.move(centerCoordinatesDest.x, centerCoordinatesDest.y, { steps: 30 }) // steps未指定の場合、一瞬で目標座標に到達するので期待したイベントハンドラーの動作にならない事がある。
await page.waitForTimeout(200)
await page.mouse.up()
上記のコメントに書いているようにドラッグアンドドロップがあまりに早く終わるとイベントハンドラーが上手く動作しない事があるのだが、dragToが上手く動作しない事があるのはそれが関係しているのかもしれない。PlaywrightのIssueにstepsをdragToに追加してくれという要望があったのだが、この要望は却下されている。
なのでこの問題については、上記のような実装で対応した。