👑

Playwright でファイルアップロードできないときの回避方法

2024/10/25に公開

はじめに

こんにちは、クラウドエース 第 2 開発部の坂本です。

最近、Playwright で E2E テストを書いていて、ファイルアップロードのテストで少し詰まりました。
具体的には、react-dropzone を使用してファイルアップロードを実装している場合に、Playwright の 公式ドキュメント に記載されている filechooser イベントを使った方法で、タイムアウトが発生しました。

そこで、本記事ではこの問題の回避方法をご紹介します。

Playwright の入門記事も弊社が出しておりますので、こちらもご覧ください。

https://zenn.dev/cloud_ace/articles/5024fa2fefcb9f

前提条件

  • @playwright/test <= 1.48.1
  • react-dropzone <= 14.2.10

詰まったところ

Playwright を使用して React で作られた Web アプリケーションのファイルアップロード処理をテストするため、公式ドキュメントを参考に以下のコードを書きました。

const fileChooserPromise = page.waitForEvent('filechooser');
const selector = "input[type="file"]";
await page.locator(selector).click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(path.join(__dirname, "test.jpg"));

コードを実行すると、ファイル選択の直前までは問題なく動いていたのですが、ファイル選択の段階で Timeout 30000ms exceeded. というエラーメッセージが表示されました。

原因

同様の事象がないか検索してみると、このような Issues が見つかりました。
これを見ると、 react-dropzone は内部で Window.showOpenFilePicker() を使用しており、 Window.showOpenFilePicker() の使用時に Playwright の filechooser イベントが発生しないことが原因のようです。

回避方法

react-dropzone では filechooser イベントが発生しないため、別の方法を取る必要があります。

まず、先ほどの Issue 内のコメントで紹介されている回避方法を試してみましたが、結果は変わりませんでした。

そこで、react-dropzone のデフォルトで挿入される input 要素を使用することにしました。

react-dropzone の デフォルトで挿入される input 要素
<input multiple="" type="file" tabindex="-1" style="display: none;">

しかし、react-dropzone では input 要素が非表示になっているため、まずはこの要素を表示させる必要があります。

Playwright では、 page.evaluate を使用することで、 対象の DOM 操作を行うことができます。
今回は page.evaluate を使用し、表示されていない input 要素を テスト時のみ display: block に変更することで、Playwright から操作できるようにします。
page.evaluate についての詳細は公式ドキュメントをご参照ください。

回避方法を反映したテストコードを以下に示します。

test.spec.ts
const fileChooserPromise = page.waitForEvent('filechooser');
const selector = "input[type="file"]";

/* DOM 操作 */
await page.evaluate(() => {
    const inputElement = document.querySelector(selector)
    if (inputElement instanceof HTMLInputElement) {
        /* 非表示の input 要素 を表示させる */
        inputElement.style.display = 'block';
    }
})

/* 表示させた input 要素 をクリックする */
await page.locator(selector).click()
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles(path.join(__dirname, "test.jpg"));

おわりに

今回はreact-dropzone を使用したアプリケーションで、 Playwright を用いてファイルアップロードの E2E テストを行う際に遭遇した問題と、その回避方法について解説しました。

現状では、 filechooser イベントが発生しないという問題に対しては、react-dropzone の内部実装に依存した回避策をとるしかありません。
しかし、Playwright は活発に開発が進められているツールなので、将来的にはよりシンプルで安定したファイルアップロードのテストの方法が提供される可能性があります。
もし、この記事で紹介した方法で問題が解決しない場合は、Playwright の公式ドキュメントIssues を参照するか、react-dropzoneIssues を確認してみてください。

この記事が、同じ問題に直面している方の助けになれば幸いです。

Discussion