├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── .gitignore ├── .husky └── pre-commit ├── LICENSE ├── README.md ├── action.yml ├── bin └── cli ├── dist └── action.js ├── package.json ├── renovate.json ├── src ├── action.ts ├── badges.ts ├── create.ts └── index.ts ├── test ├── index.test.ts └── sample-logo.svg └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: jaywcjlove 2 | buy_me_a_coffee: jaywcjlove 3 | custom: ["https://www.paypal.me/kennyiseeyou", "https://jaywcjlove.github.io/#/sponsor"] 4 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | env: 8 | CI: false 9 | 10 | jobs: 11 | build-deploy: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: write 15 | id-token: write 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: actions/setup-node@v4 19 | with: 20 | node-version: 20 21 | registry-url: 'https://registry.npmjs.org' 22 | 23 | - run: npm install 24 | - run: npm run build 25 | - run: npm run coverage 26 | - run: npm run make-badges 27 | - run: pwd 28 | - run: ls -al 29 | - run: ls -al coverage 30 | 31 | 32 | - name: Create idoc config 33 | run: | 34 | cat > idoc.yml << EOF 35 | site: "Coverage Badges {{version}}" 36 | menus: 37 | Home: index.html 38 | footer: | 39 | Sponsor • 40 | Create Tag • 41 | Contributors • 42 | Read File Content • 43 | Generated Badges 44 |
45 | Released under the MIT License. Copyright © {{idocYear}} Kenny Wong
46 | Generated by idoc v{{idocVersion}} 47 | 48 | EOF 49 | 50 | - run: npm install idoc@1 -g 51 | - run: idoc 52 | - run: cp -rp coverage/lcov-report dist 53 | 54 | - run: cat coverage/coverage-summary.json 55 | 56 | - name: Create Coverage Badges 57 | uses: ./ 58 | # uses: jaywcjlove/coverage-badges-cli@main 59 | with: 60 | style: flat 61 | label: flat style 62 | source: coverage/coverage-summary.json 63 | output: dist/badges-flat.svg 64 | 65 | - name: Create Coverage Badges 66 | uses: jaywcjlove/coverage-badges-cli@main 67 | id: coverage_badges 68 | with: 69 | label: classic style 70 | source: coverage/coverage-summary.json 71 | output: dist/badges-classic.svg 72 | 73 | - name: Create Defalut Coverage Badges 74 | uses: jaywcjlove/coverage-badges-cli@main 75 | with: 76 | source: coverage/coverage-summary.json 77 | output: dist/badges.svg 78 | 79 | - name: Generate Contributors Images 80 | uses: jaywcjlove/github-action-contributors@main 81 | with: 82 | filter-author: (renovate\[bot\]|renovate-bot|dependabot\[bot\]) 83 | output: dist/CONTRIBUTORS.svg 84 | avatarSize: 42 85 | 86 | - run: echo '${{ steps.coverage_badges.outputs.svg }}' 87 | 88 | # - run: npm i markdown-to-html-cli -g 89 | # - run: markdown-to-html --output coverage/index.html --github-corners https://github.com/jaywcjlove/coverage-badges-cli 90 | 91 | - name: Create Tag 92 | id: create_tag 93 | uses: jaywcjlove/create-tag-action@main 94 | with: 95 | package-path: ./package.json 96 | 97 | - name: get tag version 98 | id: tag_version 99 | uses: jaywcjlove/changelog-generator@main 100 | 101 | - name: Deploy 102 | uses: peaceiris/actions-gh-pages@v4 103 | if: github.ref == 'refs/heads/main' 104 | with: 105 | commit_message: ${{steps.tag_version.outputs.tag}} ${{ github.event.head_commit.message }} 106 | github_token: ${{ secrets.GITHUB_TOKEN }} 107 | publish_dir: ./dist 108 | 109 | - name: Generate Changelog 110 | id: changelog 111 | uses: jaywcjlove/changelog-generator@main 112 | with: 113 | token: ${{ secrets.GITHUB_TOKEN }} 114 | head-ref: ${{steps.create_tag.outputs.version}} 115 | filter-author: (小弟调调™|Renovate Bot) 116 | filter: '[R|r]elease[d]\s+[v|V]\d(\.\d+){0,2}' 117 | 118 | - name: Create Release 119 | uses: ncipollo/release-action@v1 120 | if: steps.create_tag.outputs.successful 121 | with: 122 | allowUpdates: true 123 | name: ${{ steps.create_tag.outputs.version }} 124 | tag: ${{ steps.create_tag.outputs.version }} 125 | token: ${{ secrets.GITHUB_TOKEN }} 126 | body: | 127 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) [![](https://img.shields.io/badge/Open%20in-unpkg-blue)](https://uiwjs.github.io/npm-unpkg/#/pkg/coverage-badges-cli@${{steps.changelog.outputs.version}}/file/README.md) 128 | 129 | Documentation ${{ steps.changelog.outputs.tag }}: https://raw.githack.com/jaywcjlove/coverage-badges-cli/${{ steps.changelog.outputs.gh-pages-short-hash }}/index.html 130 | Comparing Changes: ${{ steps.changelog.outputs.compareurl }} 131 | 132 | ${{ steps.changelog.outputs.changelog }} 133 | 134 | - name: package.json info 135 | uses: jaywcjlove/github-action-package@main 136 | with: 137 | path: package.json 138 | unset: scripts,jest,lint-staged,devDependencies 139 | 140 | # - run: npm install @jsdevtools/npm-publish -g 141 | # - run: npm-publish --token="${{ secrets.NPM_TOKEN }}" ./package.json 142 | - run: npm publish --access public --provenance 143 | name: 📦 coverage-badges-cli publish to NPM 144 | continue-on-error: true 145 | env: 146 | NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/node 2 | # Edit at https://www.gitignore.io/?templates=node 3 | 4 | esm 5 | cjs 6 | lib 7 | package-lock.json 8 | __snapshots__ 9 | 10 | ### Node ### 11 | # Logs 12 | logs 13 | *.log 14 | npm-debug.log* 15 | yarn-debug.log* 16 | yarn-error.log* 17 | lerna-debug.log* 18 | 19 | # Diagnostic reports (https://nodejs.org/api/report.html) 20 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 21 | 22 | # Runtime data 23 | pids 24 | *.pid 25 | *.seed 26 | *.pid.lock 27 | 28 | # Directory for instrumented libs generated by jscoverage/JSCover 29 | lib-cov 30 | 31 | # Coverage directory used by tools like istanbul 32 | coverage 33 | 34 | # nyc test coverage 35 | .nyc_output 36 | 37 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 38 | .grunt 39 | 40 | # Bower dependency directory (https://bower.io/) 41 | bower_components 42 | 43 | # node-waf configuration 44 | .lock-wscript 45 | 46 | # Compiled binary addons (https://nodejs.org/api/addons.html) 47 | build/Release 48 | 49 | # Dependency directories 50 | node_modules/ 51 | jspm_packages/ 52 | 53 | # TypeScript v1 declaration files 54 | typings/ 55 | 56 | # Optional npm cache directory 57 | .npm 58 | 59 | # Optional eslint cache 60 | .eslintcache 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # next.js build output 79 | .next 80 | 81 | # nuxt.js build output 82 | .nuxt 83 | 84 | # vuepress build output 85 | .vuepress/dist 86 | 87 | # Serverless directories 88 | .serverless/ 89 | 90 | # FuseBox cache 91 | .fusebox/ 92 | 93 | # DynamoDB Local files 94 | .dynamodb/ 95 | 96 | # End of https://www.gitignore.io/api/node 97 | 98 | .DS_Store 99 | .cache 100 | .vscode 101 | .idea 102 | .env 103 | 104 | *.bak 105 | *.tem 106 | *.temp 107 | #.swp 108 | *.*~ 109 | ~*.* 110 | 111 | # IDEA 112 | *.iml 113 | *.ipr 114 | *.iws 115 | .idea/ -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | npx --no lint-staged -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 小弟调调 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | coverage-badges-cli 2 | === 3 | 4 | 5 | [![Buy me a coffee](https://img.shields.io/badge/Buy%20me%20a%20coffee-048754?logo=buymeacoffee)](https://jaywcjlove.github.io/#/sponsor) 6 | [![Build & Deploy](https://github.com/jaywcjlove/coverage-badges-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/jaywcjlove/coverage-badges-cli/actions/workflows/ci.yml) 7 | [![Coverage Status](https://jaywcjlove.github.io/coverage-badges-cli/badges.svg)](https://jaywcjlove.github.io/coverage-badges-cli/lcov-report/) 8 | [![npm version](https://img.shields.io/npm/v/coverage-badges-cli.svg)](https://www.npmjs.com/package/coverage-badges-cli) 9 | [![Download NPM](https://img.shields.io/npm/dm/coverage-badges-cli.svg?style=flat)](https://www.npmjs.com/package/coverage-badges-cli/) 10 | 11 | Create coverage badges from coverage reports. Using GitHub Actions and GitHub Workflow CPU time (no 3rd parties servers). 12 | 13 | Don't worry about the [coverage.io](https://coveralls.io/) service is down. 14 | 15 | ## Install 16 | 17 | ```shell 18 | $ npm i coverage-badges-cli 19 | ``` 20 | 21 | ## Example 22 | 23 | ```js 24 | { 25 | "scripts": { 26 | "coverage": "jest --coverage" 27 | "make-badges": "coverage-badges", 28 | }, 29 | "jest": { 30 | "collectCoverageFrom": [ 31 | "/packages/**/*.{tsx,ts}", 32 | "!**/*.{js,d.ts}" 33 | ], 34 | "coverageReporters": [ 35 | "lcov", 36 | "json-summary" 37 | ], 38 | } 39 | } 40 | ``` 41 | 42 | This config creates a coverage badge in a default directory ./badges. 43 | 44 | You can add `![Coverage](./coverage/badges.svg)` to your README.md after the badge creation. 45 | 46 | ## Github Actions 47 | 48 | ### Input Parameters 49 | 50 | ![](https://jaywcjlove.github.io/coverage-badges-cli/badges-classic.svg) 51 | ![](https://jaywcjlove.github.io/coverage-badges-cli/badges-flat.svg) 52 | 53 | - `source` - The path of the target file "coverage-summary.json". 54 | - `output` - Output image path. 55 | - `label` - The left label of the badge, usually static (default `coverage`). 56 | - `labelColor` - \ or \ (default: `555`). 57 | - `color` - \ or \ (default: '') 58 | - `scale` - Set badge scale (default: `1`). 59 | - `style` - Badges style: `flat`, `classic` (default `classic`). 60 | - ~~`type`~~ - (No longer supported after v1.2.0) Coverage report type: `lines`, `statements`, `functions`, `branches` (default `statements`) 61 | - `jsonPath` - Path to the coverage percentage number to be used in the badge (default `total.statements.pct`) 62 | - `icon` - Path to icon file 63 | 64 | ```yml 65 | - name: Create Coverage Badges 66 | uses: jaywcjlove/coverage-badges-cli@main 67 | with: 68 | style: flat 69 | source: coverage/coverage-summary.json 70 | output: coverage/badges.svg 71 | jsonPath: totals.percent_covered 72 | 73 | - name: Deploy 74 | uses: peaceiris/actions-gh-pages@v3 75 | with: 76 | github_token: ${{ secrets.GITHUB_TOKEN }} 77 | publish_dir: ./build 78 | ``` 79 | 80 | ### Output Parameters 81 | 82 | svg svg image string: ` or (default: '555') 134 | --color or (default: '') 135 | 136 | Example: 137 | 138 | npm coverage-badges-cli --output coverage/badges.svg 139 | npm coverage-badges-cli --style plastic 140 | npm coverage-badges-cli --source coverage/coverage-summary.json 141 | npm coverage-badges-cli --labelColor ADF 142 | ``` 143 | 144 | ## Development 145 | 146 | ```bash 147 | $ npm i 148 | $ npm run build 149 | $ npm run watch 150 | ``` 151 | 152 | ## See also 153 | 154 | - [Github Release Changelog](https://github.com/jaywcjlove/changelog-generator) Generator A GitHub Action that compares the commit differences between two branches 155 | - [Create Tags From](https://github.com/jaywcjlove/create-tag-action) Auto create tags from commit or package.json. 156 | - [Github Action Contributors](https://github.com/jaywcjlove/github-action-contributors) Github action generates dynamic image URL for contributor list to display it! 157 | - [Create Coverage Badges](https://github.com/jaywcjlove/coverage-badges-cli) Create coverage badges from coverage reports. (no 3rd parties servers) 158 | - [Generated Badges](https://github.com/jaywcjlove/generated-badges) Create a badge using GitHub Actions and GitHub Workflow CPU time (no 3rd parties servers) 159 | 160 | ## Contributors 161 | 162 | As always, thanks to our amazing contributors! 163 | 164 | 165 | 166 | 167 | 168 | Made with [contributors](https://github.com/jaywcjlove/github-action-contributors). 169 | 170 | ## License 171 | 172 | MIT © [Kenny Wong](https://wangchujiang.com/) 173 | -------------------------------------------------------------------------------- /action.yml: -------------------------------------------------------------------------------- 1 | name: 'Create Coverage Badges' 2 | author: 'Kenny Wong' 3 | description: 'Create coverage badges from coverage reports. Using GitHub Actions and GitHub Workflow CPU time (no 3rd parties servers).' 4 | inputs: 5 | source: 6 | description: The path of the target file "coverage-summary.json". 7 | default: coverage/coverage-summary.json 8 | required: false 9 | 10 | output: 11 | description: Output image path. 12 | default: coverage/badges.svg 13 | required: false 14 | 15 | label: 16 | description: The left label of the badge, usually static. 17 | default: coverage 18 | required: false 19 | 20 | style: 21 | description: 'Badges style: flat, classic.' 22 | default: classic 23 | required: false 24 | 25 | scale: 26 | description: 'Set badge scale (default: 1)' 27 | default: "1" 28 | required: false 29 | 30 | color: 31 | description: " or (default: '555')" 32 | required: false 33 | 34 | labelColor: 35 | description: " or (default: '')" 36 | default: "555" 37 | required: false 38 | 39 | jsonPath: 40 | description: 'Path to the coverage percentage number to be used in the badge' 41 | default: total.lines.pct 42 | required: false 43 | 44 | outputs: 45 | svg: 46 | description: 'svg image string' 47 | 48 | runs: 49 | using: 'node20' 50 | main: 'dist/action.js' 51 | 52 | branding: 53 | icon: 'list' 54 | color: 'blue' 55 | -------------------------------------------------------------------------------- /bin/cli: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require('../lib/index.js').default(); -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "coverage-badges-cli", 3 | "version": "2.1.0", 4 | "description": "Create coverage badges from coverage reports. Using GitHub Actions and GitHub Workflow CPU time (no 3rd parties servers).", 5 | "homepage": "https://jaywcjlove.github.io/coverage-badges-cli/", 6 | "funding": "https://jaywcjlove.github.io/#/sponsor", 7 | "license": "MIT", 8 | "bin": { 9 | "coverage-badges": "bin/cli", 10 | "coverage-badges-cli": "bin/cli" 11 | }, 12 | "scripts": { 13 | "prepare": "husky && npm run package", 14 | "package": "ncc build src/action.ts", 15 | "make-badges": "node bin/cli", 16 | "watch": "tsbb watch", 17 | "build": "tsbb build", 18 | "test": "tsbb test", 19 | "coverage": "tsbb test --coverage" 20 | }, 21 | "files": [ 22 | "bin", 23 | "lib", 24 | "src" 25 | ], 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/jaywcjlove/coverage-badges-cli" 29 | }, 30 | "keywords": [ 31 | "coverage", 32 | "coverage-badges", 33 | "coverage-badges-cli", 34 | "badges" 35 | ], 36 | "jest": { 37 | "coverageReporters": [ 38 | "lcov", 39 | "json-summary" 40 | ] 41 | }, 42 | "engines": { 43 | "node": ">=v20.11.0", 44 | "npm": ">=10.2.4" 45 | }, 46 | "lint-staged": { 47 | "*.ts?(x)": [ 48 | "npm run package" 49 | ] 50 | }, 51 | "dependencies": { 52 | "@types/fs-extra": "~11.0.0", 53 | "@types/minimist": "~1.2.2", 54 | "badgen": "~3.2.3", 55 | "fs-extra": "~11.2.0", 56 | "lodash.get": "^4.4.2", 57 | "mini-svg-data-uri": "^1.4.4", 58 | "minimist": "~1.2.5" 59 | }, 60 | "devDependencies": { 61 | "@actions/core": "^1.10.0", 62 | "@kkt/ncc": "^1.0.15", 63 | "@types/lodash.get": "^4.4.9", 64 | "husky": "^9.0.11", 65 | "lint-staged": "^15.2.2", 66 | "tsbb": "^4.1.3" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": [ 3 | "config:base" 4 | ], 5 | "packageRules": [ 6 | { 7 | "matchPackagePatterns": ["*"], 8 | "rangeStrategy": "replace" 9 | } 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/action.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import fs from 'fs-extra'; 3 | import { setFailed, getInput, setOutput, info, startGroup, endGroup } from '@actions/core'; 4 | import { badge, BadgeOption } from './badges'; 5 | 6 | ;(async () => { 7 | try { 8 | const { version } = require('../package.json'); 9 | info(`coverage-badges-cli v\x1b[32;1m${version}\x1b[0m`); 10 | const output = path.resolve(process.cwd(), getInput('output') || 'coverage/badges.svg'); 11 | const source = path.resolve(process.cwd(), getInput('source') || 'coverage/coverage-summary.json'); 12 | const label = getInput('label') || 'coverage'; 13 | const labelColor = getInput('labelColor') || '555'; 14 | const color = getInput('color'); 15 | const scale = Number(getInput('scale') || 1) || 1; 16 | const jsonPath = getInput('jsonPath') || 'total.statements.pct'; 17 | const style = (getInput('style') || 'classic') as BadgeOption['style']; 18 | fs.ensureDirSync(path.dirname(output)); 19 | if (!fs.existsSync(source)) { 20 | setFailed(`File \x1b[31m${source}\x1b[0m does not exist.\n please specify the file directory\n\x1b[35mnpm\x1b[0m coverage-badges-cli \x1b[33m--source\x1b[0m coverage/coverage-summary.json`); 21 | return 22 | } 23 | info(`Source Path: \x1b[32;1m${source}\x1b[0m`); 24 | info(`Output Path: \x1b[32;1m${output}\x1b[0m`); 25 | 26 | const sourceData: object = fs.readJSONSync(source); 27 | startGroup(`Source Path: \x1b[32;1m${source}\x1b[0m`); 28 | info(`${JSON.stringify(sourceData, null, 2)}`); 29 | endGroup(); 30 | 31 | const svgStr = badge({ label, labelColor, color, style, jsonPath, scale } as BadgeOption, sourceData); 32 | 33 | setOutput('svg', svgStr); 34 | 35 | startGroup(`SVG String: \x1b[32;1m${output}\x1b[0m`); 36 | info(`${svgStr}`); 37 | endGroup(); 38 | 39 | fs.writeFileSync(output, svgStr); 40 | info(`\nCreate Coverage Badges: \x1b[32;1m${path.relative(process.cwd(), output)}\x1b[0m\n`); 41 | } catch (error) { 42 | setFailed(error.message); 43 | } 44 | })(); -------------------------------------------------------------------------------- /src/badges.ts: -------------------------------------------------------------------------------- 1 | import { badgen } from 'badgen'; 2 | import { readFileSync } from 'fs'; 3 | import svgToTinyDataUri from 'mini-svg-data-uri'; 4 | import get from 'lodash.get'; 5 | 6 | // Copied from `badgen` because it's not exported 7 | export type StyleOption = 'flat' | 'classic'; 8 | 9 | export interface BadgenOptions { 10 | status: string; 11 | subject?: string; 12 | color?: string; 13 | label?: string; 14 | style?: StyleOption; 15 | jsonPath?: string; 16 | labelColor?: string; 17 | icon?: string; 18 | iconWidth?: number; 19 | scale?: number; 20 | } 21 | 22 | export interface BadgeOption extends BadgenOptions {} 23 | 24 | const getIconString = (path: string) => { 25 | return readFileSync(path, 'utf8'); 26 | } 27 | 28 | type ColorData = Record; 29 | 30 | const defaultColorData: ColorData = { 31 | '#49c31a': [100], 32 | '#97c40f': [99.99, 90], 33 | '#a0a127': [89.99, 80], 34 | '#cba317': [79.99, 60], 35 | '#ce0000': [59.99, 0], 36 | } 37 | 38 | export function badge(option: BadgeOption, summary: object) { 39 | const { label = 'coverage', style = 'classic', jsonPath = 'total.statements.pct', color: optionColor, ...otherOption } = (option || {}) as BadgenOptions 40 | let pct: any = summary; 41 | pct = get(summary, jsonPath, 0); 42 | 43 | if (!isNaN(Number(pct))) { 44 | pct = Number(pct); 45 | } 46 | 47 | if (typeof pct !== 'number') { 48 | throw new Error(`${jsonPath} evaluates to ${JSON.stringify(pct)} and is not a suitable path in the JSON coverage data`); 49 | } 50 | const colorData = defaultColorData 51 | const color = Object.keys(colorData).find((value: keyof typeof colorData, idx) => { 52 | if (colorData[value].length === 1 && pct >= colorData[value][0]) { 53 | return true 54 | } 55 | if (colorData[value].length === 2 && pct <= colorData[value][0] && pct >= colorData[value][1]) { 56 | return true 57 | } 58 | return false; 59 | }); 60 | 61 | const badgenArgs: BadgenOptions = { 62 | ...otherOption, 63 | style, 64 | label, 65 | status: `${pct < 0 ? 'Unknown' : `${pct}%`}`, 66 | color: (color || 'e5e5e5').replace(/^#/, ''), 67 | }; 68 | 69 | if (optionColor) { 70 | badgenArgs.color = optionColor.replace(/^#/, ''); 71 | } 72 | 73 | if(option.icon) { 74 | const svgString = getIconString(option.icon) as string; 75 | const svgDataUri = svgToTinyDataUri(svgString); 76 | 77 | badgenArgs.icon = svgDataUri; 78 | } 79 | 80 | console.log("badgenArgs", badgenArgs) 81 | return badgen(badgenArgs); 82 | } -------------------------------------------------------------------------------- /src/create.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs-extra'; 2 | import path from 'path'; 3 | import { RunArgvs } from '.'; 4 | import { badge, BadgenOptions } from './badges'; 5 | 6 | export function create(argvs: RunArgvs) { 7 | const sourcePath = path.resolve(process.cwd(), argvs.source); 8 | const svgPath = path.resolve(process.cwd(), argvs.output); 9 | if (!fs.existsSync(sourcePath)) { 10 | console.log( 11 | `\x1b[31mErr Source Path:\x1b[0m ${path.relative(process.cwd(), sourcePath)}\n`, 12 | `> please specify the file directory\n`, 13 | `> \x1b[35mnpm\x1b[0m coverage-badges-cli \x1b[33m--source\x1b[0m coverage/coverage-summary.json` 14 | ); 15 | return; 16 | } 17 | const source: object = require(sourcePath); 18 | const svgStr = badge(argvs as BadgenOptions, source); 19 | fs.ensureDirSync(path.dirname(svgPath)); 20 | fs.writeFileSync(svgPath, svgStr); 21 | console.log(`\nCoverage Badges: \x1b[32;1m${path.relative(process.cwd(), svgPath)}\x1b[0m\n`); 22 | } -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import minimist, { ParsedArgs } from 'minimist'; 2 | import { create } from './create'; 3 | import { BadgeOption } from './badges'; 4 | 5 | export interface RunArgvs extends ParsedArgs, Partial { 6 | source?: string; 7 | version?: string; 8 | output?: string; 9 | jsonPath?: string; 10 | /// or (default: '555') 11 | labelColor?: string; 12 | } 13 | 14 | export default function run() { 15 | const argvs: RunArgvs = minimist(process.argv.slice(2), { 16 | alias: { 17 | help: 'h', 18 | source: 's', 19 | output: 'o', 20 | }, 21 | default: { 22 | style: 'classic', 23 | source: 'coverage/coverage-summary.json', 24 | output: 'coverage/badges.svg', 25 | jsonPath: 'total.statements.pct', 26 | }, 27 | }); 28 | if (argvs.h || argvs.help) { 29 | cliHelp() 30 | exampleHelp() 31 | return; 32 | } 33 | const { version } = require('../package.json'); 34 | if (argvs.v || argvs.version) { 35 | console.log(`\n coverage-badges-cli v${version}\n`); 36 | return; 37 | } 38 | console.log("argvs: ", argvs); 39 | create(argvs); 40 | } 41 | 42 | 43 | export function cliHelp() { 44 | console.log('\n Usage: coverage-badges [options] [--help|h]'); 45 | console.log('\n Options:\n'); 46 | console.log(' --version, -v ','Show version number'); 47 | console.log(' --help, -h ','Displays help information.'); 48 | console.log(' --output, -o ','Output directory.'); 49 | console.log(' --source, -s ','The path of the target file "coverage-summary.json".'); 50 | console.log(' --style ','Badges style: flat, flat-square.'); 51 | console.log(' --type ','Coverage type: lines, statements, functions, branches.'); 52 | console.log(' --scale ','Set badge scale (default: 1)'); 53 | console.log(' --icon ','Path to icon file'); 54 | console.log(' --iconWidth ','Set this if icon is not square (default: 13)'); 55 | console.log(' --label ','The left label of the badge, usually static (default `coverage`).'); 56 | console.log(' --labelColor ',' or (default: "555")'); 57 | console.log(' --color ',' or (default: "")'); 58 | } 59 | 60 | export function exampleHelp() { 61 | console.log('\n Example:\n'); 62 | console.log(' \x1b[35mnpm\x1b[0m coverage-badges-cli \x1b[33m--output\x1b[0m coverage/badges.svg'); 63 | console.log(' \x1b[35mnpm\x1b[0m coverage-badges-cli \x1b[33m--style\x1b[0m plastic'); 64 | console.log(' \x1b[35mnpm\x1b[0m coverage-badges-cli \x1b[33m--source\x1b[0m coverage/coverage-summary.json'); 65 | console.log(' \x1b[35mnpm\x1b[0m coverage-badges-cli \x1b[33m--labelColor\x1b[0m ADF'); 66 | console.log('\n'); 67 | } -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | import FS from 'fs-extra'; 2 | import path from 'path'; 3 | import run from '../src'; 4 | import { badge } from '../src/badges'; 5 | import { create } from '../src/create'; 6 | import { cliHelp, exampleHelp } from '../src'; 7 | 8 | const mockSummary = { 9 | total: { 10 | lines: { 11 | total: 60, 12 | covered: 30, 13 | skipped: 2, 14 | pct: 100, 15 | }, 16 | statements: { 17 | total: 45, 18 | covered: 25, 19 | skipped: 0, 20 | pct: 85, 21 | }, 22 | functions: { 23 | total: 50, 24 | covered: 35, 25 | skipped: 4, 26 | pct: 90, 27 | }, 28 | branches: { 29 | total: 50, 30 | covered: 35, 31 | skipped: 4, 32 | pct: 95, 33 | }, 34 | }, 35 | }; 36 | 37 | it('test run case', async () => { 38 | const summary = path.resolve(process.cwd(), 'coverage/coverage-summary.json'); 39 | FS.ensureDirSync(path.dirname(summary)); 40 | FS.writeJsonSync(summary, mockSummary) 41 | expect(run()).toBeUndefined(); 42 | const output = path.resolve(process.cwd(), 'coverage/badges.svg'); 43 | const exists = FS.existsSync(output); 44 | expect(exists).toBeTruthy(); 45 | const dirs = await FS.readdir(path.join(process.cwd(), 'coverage')); 46 | // expect(dirs).toEqual(['badges.svg', 'coverage-summary.json', 'lcov-report', 'lcov.info']); 47 | expect(dirs).toEqual(expect.arrayContaining(['badges.svg', 'coverage-summary.json'])); 48 | }); 49 | 50 | it('test badge case - default', async () => { 51 | const str = badge({ style: 'flat', status: '85%' }, mockSummary as any); 52 | 53 | expect(str.indexOf(`85%`) > 0).toBeTruthy(); 54 | expect(str.indexOf(`coverage`) > 0).toBeTruthy(); 55 | }); 56 | 57 | it('test badge case - statements', async () => { 58 | const str = badge({ style: 'flat', status: '85%', jsonPath: 'total.statements.pct' }, mockSummary as any); 59 | 60 | expect(str.indexOf(`85%`) > 0).toBeTruthy(); 61 | }); 62 | 63 | it('test badge case - lines', async () => { 64 | const str = badge({ style: 'flat', status: '100%', jsonPath: 'total.lines.pct' }, mockSummary as any); 65 | 66 | expect(str.indexOf(`100%`) > 0).toBeTruthy(); 67 | }); 68 | 69 | it('test badge case - functions', async () => { 70 | const str = badge({ style: 'flat', status: '90%', jsonPath: 'total.functions.pct' }, mockSummary as any); 71 | 72 | expect(str.indexOf(`90%`) > 0).toBeTruthy(); 73 | }); 74 | 75 | it('test badge case - branches', async () => { 76 | const str = badge({ style: 'flat', status: '95%', jsonPath: 'total.branches.pct' }, mockSummary as any); 77 | 78 | expect(str.indexOf(`95%`) > 0).toBeTruthy(); 79 | }); 80 | 81 | it('test badge case - custom label', async () => { 82 | const customLabel = "Custon Label"; 83 | const str = badge({ style: 'flat', status: '85%', label: customLabel }, mockSummary as any); 84 | 85 | expect(str.indexOf(`${customLabel}`) > 0).toBeTruthy(); 86 | }); 87 | 88 | it('test badge case - label color', async () => { 89 | const customLabel = "Custon Label"; 90 | const str = badge({ style: 'flat', status: '85%', labelColor: "ADF", label: customLabel }, mockSummary as any); 91 | expect(str.indexOf(`${customLabel}`) > 0).toBeTruthy(); 92 | expect(str.indexOf(``) > 0).toBeTruthy(); 93 | }); 94 | 95 | it('test badge case - color', async () => { 96 | const customLabel = "Custon Label"; 97 | const str = badge({ style: 'flat', status: '85%', color: "ADF", label: customLabel }, mockSummary as any); 98 | expect(str.indexOf(`${customLabel}`) > 0).toBeTruthy(); 99 | expect(str.indexOf(``) > 0).toBeTruthy(); 100 | }); 101 | 102 | it('test badge case - custom icon', async () => { 103 | const customIcon = "./test/sample-logo.svg"; 104 | const processedIconString = ``; 105 | // const processedIconString = ``; 106 | const str = badge({ style: 'flat', status: '85%', icon: customIcon }, mockSummary as any); 107 | expect(str.indexOf(processedIconString) > 0).toBeTruthy(); 108 | }); 109 | 110 | console.log = jest.fn(); 111 | it('test create case', async () => { 112 | const str = create({ style: 'flat', status: '85%', _: [], source: 'coverage2/coverage-summary.json', output: 'coverage2/svg.svg' }); 113 | 114 | // @ts-ignore 115 | expect(console.log.mock.calls[0][0]).toBe('\x1b[31mErr Source Path:\x1b[0m coverage2/coverage-summary.json\n'); 116 | // @ts-ignore 117 | expect(console.log.mock.calls[0][1]).toBe('> please specify the file directory\n'); 118 | expect(str).toBeUndefined(); 119 | }); 120 | 121 | it('test cliHelp/exampleHelp case', async () => { 122 | expect(cliHelp()).toBeUndefined(); 123 | expect(exampleHelp()).toBeUndefined(); 124 | }); 125 | -------------------------------------------------------------------------------- /test/sample-logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "esModuleInterop": true, 5 | "declaration": true, 6 | "target": "es2017", 7 | "noImplicitAny": true, 8 | "resolveJsonModule": true, 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "sourceMap": true, 13 | "strict": false, 14 | "skipLibCheck": true, 15 | "outDir": "lib", 16 | "baseUrl": "." 17 | }, 18 | "include": ["src/**/*"] 19 | } 20 | --------------------------------------------------------------------------------