Playwrightでライブプレビューツールを実装する

こんにちは、yebis0942です。

先日、@basemachina/bm-view-previewというツールをnpmで公開しました。ベースマキナのビュー機能ソースコードをローカル環境で編集しながら、Next.jsのnpm run devのようにライブプレビューで動作を確認できるツールです。

ライブプレビューはPlaywrightによってコントロールされたChromiumで表示しています。PlaywrightをE2Eテストや自動化以外の用途で使うのは珍しい事例かと思いますので、その背景と実装の裏側についてご紹介します。

ビュー機能とは

ベースマキナではビュー機能というReactベースの管理画面作成機能を提供しています。ベースマキナのデフォルトの画面では不十分な場合に、Reactによって柔軟に画面を実装することができます。専用の関数を実行することで、ベースマキナのアクション実行やジョブ実行などの機能を呼び出すことができます。

編集画面では、以下のようにコードエディタとプレビューエリアを用意しています。コードエディタにReactのコードを入力すると、プレビューエリアが更新されます。

ビュー機能の編集画面

ローカル環境で開発したい

ビュー機能でまとまった画面を開発する事例が増えるにつれて、ローカル環境で開発を行いたいというご要望をいただくようになりました。

具体的には

  • 使い慣れたエディタでコードを書きたい
  • tsc(TypeScript)のようなトランスパイラーや、Webpackのようなバンドラーを使いたい

といったご要望です。

この記事では詳細は割愛しますが、ビュー機能ではレンダリング時にクライアントサイドJavaScriptソースコードのトランスパイルやモジュール解決を行っており、ベースマキナのアクションなどの呼び出しに関してはサーバーサイドの機能に大きく依存しています。

このような背景から、ローカル環境のみで完結するプレビュー機能を提供することは現実的ではありませんでした。

bm-view-previewの内部構成

今回は、Playwrightを組み込んだNode.jsアプリケーションを提供することで課題の解決を試みました。

bm-view-previewはnpmでパッケージとして配布されているNode.jsアプリケーションです。bm-view-previewコマンドを起動すると、Playwrightを通してChromiumを立ち上げ、basemachina.comのビューの新規作成ページを開きます。そして、ローカル環境のソースファイルの更新を監視して、ビューの新規作成ページ内のコードエディタに随時その内容を流し込んでいきます。

このような構成にすることで、既存の実装を利用する形でローカル環境でのライブプレビューを実現しています。

ライブラリとしてのPlaywright

Playwrightには、E2Eテストなどの機能を提供する@playwright/testパッケージのほかに、ブラウザの操作などの基本的な機能を提供するplaywrightパッケージがあります。

以下のようにplaywrightライブラリを直接使うことで、ブラウザを起動して操作することができます。

// 操作対象のブラウザとしてchromium, firefox, webkitがimportできる。今回はchromiumを使う。
import { chromium } from "playwright";

(async () => {
  // ブラウザを起動する
  const browser = await chromium.launch();

  // タブを開く
  const page = await browser.newPage();

  // 開いたタブでhttp://example.com/にアクセスする
  await page.goto("http://example.com/");
})();

ブラウザを人の手で操作できるようにする

これでブラウザを動かすことができましたが、Playwrightはデフォルトでは自動テストやオートメーションに使われることを想定した設定となっています。そのままでは人の手でブラウザを操作するには不便なので、設定をいくつか変更します。

ウィンドウを表示する

Playwrightはデフォルトではウィンドウを非表示となっています。launchメソッドのオプションに headless: false を渡すことでウィンドウを表示させることができます。

 import { chromium } from "playwright";

 (async () => {
-  const browser = await chromium.launch();
+  const browser = await chromium.launch({
+    headless: false,
+  });
   const page = await browser.newPage();
   await page.goto("http://example.com/");

ウィンドウのリサイズに対応する

Playwrightはデフォルトではviewport(ページの描画領域)のサイズが1280x720に固定されています。そのため、ウィンドウを広げても描画領域は広がらず、以下のように空白の領域が現れます。

ウィンドウを広げても表示領域は広がらず、画面右と画面下に空白が生じる

これは自動テストの実行結果を安定させるための意図的な挙動です*1

とはいえユーザーがウィンドウを操作する場合には不便なので、以下のように設定して無効にしておきます。

 (async () => {
   const browser = await chromium.launch({
     headless: false,
+    viewport: null,
   });
   const page = await browser.newPage();
   await page.goto("http://example.com/");

ダイアログを自動で閉じない

Playwrightはデフォルトではダイアログ(window.alert, window.confirm, window.promptによって開かれるネイティブのダイアログ)をすべて自動で閉じます。ダイアログが開いたままではページの操作がブロックされ、テストが進められなくなってしまうためです。

ですが、ユーザーがウィンドウを操作する場合にはこのような挙動は不要です。以下のように、dialogイベントに対して何もしないハンドラを明示的に指定して無効にしておきます。

page.on('dialog', () => {});

できなかったこと

アプリケーションモードでの起動

ChromiumGoogle Chromeではchromium --app=https://example.comのように起動オプションを指定することでアプリケーションモードとなり、URLバーやタブ一覧が隠された状態で起動します。

プレビュー画面だけを表示するのにちょうど良さそうな機能ですが、Playwrightではこの機能は無効化されており、使うことができません。

自動テスト

bm-view-preview自体のE2Eテストを実装することができていません。PlaywrightによってコントロールされているChromiumを、さらに別のテストコードから制御して動作を検証するための適切な手法が見つけられていないためです。

そのため、現在はリリース前の動作確認を手動で行っています。

まとめ

Playwrightによるライブプレビューツールの実装についてご紹介しました。

このようにライブラリとしてのPlaywrightを利用することで、通常のウェブブラウザだけでは実現できない機能を比較的容易に追加することができます。また、Webアプリケーション本体に変更を加える必要がないという点も、場合によっては大きなメリットになりうると感じました。

一方で、Playwrightの制御下のブラウザはアプリケーションとして利用することが想定されていないため、いくつかの課題があることも分かりました。

ビュー機能では、今回ご紹介したライブプレビュー機能と「コード取得設定」を合わせて使うことで、ベースマキナの管理画面の構築機能を活用しながら、通常のフロントエンド開発に近い開発体験で画面を作り込むことができるようになっています。今後とも、利用体験の改善の裏側をご紹介できればと思っています。

*1:It makes the execution of the tests non-deterministic. Playwright - viewport