Node.jsからLighthouseを使用して認証付きサイトを検証する方法



## 目的
Node.jsからLighthouseを使って認証付きサイトの測定を自動化しようとして色々調べていたが、情報が散らばっていて構築するのにすごく手間がかかったので、メモがてら残しておく

## 環境
nodeがインストールされていることが前提
node v16.13.1


## 準備
まずは`Lighthouse`とブラウザ操作をするために`Puppeteer`、パスワードなど環境変数を使用するために`dotenv`をインストールする

```
npm install lighthouse
npm install puppeteer
npm install dotenv
```

## Node.jsで動かすためのコード
.envやコンフィグファイルを使用して、ユーザー名やパスワードをフォームに入力し、狙いのURLをLighthouseで検証するコードは以下となる。
Basic認証を通している。

```lighthouse.js
const fs = require("fs");
const puppeteer = require("puppeteer");
const lighthouse = require("lighthouse");
const { URL } = require("url");
const config = require("./custom-config.js");
require('dotenv').config();

(async () => {

  // ブラウザを立ち上げる
  const browser = await puppeteer.launch({
    headless: false,
    defaultViewport: null,
  });
  const page = await browser.newPage();

  // Basic認証の設定   
  await page.setExtraHTTPHeaders({
    Authorization: `Basic ${Buffer.from(`${process.env.BASIC_USERNAME}:${process.env.BASIC_PASSWORD}`).toString("base64")}`,
  });

  // ログインページに遷移
  await page.goto(loginUrl, {
    waitUntil: "domcontentloaded",
  });
  // ユーザーメイトパスワードを入力してログインボタンを押す
  await page.type("ユーザー名のフォームを指定", process.env.USER_NAME);
  await page.type("パスワードのフォームを指定", process.env.USER_PASSWORD);
  await page.click("ログインボタンを指定");
  await page.waitForNavigation({
    timeout: 20000,
    waitUntil: "domcontentloaded",
  });

  // 測定したいページに遷移
  await page.goto(process.env.TARGET_URL, {
    waitUntil: "domcontentloaded",
  });

  // BASIC認証を入れてLighthouseを起動する
  const runnerResult = await lighthouse(
    targetUrl,
    {
      port: new URL(browser.wsEndpoint()).port,
      logLevel: "info",
      output: "html",
      disableStorageReset: true,
      extraHeaders: {
        Authorization: `Basic ${Buffer.from(`${process.env.BASIC_USERNAME}:${process.env.BASIC_PASSWORD}`).toString("base64")}`,
      },
    },
    config
  );

  // Lighthouseのレポートを保存する
  const reportHtml = runnerResult.report;
  fs.writeFileSync("./reports/" + nameReport, reportHtml);

  // スコアをコンソールに表示
  console.log(
    `Lighthouse scores: ${Object.values(runnerResult.lhr.categories)
      .map((c) => c.score * 100)
      .join(", ")}`
  );

  await browser.close();

})();
```


Lighthouseに設定値を使用するためにコンフィグファイルを使用している。

```custom-config.js
module.exports = {
    extends: "lighthouse:default",
    settings: {
      formFactor: "desktop",
      screenEmulation: {
        mobile: false,
        width: 1366,
        height: 768,
        deviceScaleFactor: 1,
        disabled: false,
      },
      emulatedUserAgent:
        "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4143.7 Safari/537.36 Chrome-Lighthouse",
    },
  };
```

さらに、パスワードなどの変数を使用するために.envファイルを使用する。

```.env
BASIC_USERNAME = ""
BASIC_PASSWORD = ""
USER_NAME = ""
USER_PASSWORD = ""
```

## 動かし方
コマンドラインで以下を実行する
node lighthouse.js

ブラウザが自動で立ち上がりユーザー名やパスワードを入れてページ遷移をして、Lighthouseの計測が始まる


## 所感
１．Node.jsで計測したLighthouseの結果とChrome開発者ツールで測定した結果があまりにも違いすぎる

Node.jsで計測した結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2359451/b8f963a7-df10-c6f1-d01b-199e7f4c0e13.png)

Chrome開発者ツールで測定した結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2359451/9eef0ea4-fa71-b46a-6968-f1cf60e9a2cf.png)


２．３回に１回くらい失敗する
　→これはChrome開発者ツールで測定しても失敗することがある（セッションが絡むと上手くいかないことがある）