この記事はベリサーブ Advent Calendar 2020 - Qiitaの2日目です。
昨日は@s-tanoueさんのGitHubActionsを用いて、爆速でNuxt.jsのコンポーネントテストをCIで動かす - Qiitaでした。
[12/3 追記]
出力したコードに関連して、ARIAってなんぞ?となった場合はこちらを参照
Puppeteer と ARIA Handler | Medium
[追記おわり]
ChromeのDevToolsにPuppeteerReporderが標準搭載されるらしい
以下の記事をTwitterで見かけました。
Automatically record puppeteer tests - Chrome DevTools - Dev Tips
ChromeのDevToolsのオプションをちょっといじると、Recordingsという項目が追加され、ブラウザ操作をキャプチャしてPuppeteerの実行コードを生成できるとのこと。面白そう。
でもまだ正式リリース前なので、実際動かすまでの手順と動かしてみてどうか、を記録しておきます。
使い方
1. Chrome canary releaseをインストール
ここからデベロッパー向けビルドをダウンロードしてインストールします。
Chrome Canaryをインストールしても既存のChromeは消えず、別のブラウザとしてインストールされますのでご安心を。
2. DevToolsからPuppeteerReporderを有効にする
DevToolsを開いて、歯車のマークを選択
Experimentsの中の「Recorder」にチェックを入れる
Reload DevToolsボタンを押す
Sourcesタブから>>を選んで、Recordingsを選択
これで記録の準備ができました。
3. 操作を記録する
+Add Recordingを押してはじめます。新しい記録が作成されますので、任意に名前を変えましょう。
下のRecordボタンを押すと記録が始まります。が、このとき注意が必要で、先にテストしたいサイトを開いた状態で開始する必要がありました。
Recordボタンを押してからURLバーに操作したいサイトのURLを入れても、コード側には反映されず。残念。
Record中の操作は画面下部にリアルタイムに表示されます。
パスワードは丸見えですね。
未ログイン状態のQiitaのトップで、ユーザ名・メールアドレス・パスワードを入れて登録するボタンを押す操作を記録した結果が以下です。
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();
})();
↑が原文ママです。インデントがアレなのと、なぜか1つの操作が3行ずつ表示されてしまっています。
登録するボタンを押す操作が記録されていませんが、規約の同意などのチェックボックスをONにしない状態で登録するボタンを押しても、画面遷移などせずバルーンで警告が出るのみでした。画面遷移など伴わないと記録されない模様。
Puppeteerで動かす
原文ママだと動かせ無さそうだったので、少し調整してみます。
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();
})();
重複操作を除いたのと、あとはヘッドレスではなく画面表示するようにしました。
これで動かすと・・・
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指定にしてみましょう。
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やるぞ!となった場合のとっかかりとしてはよさそうです。とりあえず記録して出力されたコードを、エラーの原因調べつつ調整していく、という使い道。
ブラウザの標準機能として備わっていくということで、今後が楽しみです。