動作検証のためのブラウザ自動化

# はじめに

エキサイトの新卒2年目エンジニアの伊藤です。
adventCalendar8日目は, ブラウザ自動化の実践をしてみます。

## なぜブラウザ自動化？

ブラウザ自動化自体はそこまで真新しいものではなく, 探せば色々な記事が出てきます。
その用途は主にスクレイピング, E2Eテストの2つが強い印象です。
今回はE2Eに近いのですが, 動作検証にも結構効果的に使えるんじゃないか？って話をします。

## 動作検証の時間

最近, 気づいたことがあります。
プログラマーはコードを書いている時間より読んでいる時間の方が長いと言う話はよく耳にしますが, それ以上に自分の場合は動作検証にも時間を奪われてしまっています。

オンラインカウンセリングサービスのバックエンドを担当させていただいてるのですが, 動作確認の一連の流れが最も長くなるとこんな感じです。

テスト用のカウンセラーでログイン -> カウンセラーを待機状態へ -> テスト用ユーザーを新規登録 -> カウンセリング -> テスト用カウンセラーでレポート記載 -> テスト用ユーザー退会

APIあるんやろ？よしなに叩けばええやん。と言われそうですが, 古いサービスなのでDBの整合性やAPIのバリデーションがどこまでしっかりしているのかわからず不安になります。必要な全てのコードを読むのは辛いです。

そこでブラウザ自動化の登場です。

## ログインしてみる

javascriptのpuppeteerを使います。深い意味はありません。環境設定が楽だと書いてあったもので。(書いてあった記事を見つけきれない。。。)
実際楽でした。
https://github.com/puppeteer/puppeteer

実際のコードがこちらです。

```javascript
const puppeteer = require('puppeteer');
const fs = require('fs');
const yaml = require('js-yaml');

(async () => {
    const auth = await yaml.load(fs.readFileSync(process.argv[2], 'utf8'));

    const config = await yaml.load(fs.readFileSync('./counselor/config.yml', 'utf8'));

    const browser = await puppeteer.launch({
        ignoreHTTPSErrors: true,
        headless: false,
        args: [
            '--window-size=1920,1080'
        ]
    });
    const page = await browser.newPage();

    await page.setViewport({
        width: 1920,
        height: 1080,
    });

    await page.goto(`https://${config['host']}/`);

    const login_link = '#btn-group > ul > li:nth-child(2) > a';
    await page.waitForSelector(login_link);
    await page.click(login_link)

    const username = '#form > ul > li:nth-child(1) > div > input';
    await page.waitForSelector(username);
    await page.click(username);
    await page.keyboard.type(auth['counselor']['username']);

    const password = '#form > ul > li:nth-child(2) > div > input';
    await page.waitForSelector(password);
    await page.click(password);
    await page.keyboard.type(auth['counselor']['password']);

    const login_button = '#form > ul > input';
    await page.waitForSelector(login_button);
    await page.click(login_button);

    const cookies = await page.cookies();
    fs.writeFileSync(config['cookie_path'], JSON.stringify(cookies));

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

ログイン情報やサイトURLは外からとるようにさせていただいております。
特徴は最終的にクッキーをファイルに吐いておき次回読み込ませることで継続的な状態を作り出せると言うことでしょうか。

例えば次に何かするときにはこんな感じで元のクッキー状態を引き継げます。

```javascript
const puppeteer = require('puppeteer');
const fs = require('fs');
const yaml = require('js-yaml');

(async () => {
    const config = await yaml.load(fs.readFileSync('./counselor/config.yml', 'utf8'));
    const browser = await puppeteer.launch({
        ignoreHTTPSErrors: true,
        headless: false,
        args: [
            '--window-size=1920,1080'
        ]
    });
    const page = await browser.newPage();

    await page.setViewport({
        width: 1920,
        height: 1080,
    });

    const cookies = await JSON.parse(fs.readFileSync(config['cookie_path'], 'utf-8'));
    for (let cookie of cookies) {
      await page.setCookie(cookie);
    }

    await page.goto(`https://${config['host']}/prof/659268`);
})();

```

# 最後に

まだまだ思い立った段階で, 本当に動作検証に役に立つのかの運用はこれからになります。
ログイン, カウンセリング, 退会, 新規登録などのユースケースごとにスクリプトを作っておき, よしなに組み合わせることができる方法を模索していきます。

弊社のアドベントカレンダー他記事もよろしくお願いします。
https://qiita.com/advent-calendar/2021/excite-hd

採用情報はこちらになります。
https://www.wantedly.com/companies/excite