QR Send

スマホとPCのChrome間で、URLやテキストをQRコード1回のペアリングで送り合える自作Chrome拡張。アカウント不要・無料。

使ってみる →

Playwright 1.60のlocator.drop()でドラッグ&ドロップUIをテストする:setInputFiles()との使い分け

テスト自動化

ファイルを選べるのに、ドラッグすると壊れる

ファイルアップロードのテストで、私はしばらくsetInputFiles()だけを使っていました。隠し<input type="file">にファイルを渡せば、アップロード処理そのものは確認できます。

ところが、実際の画面には「ここにファイルをドロップ」と書かれた領域しかなく、ドラッグ中の見た目やドロップ時のイベント処理が重要なことがあります。inputに直接ファイルを入れるテストだけでは、dragenter、dragover、dropを処理するUIの穴を見逃します。

locator.drop()は外部からのドロップを再現する

Playwright 1.60でLocator.drop()が追加されました。公式APIでは、ファイルやクリップボードのようなデータを、外部からロケーターへドロップする操作をシミュレートすると説明されています。

まず、ドロップ領域にテスト用のファイルを落とす最小例です。

import { test, expect } from '@playwright/test';

test('ドロップ領域にファイルを置ける', async ({ page }) => {
  await page.goto('/upload');

  await page.getByTestId('dropzone').drop({
    files: {
      name: 'note.txt',
      mimeType: 'text/plain',
      buffer: Buffer.from('hello'),
    },
  });

  await expect(page.getByText('note.txt')).toBeVisible();
});

実ファイルを用意しなくても、名前・MIMEタイプ・バッファをテスト内で指定できます。テストデータの準備が、テストケースの近くに置けるのも扱いやすいところです。

URLやテキストのドロップもできる

drop()はファイル専用ではありません。リンクを受け取るドロップ領域なら、クリップボードに近いデータとしてテキストやURLを渡せます。

await page.getByTestId('link-dropzone').drop({
  data: {
    'text/plain': 'QR Send',
    'text/uri-list': 'https://qrsend.app/',
  },
});

ここで大切なのは、アプリ側がどの形式のデータを読むかを先に確認することです。テスト側で勝手に形式を増やしても、実際のブラウザ操作と同じになるとは限りません。

setInputFiles()との違い

両者は似て見えますが、確認する層が違います。

  • setInputFiles():ファイル選択欄にファイルが渡された後のアップロード処理を確認する。
  • locator.drop():外部からドロップされた時のイベントや、ドロップ領域のUIを確認する。

隠しinputをクリックする設計なら、まずsetInputFiles()で十分です。ドラッグ中にクラスを変える、ドロップ時にDataTransferを読む、ファイル以外のURLも受け取る、といった実装があるならdrop()を使う価値があります。

逆に、すべてのアップロードテストをdrop()へ置き換える必要はありません。入力処理のテストまで毎回ドラッグ操作にすると、テストの意図がぼやけます。UIイベントを確認するケースと、アップロード後の処理を確認するケースを分ける方が読みやすくなります。

drop()が失敗するときに見る場所

公式APIの注意点として、対象のdragoverリスナーがpreventDefault()を呼ばない場合、ドロップを拒否したと見なされます。その場合、Playwrightはdragleaveを発火してエラーになります。

これはテストの不具合ではなく、ブラウザのドラッグ&ドロップ契約にアプリが従っているかを教えてくれる失敗かもしれません。次のような実装なら、ドロップを受け入れる意図がコードから分かります。

dropzone.addEventListener('dragover', (event) => {
  event.preventDefault();
});

dropzone.addEventListener('drop', (event) => {
  event.preventDefault();
  const files = event.dataTransfer?.files;
  // filesを検証してアップロード処理へ渡す
});

ただし、アプリ側で本当にドロップを受け付ける設計なのにpreventDefault()が無いなら、テストを弱めるのではなく実装を直す方が安全です。

このテストで確認できないこと

drop()は便利ですが、実物のマウスやOSのファイルマネージャーを操作するテストではありません。ブラウザが受け取るイベントとデータを再現するため、OSごとのマウス挙動、ファイルマネージャーからの貼り付け、巨大ファイルの転送速度までは確認できません。

また、アプリが独自のドラッグ処理を実装している場合、テストで渡したデータ形式が実際のユーザー操作と一致しているかを確認してください。テストが通ったことだけで、すべてのドラッグ&ドロップ操作が保証されたとは言えません。

まとめ

Playwright 1.60のlocator.drop()は、ファイルを直接選択するためのAPIではなく、ドロップ領域へ外部データを渡すためのAPIです。

  • アップロード後の処理を短く確認するならsetInputFiles()。
  • ドラッグ&ドロップのイベント処理やUIを確認するならlocator.drop()。
  • ドロップ拒否時はdragoverのpreventDefault()を確認する。

「ファイルを受け取れたか」だけでなく、「ユーザーが実際に使う入口を通ったか」までテストしたいときに、drop()を1ケース追加するのがちょうどよい使い方だと思います。詳しい引数や対応形式はPlaywright公式APIで確認できます。

コメント

タイトルとURLをコピーしました