Vue アプリケーションのソース単位のコードカバレッジ取得

## はじめに

Vue.js で作成した画面処理をEnd-To-Endで実行した際の、フロントエンド側のコードカバレッジを、実際に取得できるようにした後の整理として記載する。

## 前提

### 確認した環境について

本記事で確認に使用しか環境は次のとおり。

| 種類 | バージョン |
|:-:|:-:|
| OS | macOS Sonoma 14.2.1 |
| node | v21.6.1 |
| npm | 10.2.4 |

### 対象アプリケーションについて

対象のVue.jsアプリケーションは、書籍『[コードレベルで比べるReact Angular Vue.js　フレームワークの選択で後悔しないために](https://www.amazon.co.jp/%E3%82%B3%E3%83%BC%E3%83%89%E3%83%AC%E3%83%99%E3%83%AB%E3%81%A7%E6%AF%94%E3%81%B9%E3%82%8BReact-Angular-Vue-js-%E3%83%95%E3%83%AC%E3%83%BC%E3%83%A0%E3%83%AF%E3%83%BC%E3%82%AF%E3%81%AE%E9%81%B8%E6%8A%9E%E3%81%A7%E5%BE%8C%E6%82%94%E3%81%97%E3%81%AA%E3%81%84%E3%81%9F%E3%82%81%E3%81%AB-%E6%9C%AB%E6%AC%A1-%E7%AB%A0-ebook/dp/B0BHYRSDHD "Amazon Kindleストア")』のサンプルアプリケーション。

このアプリケーションは簡単なToDo管理を行うもので、外部リソースへのアクセスはない。そのため、End-To-End で実行するにあたり、HTTPサーバーのみで実行できることから、目的のコードカバレッジ取得する点に集中しやすいので選択した。

このアプリケーションは `npm create vue@latest` で作成する際、各プロンプトに次の選択をした状態(端的にすべてデフォルトの No を選択した状態)。

| プロンプト | 選択肢(*) | 選択 |
|:-|:-:|:-:|
| Add TypeScript? | **No** / Yes | No |
| Add JSX Support? | **No** / Yes | No |
| Add Vue Router for Single Page <br>Application development? | **No** / Yes | No |
| Add Pinia for state management? | **No** / Yes | No |
| Add Vitest for Unit Testing? | **No** / Yes | No |
| Add an End-to-End Testing Solution? | **No** / Cypress /<br>Nightwatch /<br>Playwright | No |
| Add ESLint for code quality? | **No** / Yes | No |

```math
    \hspace90mm\llap(*) 太字はデフォルトの選択値
```

このため、End-to-End Testing Solution に No か Playwright 以外を選択したアプリケーションの場合は本記事の通りには手順を進められないと考えられる。

### テストコードについて

サンプルアプリケーションとしてはテストコードは含まれないため、２〜３ケース分のテストコードは Playwright の codegen を利用して作成することとした。

つまり、新規に小さなアプリケーションを作成した直後に次の作業を実施した形。

1. テストフレームワークの組み込み〜カバレッジ取得までの環境構築
1. テストの実行とカバレッジ取得

なお、テストコード作成の過程は他の優れた記事に譲り本記事の対象外とする。

## 環境構築

### 【手順1】Playwright の初期化

Playwright を使えるように次のコマンドを実行して初期化し必要ファイルなどの環境を整備する。

```console
npm init playwright@latest
```

各プロンプトは次のように選択する。

| プロンプト | 選択肢(*) | 選択 |
|:-|:-:|:-:|
| Do you want to use TypeScript or JavaScript? | **TypeScript** /<br>JavaScript | JavaScript |
| Where to put your end-to-end tests? | **tests** | tests |
| Add a GitHub Actions workflow? | y / **N**  | N |
|Install Playwright browsers (can be done <br>manually via 'npx playwright install')?| **Y** / n | Y |

```math
    \hspace90mm\llap(*) 太字はデフォルトの選択値
```

この結果、以下のディレクトリとファイルが作成される。

```console
$ tree -L 2
.
...<省略>

├── playwright.config.js

...<途中省略>

├── tests
│   └── example.spec.js
├── tests-examples
│   └── demo-todo-app.spec.js

...<以下省略>
```

### 【手順2】必要パッケージのインストール

【手順1】でもいくつかパッケージが追加されているが、他に必要な次の表のパッケージをインストールする。

| パッケージ | 用途 |
|:--|:--|
| playwright-test-coverage | Playwrightのコードカバレッジ取得用拡張 |
| @playwright/experimental-ct-vue | PlaywrightのVue.jsコンポーネントテスト用拡張 |
| vite-plugin-istanbul | nyc/istanbul に合わせてコードをインストルメント化するViteプラグイン |
| nyc | カバレッジ計測ツールistanbulのテスト実行用コマンド |

すべて`devDependencies`に追加する。
コマンド例は次のとおり。

```console
npm install --save-dev \
     @playwright/test playwright-test-coverage @playwright/experimental-ct-vue \
     vite-plugin-istanbul @istanbuljs/nyc-config-babel nyc
```

### 【手順3】`package.json` の編集

defineConfig の scripts の "dev"を以下のように変更する。

    変更前  "dev": "vite",
    変更後  "dev": "vite --port 5173",

defineConfig の scripts に以下を追加する。最後に追加するときには前の行末にカンマを忘れないこと。

    "test:e2e-coverage": "nyc playwright test"


### 【手順4】`playwright.config.js` の編集

ES6のjs形式への書き換え

    変更前  const { defineConfig, devices } = require('@playwright/test');
    変更後  import { defineConfig, devices } from '@playwright/test';

    変更前  module.exports = defineConfig({
    変更後  export default defineConfig({ 

retries を 最低1に設定(リトライでトレースと動画を録るため)する。

    変更前  retries: process.env.CI ? 2 : 0,
    変更後  retries: process.env.CI ? 2 : 1,

reporter を以下のように変更する。

    変更前  reporter: 'html',
    変更後  reporter: [
              [ 'html', { open: 'never' } ],
              [ 'list' ]
            ],
  
use の baseUrl を有効化し、baseUrl のサーバーアドレスとポートを設定する。
この時アドレスが 127.0.0.1 で良い場合でも localhost にしておく（先々で動作不良の元になる）

    変更前  baseUrl: 'http://127.0.0.1:3000',
    変更後  baseUrl: 'http://localhost:5173',

use のコメントアウトされている trace を有効化する。

    trace: 'on-first-retry',

use に video を追加しリトライ時に動画録画する

    video: 'on-first-retry',

コメントアウトされている webServer を有効化し、command を package.json の dev を実行する形にする。

    変更前  command: 'npm run start',
    変更後  command: 'npm run dev',
    
url のサーバーアドレスとポートを設定（baseUrlと同じ）する。

    変更前  url: 'http://127.0.0.1:3000',
    変更後  url: 'http://localhost:5173',


### 【手順5】`nyc.config.js` の作成

`nyc.config.js` を次の内容で作成する。

```javascript
'use strict'

module.exports = {
  all: true,
  extension: ["js","vue"],
  include: [
    "src/**/*"
  ],
  reporter: [
    "text",
    "lcov"
  ]
}
```

### 【手順6】`vite.config.js` の編集

istanbul のインポートの追加。

    import istanbul from 'vite-plugin-istanbul'

defineConfig に build エントリーを以下のように追加する。

    build: {
      sourcemap: true
    },

defineConfig の plugins に以下を追加する。

      istanbul({
        include: ['src/**/*'], // files to track coverage on
        exclude: ['node_modules'], // files to NOT track coverage on
        requireEnv: false
      }),


## 実行

### テストケースの作成

次のコマンドを実行して１、２ケーステストケースを作成する。保管先ディレクトリーは`package.json`があるディレクトリの`tests`ディレクトリ。


```zsh
npx vite & ; npx playwright codegen --output tests/test1.spec.js
```

ブラウザが起動するので、何かしら操作して `codegen` のブラウザを終了させるとテストケースが保存される。

### テストの実行

作成したテストケースを次のコマンドで実行する。

```shell
npm run test:e2e-coverage
```

### Playwright のテスト結果の参照

#### ブラウザで参照

今回は `playwright.config.cjs` に `reporter` として `html` を指定しているので、次のコマンドを実行するとブラウザが起動して参照できるようになる。

```shell
npx playwright show-report
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/6934ff86-c96c-7d2a-c399-c231142644c2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/4a982847-edff-30de-999b-23ccba4ccea1.png)



#### トレースビューワーで参照

失敗するケースがあった場合、最初のリトライ時にトレースを取る設定にしている。
トレースは `test-results/[テストケース名]-[プロジェクト名]-retry1` というディレクトリーに、`trace.zip` というファイルができるので、それを次のコマンドで参照することができる。

```shell
npx playwright show-trace test-results/trest1-1-test-chromium-retry1/trace.zip
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/0e610d33-09f9-8e25-5239-c0e3c821593a.png)

### コードカバレッジの参照

今回は `nyc.config.cjs` に `reporter` として `lcov` を設定しているので以下のファイルをブラウザで開くことでカバレッジを参照できる。

```shell
open coverage/lcov-report/index.html
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/2a440f2b-e39b-c895-6863-965c4826eb48.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/b3b8a744-0ffd-e881-edc7-af54c2de3c30.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/7fc38c5b-6eb7-b5aa-d94c-855774d63cf8.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31769/94fd09b9-5b84-2604-c6c4-884de0948100.png)