Playwrightで簡単なクロスブラウザテストを書いてみた

2020 年 5 月の頭頃、Playwright が 1.0 に到達した際に、作成チームによって書かれた記事がホットエントリーになっていました。
https://medium.com/@arjunattam/fast-and-reliable-cross-browser-testing-with-playwright-155c0e8a821f

タイミング良くクロスブラウザテストを書く機会があったので、簡単なテストを書いてみました。
※ ブラウザ毎に挙動の異なる API のテストだと良かったのですが、こちらで実施するのはボタンをクリックして innerText の変更をテストするのみです。

ソースはこちらにあります。
https://github.com/nnashiki/playwright_sample

# Playwrigh とは

Chromium、Firefox、Safari を簡単にクロスブラウザテストするための、Node library とのことです。

> Playwright is a Node library to automate Chromium, Firefox and WebKit with a single API. Playwright is built to enable cross-browser web automation that is ever-green, capable, reliable and fast.

https://playwright.dev/
https://github.com/microsoft/playwright

冒頭に挙げた medium の記事では、Chromium、WebKit、FireFox、WebDriver などに協力を得た(巨人の肩に乗ってるんだぜ!)とあります。
アーリアダプターは[こんな感じ](https://github.com/microsoft/playwright/blob/master/docs/showcase.md)に充実しています。

# テスト対象

"ボタン押下前"と書かれているボタンを押すと"ボタン押下前"に変わる簡単なものです。

以下コマンドでサービスを立ち上げます。
`docker run --rm --name sample -p 8090:80 -v $PWD/src/app:/usr/share/nginx/html -d nginx`

```index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <title>ボタンテスト</title>
    <link rel="icon" href="favicon.ico" />
    <!- bootstrap4 ->
    <link
      rel="stylesheet"
      href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"
      integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
      crossorigin="anonymous"
    />
    <!- bootstrap4 ->
  </head>
  <body>
    <div class="container">
      <button id="test_button1" type="button" class="btn btn-primary">
        ボタン押下前
      </button>
    </div>
    <script type="text/javascript" src="./index.js?0.0"></script>
    <!- bootstrap4 ->
    <script
      src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
      integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"
      integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1"
      crossorigin="anonymous"
    ></script>
    <script
      src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"
      integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM"
      crossorigin="anonymous"
    ></script>
    <!- bootstrap4 ->
  </body>
</html>
```

```index.js
const button1 = document.getElementById("test_button1");
button1.addEventListener("click", (e) => {
  document.getElementById(e.target.id).innerText = "ボタン押下後";
});

```

# テストコード

## 利用コード

test runner は Jest を使用しています。
各ブラウザで自動操縦でボタンをクリックして、表記の変化を確認するテストです。
ブラウザ単位でテスト結果を確認したいので分割しました。

```src/test/button_action.test.js
const playwright = require("playwright");

// #test_button1 のボタンを押下して、ボタンの表記を取得する
const croll_click = async (browserType) => {
  const browser = await playwright[browserType].launch();
  const context = await browser.newContext();
  const page = await context.newPage();
  await page.goto("http://localhost:8090");
  await page.click("#test_button1");
  const click_after_text = await page.evaluate(() => {
    return document.getElementById("test_button1").innerText;
  });
  await browser.close();
  return click_after_text;
};

test("click chromium", async () => {
  let result = await croll_click("chromium");
  expect(result).toMatch(/ボタン押下後/);
});

test("click firefox", async () => {
  let result = await croll_click("firefox");
  expect(result).toMatch(/ボタン押下後/);
});

test("click webkit", async () => {
  let result = await croll_click("webkit");
  expect(result).toMatch(/ボタン押下後/);
});

```

page の中での javascript(webAPI)の実行は以下を参考にしました。
https://playwright.dev/#version=v1.0.2&path=docs%2Fverification.md&q=evaluating-javascript

Jest の set up、tear down でブラウザの呼び出しと終了をするには以下が参考になります。
https://playwright.dev/#version=v1.0.2&path=docs%2Ftest-runners.md&q=jest--jasmine

## 環境構築

Playwright、 Jest を install します。

```
npm install --save-dev playwright
npm install --save-dev jest
```

# 実行してみる

`jest src/test` を実行します。
ブラウザ毎のテストを実行することができました。

```
> jest src/test

 PASS  src/test/button_action.test.js (7.1 s)
  ✓ click chromium (1327 ms)
  ✓ click firefox (4186 ms)
  ✓ click webkit (1073 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        7.594 s
Ran all test suites matching /src\/test/i.

```

## tips

そのまま実行すると、Jest の default time out が引っかかることがありました。

```zsh
 FAIL  src/test/button_action.test.js (8.725 s)
  ✓ click chromium (2076 ms)
  ✕ click firefox (5002 ms)
  ✓ click webkit (1018 ms)

  ● click firefox

    : Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 5000 ms timeout specified by jest.setTimeout.Error:

      22 | });
      23 |
    > 24 | test("click firefox", async () => {
         | ^
      25 |   let result = await croll_click("firefox");
      26 |   expect(result).toMatch(/ボタン押下後/);
      27 | });

      at new Spec (node_modules/jest-jasmine2/build/jasmine/Spec.js:116:22)
      at Object.<anonymous> (src/test/button_action.test.js:24:1)

Test Suites: 1 failed, 1 total
Tests:       1 failed, 2 passed, 3 total
Snapshots:   0 total
Time:        9.605 s, estimated 12 s
Ran all test suites.
```

そこで、timeout を伸ばして実行しました。
`jest src/test --testTimeout=10000`
5000mm を超えても実行できています。

```
$ jest src/test --testTimeout=10000
 PASS  src/test/button_action.test.js (11.592 s)
  ✓ click chromium (3831 ms)
  ✓ click firefox (5042 ms)
  ✓ click webkit (1948 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        12.957 s
Ran all test suites matching /src\/test/i.
```

# まとめ

puppeteer は使った事が無いので、比較はできませんが利用の開始はとても簡単でした。
Playwright は普及しそうなので、今後も追ってみたいと思います。