Docker上でPlaywrightを動かしてホストOSからテストレポートにアクセスしたい！

ディップのフロントエンド課でフロントエンドエンジニアをしている@e_fullです！

Playwrightの導入検証を行う中で発生した、Docker上で動かしたテストレポートが見れない現象と戦った話について書いていきます。初投稿ながら大変なボリュームかつ、Playwrightユーザーに向けたニッチな内容ですが誰かの参考になれば嬉しいです☺️

## ****TL;DR****

Playwrightのレポートは本番運用を想定していないため、Reactアプリのようにそれ単体で動かしても外部からアクセスできるようになっていません。

そのため、ローカルで起動したDocker上でPlaywrightのレポートを実行して、ホストOSから`http://localhost:9323`にアクセスしてもアクセスできません。

対処方法としてはPlaywrightが起動しているDocker内にNginxでリバースプロキシの設定を行なってホストOSから通信可能にしてあげる必要があります。この投稿ではその過程と解決策について説明します！

## はじめに

フロントエンド課ではLaravelによる本開発前にUIを確認できるよう、phpファイルによるスケルトンというテンプレートを作成しています。(スケルトンは社内用語)

スケルトンではアトミックデザインの概念を取り入れた構成となっているため、MoleculeやAtomなどの共通コンポーネントが存在します。

共通コンポーネントに手を入れた場合、対象外のスケルトンに意図しない変更が発生していないか確認する必要がありますが、これまではこうしたデグレの確認を個々人がキャプチャを作成して比較することで対応していました。

この確認作業改善のためにVRT導入を検討し、Playwrightの動作検証を行なっています。

PlaywrightはMicrosoftがPuppetterからフォークして開発しているテスト自動化フレームワークです。

## 前提

前提としてローカルでテスト対象のwebアプリが起動していることを想定しています。

また、今回のPlaywrightのインストール先はテスト対象のwebアプリのリポジトリ内です。

そもそもなぜDockerを利用してPlaywrightを動かしたかったのかというと、実施する環境によって結果がブレる場合があったためです。

当初はローカルに起動している開発環境であるDocker内にPlaywrightをインストールして・・・ということを画策して居ました。しかし、Playwrightが前提としているOSと開発環境のOSに乖離があったり、Docker起動時のコマンドが複雑になるなどの理由から諦めかけていたところ、他のチームの方から公式でDockerイメージが提供されていることを教わりました👏🏻

https://playwright.dev/docs/docker

## Playwrightの初期設定

まずは開発環境のリポジトリに`npm init playwright@latest` でplaywrightをインストール・初期構築します。

作成されたtestsフォルダ内にある`tests/example.spec.js` を以下の通り書き換えます。

テスト内容は、アクセスしたページ全体のキャプチャを撮ってキャプチャ元と比較するというものです。

```javascript
"use strict"
// @ts-check
import { test, expect } from '@playwright/test';

const testURL = 'http://localhost:5173/'; //ローカルで起動しているテスト対象のwebアプリ

// テスト実行
test("test", async ({ page }) => {
  await page.goto(testURL, { waitUntil: 'domcontentloaded' });
  await expect(page).toHaveScreenshot({ fullPage: true });
});
```

この変更により、ローカルで`npx playwright test` コマンドによってテストの通過が確認できたかと思います。

初回は`npx playwright test -update-snapshots` としないと比較元のキャプチャがなく全てのテストがfailedになってしまいます。

テスト通過後、テストがfailedしていればそのままレポート閲覧のためのサーバーが起動しますが、すべてクリアした場合は処理が終了します。その場合は以下のコマンドでレポート閲覧のためのサーバーを起動します。

`npx playwright show-report`

ここまでの導入はとても簡単で、テストもjest等のようにnodeベースで記載することができました👏🏻

テスト結果のレポートは以下のように差分が確認できて少しの差もとてもわかりやすくなっています🥳

![画面収録_2023-06-15_23_15_01_AdobeExpress.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477579/f72a93c3-4337-a5c7-2d16-78cc445d7447.gif)


## Docker上でテストを実行

続いてDocker環境を整備して、Docker上でレポートを実行、ホストOSのブラウザでレポートを表示していきます。

イメージは公式で提供されている`mcr.microsoft.com/playwright:v1.35.0-jammy`を利用します。

手っ取り早く実行するには、以下のコマンドを投げればビルドされたDocker環境上でテストを実行してくれます。

```bash
docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.35.0-jammy /bin/bash -c 'npx playwright test'
```

ターミナル上ではレポートを`http://localhost:9323`でサーブしたよとメッセージが出ていますが、ホストOSのブラウザからアクセスしてもアクセスできません。

![スクリーンショット 2023-06-15 16.31.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477579/348ceada-d267-e5d6-757f-213e1aa68875.png)



Dockerコマンドでポートが通ってないからかな？と思ってportオプションを足してみたり、Playwrightの[Discussion](https://github.com/microsoft/playwright/discussions/20986)を見てみたりしたけど解決策がわからずしばらく彷徨い・・・

以下のページに行きつきました。

https://tech.mokelab.com/infra/docker/tips/empty_reply_from_server.html

> コンテナ内で起動しているサーバーがlocalhost指定でListenしている可能性があります。
> 

そこでPlaywrightのコードを深掘りしてみたところ、確かにレポートのサーバーをlocalhostで建てているようでした。

※131行目で初期値がlocalhostなので、何かの設定でhostを変えられるんじゃないかと思いましたがそこまで追えませんでした。

![スクリーンショット 2023-06-15 16.40.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477579/58ad5e51-c917-74b5-c88f-e95af6e099d4.png)


## docker-composeとNginxの設定

原因が見えてきたところで上記の参考サイトに記載の通り、コンテナ内でNginxを利用してリバースプロキシを置くことにしました。

`docker run …` のコマンドで実行した場合、コンテナ起動後にコンテナに入り、Nginxをインストールしてdefault.confを設定して・・・とテスト実行毎に面倒なことになるので、docker-composeを利用してコンテナを立てることにしました。

まずはリポジトリ内に以下のファイルを用意します。

```bash
.
├── docker
│  └── playwright
│     ├── default.conf // Nginx用
│     └── dockerfile
└── playwright-test.yml
```

それぞれのファイルは以下のように記述します。

```json:default.conf
server {
  listen 9333;
  server_name localhost;

  location / {
    proxy_pass http://localhost:9323;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}
```

```dockerfile:dockerfile
FROM mcr.microsoft.com/playwright:v1.35.0-jammy

WORKDIR /app

# Nginxと簡易ファイアウォールのufwをインストール
RUN apt update && apt install -y sudo ufw nginx

# ufw利用のためのユーザーを作成
ARG DOCKER_UID=0333
ARG DOCKER_USER=playwright
ARG DOCKER_PASSWORD=playwright
RUN useradd -m --uid ${DOCKER_UID} --groups sudo ${DOCKER_USER} \
  && echo ${DOCKER_USER}:${DOCKER_PASSWORD} | chpasswd
RUN passwd -d ${DOCKER_USER}

# 作成したユーザーに切り替える
USER ${DOCKER_USER}
```

```yml:playwright-test.yml
version: '3'

services:
  playwright:
    build: ./playwright
    container_name: playwright
    ports:
      - "9323:9333" #`Playwright test result`
    volumes:
      - ./playwright/default.conf:/etc/nginx/conf.d/default.conf
      - ../:/app/
    command: sh -c "sudo ufw enable && sudo ufw allow 'Nginx HTTP' && sudo ufw allow 9333 &&sudo nginx && npx playwright test --trace on --workers 4"
    cap_add:
      - NET_ADMIN
```

ここまで環境を整えたら、Dockerを起動します。

```bash
docker-compose -f ./docker/playwright-test.yml up
```

これでDockerを起動するホストOSから`http://localhost:9323`にアクセスすることでレポートを表示することができました🎉

最終的に以下の図のようなポートを辿ってレポートが表示されるようになりました！

![Qiita-img.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3477579/3b83570d-4d14-2344-c185-354ee2bf8343.png)


上記のコマンドでテストがパスしてしまって処理が終わった場合は、./docker/playwright-test.ymlのcommand部分を`sh -c "sudo nginx && npx playwright show-report"` に変更して動かしてみてください。

補足として、`./docker/playwright/dockerfile` でufwを設定していますがなくても動きます。ただし、今後社内サーバーで動かしたいなどの変更があるかもしれないことを見込んで先にFWの設定を入れました。

## まとめ

ここまではレポートの表示につまづいたりテスト先のページにアクセスできないなどの問題があったりしつつ、他チームの方に助けて頂きつつ🙏🏻、比較的簡単に導入できたと思っています。

しかし、実行回によって変更していない箇所のテストがfailedになるという実行結果のブレに現在悩まされているので、今後はこの辺りのTipsを共有できればと思っています。

とはいえ、Playwrightを入れることでも十分業務効率が改善する見込みがあります。まずはwebページ一枚っぺらの比較のみで小さく入れて、今後モーダルを動かしたり動的な部分のテストを育てていく予定です。

まだまだ英語情報ばかりですが、1次情報は比較的豊富な印象です。今後もテストを育てつつ業務効率改善していきます💪🏻