Github の Readme に Coverage badge を付ける

<details open>
  <summary>更新履歴</summary>

* 2023/07/23 nyc 関係を更新


</details>

# 目的

* github の ReadMe に coverage badge を表示したい

# 前提

* `Node.js`の環境構築済み
* `Git`と`Github`の環境構築済み

https://qiita.com/ryo2020/items/136f1352aca97ddb026b

https://qiita.com/ryo2020/items/8bcae7d3993981c43068

# 環境

* Windows - v11
* Node.js - v16
* nyc - v15
* mocha - v10
* chai - v4
* Coveralls - v3
* Github Actions

# 結論

こうする。

```json:package.json
}
  "scripts": {
    "pretest": "rm -rf .nyc_output",
    "test": "nyc mocha -R spec",
    "precoverage": "rm -rf coverage && mkdir coverage",
    "coverage": "npm run test && nyc report --reporter=lcovonly"
  },
  "devDependencies": {
    "chai": "^4.3.7",
    "coveralls": "^3.1.1",
    "mocha": "^10.1.0",
    "nyc": "^15.1.0"
  }
}
```

```json:.nycrc
{
  "reporter": [
    "text-summary"
  ],
  "extension": [
    ".js",
    ".cjs",
    ".mjs",
    ".ts"
  ],
  "exclude": [
    "**/test/**.spec.js"
  ]
}
```

```yml:.github/workflow/coverage-badge.yml
name: Coveralls GitHub Action

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:

jobs:
  test:
    runs-on: windows-latest

    strategy:
      matrix:
        node-version: [15.x, 16.x] # (need)

    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Use Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }} # (need)
          cache: "npm"
          cache-dependency-path: "**/package-lock.json"

      - run: npm ci # (need) # better than "npm install"
      - run: npm run coverage # (need)

      - name: Coveralls GitHub Action
        uses: coverallsapp/github-action@1.1.3 # (need)
        with:
          github-token: ${{ secrets.GITHUB_TOKEN }} # (need)
```

```md:README.md
[![Coverage Status](https://coveralls.io/repos/github/[user]/coverage-badge-test/badge.svg?branch=main)](https://coveralls.io/github/[user]/coverage-badge-test?branch=main)

・・・ 以下 REAMDE 本文 ・・・
```

# 詳細

* Test runner : mocha ([NPM](https://www.npmjs.com/package/mocha))
* assertion library : Chai ([NPM](https://www.npmjs.com/package/chai))
* coverage tool : nyc ([NPM](https://www.npmjs.com/package/nyc))
* coverage badge : coveralls ([NPM](https://www.npmjs.com/package/coveralls))

## 1. nyc + mocha + chai のセットアップ

### 1.1. install

```bash:gitbash
$ npm install mocha --save-dev
$ npm install chai --save-dev
$ npm install nyc --save-dev
```

### 1.2. テスト対象の実行ファイルを作成

内容は適当。

```javascript:index.js
module.exports = () => {
  console.log("Hallow World");
};
```

### 1.3. テストスクリプトの作成

デフォルトのファイル構成

* :open_file_folder: test
  * :pencil: index.spec.js

内容は適当に。

```javascript:test/index.spec.js
const { describe, it } = require("mocha");
const { assert } = require("chai");

describe("test index", () => {
  const index = require("../index");

  it("index is function", () => {
    assert.isFunction(index);
  });
});
```

### 1.4. npm run script を作成

以下の様に`nyc`を噛ませて`coverage`が出力される様にする。

```json:package.json
{
  "scripts":{
    "test": "nyc mocha -R spec"
  }
}
```

### 1.4. テストの実行確認

スクリプトを実行

```bash:gitbash
$ npm run test
```

以下の様に`coverage`が表示されれば、一旦OK

```bash:gitbash
> nyc mocha -R spec

  test index
    ✔ index is function

  1 passing (6ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |      50 |      100 |       0 |      50 |
 index.js |      50 |      100 |       0 |      50 | 2
----------|---------|----------|---------|---------|-------------------
```

:::note info
情報
`nyc`で`coverage`を出力すると、以下の様に`.nyc_output`ディレクトリが自動生成されますが、`coverage`の生データであり、気にする必要はありません。(`temp-dir:"./.nyc_output"`(参考：[github](https://github.com/istanbuljs/nyc#common-configuration-options)))

* :open_file_folder: .nyc_output
:::

## 2. `Coveralls`をセットアップ

### 2.1. `coveralls.io`にアカウントを作成する

リンク：[Coveralls.io](https://coveralls.io/)

* 公開リポジトリ＋OSSであれば、無料。

### 2.2. `coveralls.io`と`Github`を連携する

`Coveralls.io`から`Github`にHTTPリクエストが発生するので、その認証が必要。らしい。

連携が出来ていれば、`coveralls.io`の[アカウントページ](https://coveralls.io/account)に`Github`のプロフィールアイコンが表示される。ハズ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/feff2427-80b4-3db1-4b62-00009ae4e095.png)


### 2.3. `Coveralls.io`と`Github`の各リポジトリを紐づけする

[このページ](https://coveralls.io/repos/new)に行くと、`Github`の個人リポジトリの一覧が表示されるので、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/b6f8d095-1736-1509-5bec-e8ca8d778115.png)

`coverage-badge`を出力したいリポジトリ（今いじっているリポジトリ）を OFF → ON に切り替える。まだリモートリポジトリを作って無いのならこのタイミング`Github`に作成しちゃいましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/52ccf846-de47-ca21-1262-ffb9cf5eb079.png)

### 2.4. install

```git:gitbash
$ npm install --save-dev coveralls
```

### 2.5. npm run script を作成する

`coveralls`が読み込み可能な`coverage-report`が出力される様にする必要がある。

```diff_json:package.json
{
  "scripts":{
    "test": "nyc mocha -R spec",
+   "precoverage": "rm -rf coverage && mkdir coverage",
+   "coverage": "npm run test && nyc report --reporter=lcovonly"
  }
}
```

:::note warn
解説
* `coverage-report`は`coveralls`の仕様上`./coverage/lcov.info`ファイルに`text-lcov`形式で出力される必要がある。ファイルパスが一致しない場合は`Error: Lcov file not found.`を出力して終了する。
* `nyc`の`reporter`はファイル形式、出力ディレクトリの指定はできるが、ファイル名の指定が出来ない。また、出力場所を指定したとしてもそのディレクトリが無い場合、エラーを出力して終了する。
* 上記の理由から、まず出力場所である`./coverage`ディレクトリを作成する必要がある。`rm -rf coverage && mkdir coverage`
  * `MakeFile`でやれ的な説明が多いが、コマンドラインから直接作っても動作した
* `node-coveralls`の[github](https://github.com/nickmerwin/node-coveralls#nyc)には`nyc npm test && nyc report --reporter=text-lcov | coveralls`と書かれているが、それだと`"Error : Failed to parse string`が出力され終了してしまう。

```bash:gitbash
[error] "2022-11-22T09:33:14.585Z"  'error from lcovParse: ' 'Failed to parse string'
[error] "2022-11-22T09:33:14.588Z"  'input: ' ''
[error] "2022-11-22T09:33:14.588Z"  'error from convertLcovToCoveralls'

C:\Users\[user]\workSpace\coverage-badge-test\node_modules\coveralls\bin\coveralls.js:19
      throw err;
      ^
Failed to parse string
(Use `node --trace-uncaught ...` to show where the exception was thrown)
```

* なので、`npm script`では`reporter`の出力までを行い、あとは`github-actions`に任せるのが良い。
:::

### 2.6. `reporter`の実行確認

以下を実行

```bash:gitbash
$ npm run coverage-reporter
```

以下の様になればOK。

```bash:gitbash
> rm -rf coverage && mkdir coverage

> npm run test && nyc report --reporter=lcovonly

> rm -rf .nyc_output

> nyc mocha -R spec

  test index
    ✔ index is function

  1 passing (6ms)

----------|---------|----------|---------|---------|-------------------
File      | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
----------|---------|----------|---------|---------|-------------------
All files |      50 |      100 |       0 |      50 |
 index.js |      50 |      100 |       0 |      50 | 2
----------|---------|----------|---------|---------|-------------------
```

:::note info
確認
`./coverage/lcov.info`ファイルが作成されているか確認する。

* :open_file_folder: coverage
  * :pencil: lcov.info

```info:lcov.info
TN:
SF:index.js
FN:1,(anonymous_0)
FNF:1
FNH:0
FNDA:0,(anonymous_0)
DA:1,1
DA:2,0
LF:2
LH:1
BRF:0
BRH:0
end_of_record
```
:::

## 3. `Github Actions`を作成する

本ページ内の[結論](#結論)に書いた`.github/workflow/coverage-badge.yml`。

### 3.1. `Github Action`を実行

Githubのリモートリポジトリに今いじっているローカルリポジトリをpushして確認。

1. リポジトリ内の上部にある「Actions」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/43191326-01aa-1ae5-81d8-e81b90990243.png)

1. Actionisページ内の左部にある「Coveralls Github Action」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/234cb10f-7663-1bfe-eff7-f5cc9bb03e53.png)

1. ページ右部の「Run workflow▼」→「Run Workflow」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/4a7e18e9-7ccf-bb9c-2df5-c975dc523cd6.png)


1. 経過ログも見れるし、終了した後でもそのログを参照可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/bf7fa38e-31b7-dc49-4e24-e751a128790b.png)


## 4. README にバッジを埋め込む

本ページ内の[結論](#結論)に書いた`README.md`。

`README`に貼り付けるMarkdownは、`coveralls.io`の`REPOS`のページ内にある`BUILD #***`のリンクを踏んだ先にある各ビルド毎の`coverage`詳細ページにある「EMBED▼」ボタンからジェネレートできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/3465f30a-8c77-7a9d-459b-a0595e0cd2dd.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/f9803077-ac67-0cd4-2acd-853e8c45dd54.png)


こんな感じになればOK
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/682590/40a7d0ea-6f01-e383-5843-24f19aef7945.png)

了

# 参考リンク

https://github.com/istanbuljs/nyc#installation--usage

https://github.com/nickmerwin/node-coveralls

https://docs.github.com/en/actions/using-workflows/about-workflows

https://github.com/actions/setup-node

https://github.com/coverallsapp/github-action