GoogleChromeのDevToolsでブラウザ操作を記録し、Puppeteerのコードを出力してみる

この記事は[ベリサーブ Advent Calendar 2020 - Qiita](https://qiita.com/advent-calendar/2020/veriserve)の２日目です。

昨日は@s-tanoueさんの[GitHubActionsを用いて、爆速でNuxt.jsのコンポーネントテストをCIで動かす - Qiita](https://qiita.com/s-tanoue/items/fcc5e3fe1c6d90bfa90c)でした。

[12/3 追記]
出力したコードに関連して、ARIAってなんぞ？となった場合はこちらを参照
[Puppeteer と ARIA Handler | Medium](https://quramy.medium.com/puppeteer-%E3%81%A8-aria-handler-188c7ebbed8)
[追記おわり]

## ChromeのDevToolsにPuppeteerReporderが標準搭載されるらしい

以下の記事をTwitterで見かけました。

[Automatically record puppeteer tests - Chrome DevTools - Dev Tips](https://umaar.com/dev-tips/241-puppeteer-recorder/)

ChromeのDevToolsのオプションをちょっといじると、Recordingsという項目が追加され、ブラウザ操作をキャプチャしてPuppeteerの実行コードを生成できるとのこと。面白そう。

でもまだ正式リリース前なので、実際動かすまでの手順と動かしてみてどうか、を記録しておきます。

## 使い方

### 1. Chrome canary releaseをインストール

[Chrome Canary](https://www.google.com/intl/ja/chrome/canary/)

ここからデベロッパー向けビルドをダウンロードしてインストールします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/08cd734c-eed5-9e9f-6d0a-19a02b3cac6f.png)

Chrome Canaryをインストールしても既存のChromeは消えず、別のブラウザとしてインストールされますのでご安心を。

### 2. DevToolsからPuppeteerReporderを有効にする

DevToolsを開いて、歯車のマークを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/368b7229-0af6-27b3-d31f-9c07bf683da0.png)

Experimentsの中の「Recorder」にチェックを入れる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/7b3c2b4d-b147-908b-5a5c-fd443b8cfe4d.png)

Reload DevToolsボタンを押す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/8bff2d00-9a0d-e5f1-d7a0-69320c667801.png)

Sourcesタブから>>を選んで、Recordingsを選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/a52656ad-500f-a93e-906d-cc494c52b6bc.png)

これで記録の準備ができました。

### 3. 操作を記録する

＋Add Recordingを押してはじめます。新しい記録が作成されますので、任意に名前を変えましょう。
下のRecordボタンを押すと記録が始まります。が、このとき注意が必要で、先にテストしたいサイトを開いた状態で開始する必要がありました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/1ce02d86-c71b-7088-5201-e6b4beefef0e.png)

Recordボタンを押してからURLバーに操作したいサイトのURLを入れても、コード側には反映されず。残念。

Record中の操作は画面下部にリアルタイムに表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/46492/8f2d977f-c919-f003-bcd4-2cf185200c1a.png)

パスワードは丸見えですね。

未ログイン状態のQiitaのトップで、ユーザ名・メールアドレス・パスワードを入れて登録するボタンを押す操作を記録した結果が以下です。

``` js
const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch();
    const page = await browser.newPage();

    await page.goto("https://qiita.com/");
await page.click("aria/qiitan");
await page.click("aria/qiitan");
    await page.click("aria/qiitan");
await page.type("aria/qiitan", "Hoge");
await page.type("aria/qiitan", "Hoge");
    await page.type("aria/qiitan", "Hoge");
await page.type("aria/qiitan@qiita.com", "hoge@hoge.com");
await page.type("aria/qiitan@qiita.com", "hoge@hoge.com");
    await page.type("aria/qiitan@qiita.com", "hoge@hoge.com");
await page.type("aria/********", "hogehoge");
await page.type("aria/********", "hogehoge");
    await page.type("aria/********", "hogehoge");
    await browser.close();
})();
```

↑が原文ママです。インデントがアレなのと、なぜか１つの操作が３行ずつ表示されてしまっています。

登録するボタンを押す操作が記録されていませんが、規約の同意などのチェックボックスをONにしない状態で登録するボタンを押しても、画面遷移などせずバルーンで警告が出るのみでした。画面遷移など伴わないと記録されない模様。

## Puppeteerで動かす

原文ママだと動かせ無さそうだったので、少し調整してみます。

``` js
const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({
        headless: false
    });
    const page = await browser.newPage();

    await page.goto("https://qiita.com/");

    await page.click("aria/qiitan");
    await page.type("aria/qiitan", "Hoge");
    await page.type("aria/qiitan@qiita.com", "hoge@hoge.com");
    await page.type("aria/********", "hogehoge");

    await browser.close();
})();
```

重複操作を除いたのと、あとはヘッドレスではなく画面表示するようにしました。

これで動かすと・・・

``` cmd
C:\Users\yoshiki.itou\Documents\workspace\my-puppeteer>node index.js
C:\Users\yoshiki.itou\Documents\workspace\my-puppeteer\node_modules\puppeteer\lib\cjs\puppeteer\common\assert.js:26
        throw new Error(message);
              ^

Error: No node found for selector: aria/qiitan
    at Object.exports.assert (C:\Users\yoshiki.itou\Documents\workspace\my-puppeteer\node_modules\puppeteer\lib\cjs\puppeteer\common\assert.js:26:15)
    at DOMWorld.click (C:\Users\yoshiki.itou\Documents\workspace\my-puppeteer\node_modules\puppeteer\lib\cjs\puppeteer\common\DOMWorld.js:276:21)
    at processTicksAndRejections (node:internal/process/task_queues:93:5)
    at async C:\Users\yoshiki.itou\Documents\workspace\my-puppeteer\index.js:11:5
```

みごとにエラー。ロケータがおかしい？

全てID指定にしてみましょう。

``` js
const puppeteer = require('puppeteer');

(async () => {
    const browser = await puppeteer.launch({
        headless: false
    });
    const page = await browser.newPage();

    await page.goto("https://qiita.com/");

    await page.click("#url_name");
    await page.type("#url_name", "Hoge");
    await page.type("#email", "hoge@hoge.com");
    await page.type("#password", "hogehoge");

    await browser.close();
})();
```

今度はちゃんと動いてくれました。


## 感想

元記事にあるように、記録すればそのまますぐ使えるというわけではなく、実際には

- Selectors are appropriate and maintainable.
    - セレクタを適切なもの、メンテしやすいものに変える
- waits/pauses are added if needed.
    - 待機を入れる
- Assertions are added in (assuming you're writing a test)
    - アサーションを入れる

などなどの対応が必要になります。このへんはSeleniumIDEをはじめ、画面操作を記録してコードを出力する系のツールには現状共通の課題ですね。

普段別の言語でSelenium使ってる人がJSでPuppeteerやるぞ！となった場合のとっかかりとしてはよさそうです。とりあえず記録して出力されたコードを、エラーの原因調べつつ調整していく、という使い道。

ブラウザの標準機能として備わっていくということで、今後が楽しみです。