├── coverage.shield.badge.md ├── circle.yml ├── codecov.yml ├── .gitignore ├── LICENSE ├── package.json ├── update-readme-with-shield-badge.js ├── README.md ├── istanbul-reporter-shield-badge.js └── test └── istanbul-reporter-shield-badge.spec.js /coverage.shield.badge.md: -------------------------------------------------------------------------------- 1 | https://img.shields.io/badge/Local%20Coverage-100%25-brightgreen.svg 2 | ![Local Coverage-shield-badge-1](https://img.shields.io/badge/Local%20Coverage-100%25-brightgreen.svg) -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | timezone: 3 | America/Montreal 4 | node: 5 | version: 6 | 7.1.0 7 | test: 8 | post: 9 | - bash <(curl -s https://codecov.io/bash) -t $CODECOV_TOKEN 10 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | codecov: 2 | notify: 3 | require_ci_to_pass: true 4 | comment: 5 | behavior: default 6 | layout: header, diff 7 | require_changes: false 8 | coverage: 9 | precision: 2 10 | range: 11 | - 70.0 12 | - 100.0 13 | round: down 14 | status: 15 | changes: false 16 | patch: true 17 | project: true 18 | parsers: 19 | gcov: 20 | branch_detection: 21 | conditional: true 22 | loop: true 23 | macro: false 24 | method: false 25 | javascript: 26 | enable_partials: false 27 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # nyc test coverage 18 | .nyc_output 19 | 20 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 21 | .grunt 22 | 23 | # node-waf configuration 24 | .lock-wscript 25 | 26 | # Compiled binary addons (http://nodejs.org/api/addons.html) 27 | build/Release 28 | 29 | # Dependency directories 30 | node_modules 31 | jspm_packages 32 | 33 | # Optional npm cache directory 34 | .npm 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 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 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "istanbul-reporter-shield-badge", 3 | "version": "1.2.1", 4 | "description": "", 5 | "main": "istanbul-reporter-shield-badge.js", 6 | "scripts": { 7 | "test": "node_modules/mocha/bin/_mocha test", 8 | "testlocal": "node_modules/mocha/bin/_mocha test && node update-readme-with-shield-badge.js", 9 | "testwatch": "mocha test -w" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+ssh://git@github.com/Elgolfin/istanbul-reporter-shield-badge.git" 14 | }, 15 | "keywords": [ 16 | "istanbul", 17 | "reporter", 18 | "shield", 19 | "badge", 20 | "markdown", 21 | "karma", 22 | "shields.io" 23 | ], 24 | "author": "Elgolfin", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/Elgolfin/istanbul-reporter-shield-badge/issues" 28 | }, 29 | "homepage": "https://github.com/Elgolfin/istanbul-reporter-shield-badge#readme", 30 | "devDependencies": { 31 | "chai": "^3.5.0", 32 | "istanbul": "^0.4.5", 33 | "lodash.includes": "^4.3.0", 34 | "mocha": "^3.2.0", 35 | "sinon": "^1.17.7", 36 | "sinon-chai": "^2.8.0" 37 | }, 38 | "peerDependencies": { 39 | "istanbul": "^0.4.5", 40 | "lodash.includes": "^4.3.0" 41 | }, 42 | "dependencies": { 43 | "istanbul": "^0.4.5", 44 | "lodash.includes": "^4.3.0" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /update-readme-with-shield-badge.js: -------------------------------------------------------------------------------- 1 | /* ************** 2 | The only purpose of this script is to be used by the npm test script to write the shield badge within the README.md file 3 | ************** */ 4 | 5 | var fs = require('fs') 6 | var path = require('path') 7 | var istanbul = require('istanbul') 8 | var collector = new istanbul.Collector() 9 | var Report = istanbul.Report 10 | var shieldBadgeReporter = require('./istanbul-reporter-shield-badge') 11 | 12 | istanbul.Report.register(shieldBadgeReporter) 13 | var report = Report.create('shield-badge', { 14 | readmeFilename: 'README.md', 15 | readmeDir: path.resolve(__dirname, '.'), 16 | subject: 'Local Coverage' 17 | }) 18 | 19 | try { 20 | console.log('\n====================== Adding the badge to the ' + report.readmeFilename + ' =======================') 21 | var coverageDir = path.resolve(__dirname, 'coverage') 22 | fs.readdirSync(coverageDir).forEach(function (file) { 23 | if (file.indexOf('cov') === 0 && file.indexOf('.json') > 0) { 24 | collector.add(JSON.parse(fs.readFileSync(path.resolve(coverageDir, file), 'utf8'))) 25 | } 26 | }) 27 | report.on('done', function () { 28 | console.log('The istanbul shield badge report has been generated') 29 | }) 30 | report.writeReport(collector, true) 31 | } catch (err) { 32 | console.error(err.message) 33 | process.exit(1) 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Istanbul Reporter Shields.io Badge 2 | 3 | **Built for developers out there who do not want to (or cannot) rely on (and/or use) third-party services like http://codecov.io/ or http://coveralls.io/.** 4 | 5 | Generates a shields.io url representing the coverage of your tests suit. 6 | Also generates the markdown equivalent code. 7 | More importantly, it updates automatically your README.md file with the generated markdown each time you are running your tests. 8 | 9 | ## Project Health 10 | 11 | [![CircleCI](https://circleci.com/gh/Elgolfin/istanbul-reporter-shield-badge.svg?style=shield)](https://circleci.com/gh/Elgolfin/istanbul-reporter-shield-badge) ![Local Coverage-shield-badge-1](https://img.shields.io/badge/Local%20Coverage-100%25-brightgreen.svg) [![codecov](https://codecov.io/gh/Elgolfin/istanbul-reporter-shield-badge/branch/master/graph/badge.svg)](https://codecov.io/gh/Elgolfin/istanbul-reporter-shield-badge) ![dependencies](https://david-dm.org/Elgolfin/istanbul-reporter-shield-badge.svg) 12 | 13 | ## Install 14 | 15 | > npm install --save-dev istanbul-reporter-shield-badge 16 | 17 | ## Usage 18 | 19 | Register the report using the istanbul Report factory: 20 | 21 | ```javascript 22 | var shieldBadgeReporter = require('istanbul-reporter-shield-badge'); 23 | var istanbul = require('istanbul'); 24 | istanbul.Report.register(shieldBadgeReporter); 25 | ``` 26 | 27 | Create a report after istanbul has collected coverage information: 28 | 29 | ```javascript 30 | var report = require('istanbul').Report.create('shield-badge'); 31 | ``` 32 | 33 | Add to your karma config file: 34 | 35 | ```javascript 36 | config.set({ 37 | reporters: ['coverage'], 38 | coverageReporter: { 39 | dir: './coverage', 40 | reporters: [ 41 | { type: 'lcov', subdir: '.' }, 42 | { type: 'text-summary' }, 43 | { type: function() { 44 | var shieldBadgeReporter = require('istanbul-reporter-shield-badge') 45 | var istanbul = require('istanbul') 46 | istanbul.Report.register(shieldBadgeReporter) 47 | return 'shield-badge' 48 | }(), subdir: '.' } 49 | ] 50 | } 51 | }) 52 | ``` 53 | 54 | ### Config options 55 | |name |default |description | 56 | |--------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------| 57 | |file |coverage.shield.badge.md |the file where the url of the badge will be generated | 58 | |subject |coverage |the text that will appear on the left of the shield badge; cannot contain the character ']' (closed bracket) | 59 | |range |[50, 80] |must be a JavaScript Array representing medium and high levels of coverage | 60 | |coverageType |lines |must be one of: statements, lines, branches, functions; will be used to display the coverage percentage of the chosen type | 61 | |readmeFilename|null |the name of your readme file; **MUST BE readme.md (only) in any case** | 62 | |readmeDir |null |the **absolute** path of the folder where is located your readme.md file | 63 | 64 | 65 | N.b: if you supply the readmeFilename configuration, the reporter will add the markdown of the badge into your Readme.md file at the very beginning of the file. 66 | Once added, you are free to move the markdown code anywhere within the Readme.md file and the next time the reporter will run, 67 | it will automatically replace the previous badge with the new generated one. 68 | 69 | #### Standalone Usage Example 70 | 71 | Please refer to the [sample script](https://github.com/Elgolfin/istanbul-reporter-shield-badge/blob/master/update-readme-with-shield-badge.js). 72 | 73 | It was used in the package.json file as the 'npm test script' as follows: 74 | 75 | ```bash 76 | istanbul cover _mocha test && node update-readme-with-shield-badge.js 77 | ``` 78 | 79 | #### Karma Configuration File Example 80 | 81 | ```javascript 82 | config.set({ 83 | reporters: ['coverage'], 84 | coverageReporter: { 85 | dir: './coverage', 86 | reporters: [ 87 | { type: 'lcov', subdir: '.' }, 88 | { type: 'text-summary' }, 89 | { type: function() { 90 | var shieldBadgeReporter = require('istanbul-reporter-shield-badge') 91 | var istanbul = require('istanbul') 92 | istanbul.Report.register(shieldBadgeReporter) 93 | return 'shield-badge' 94 | }(), 95 | subdir: '.', 96 | coverageType: 'statements', 97 | range: [75, 90], 98 | subject: 'Code Coverage', 99 | readmeFilename: 'README.md', 100 | readmeDir: path.resolve(__dirname, '../..') // i.e. if karma.conf.js is located in test/unit from the root folder of your project 101 | } 102 | ] 103 | } 104 | }) 105 | ``` 106 | 107 | The example above will generate the following shields.io badge for a coverage percentage of 88% at the statements level: 108 | ![Code Coverage-shield-badge-](https://img.shields.io/badge/Code%20Coverage-88%25-yellow.svg) 109 | 110 | It will also include it in the README.md file located at the root of your project. 111 | 112 | ## LICENSE 113 | 114 | MIT License 115 | 116 | Copyright (c) 2017 117 | 118 | Permission is hereby granted, free of charge, to any person obtaining a copy 119 | of this software and associated documentation files (the "Software"), to deal 120 | in the Software without restriction, including without limitation the rights 121 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 122 | copies of the Software, and to permit persons to whom the Software is 123 | furnished to do so, subject to the following conditions: 124 | 125 | The above copyright notice and this permission notice shall be included in all 126 | copies or substantial portions of the Software. 127 | 128 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 129 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 130 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 131 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 132 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 133 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 134 | SOFTWARE. 135 | -------------------------------------------------------------------------------- /istanbul-reporter-shield-badge.js: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2017 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | 'use strict' 26 | 27 | var path = require('path') 28 | var fs = require('fs') 29 | var util = require('util') 30 | var istanbul = require('istanbul') 31 | var utils = require('istanbul').utils 32 | var _includes = require('lodash.includes') 33 | // var find = require("../find-in-files").find 34 | var Report = istanbul.Report 35 | var FileWriter = istanbul.FileWriter 36 | var acceptedTypes = ['lines', 'statements', 'functions', 'branches'] 37 | var colors = { 38 | low: 'red', 39 | medium: 'yellow', 40 | high: 'brightgreen' 41 | } 42 | 43 | /* 44 | https://img.shields.io/badge/--.svg 45 | Dashes -- → - Dash 46 | Underscores __ → _ Underscore 47 | _ or Space → Space 48 | 49 | colors: brightgreen green yellowgreen yellow orange red lightgrey blue ff69b4 (pink) 50 | 51 | */ 52 | 53 | function ShieldBadgeReporter (opts) { 54 | this.opts = opts || {} 55 | this.opts.dir = this.opts.dir || process.cwd() 56 | this.opts.file = this.opts.file || this.getDefaultConfig().file 57 | this.opts.writer = this.opts.writer || null 58 | this.subject = this.opts.subject || this.getDefaultConfig().subject 59 | if (typeof this.opts.coverageType === 'string' && _includes(acceptedTypes, this.opts.coverageType.toLowerCase())) { 60 | this.coverageType = this.opts.coverageType.toLowerCase() 61 | } else { 62 | this.coverageType = this.getDefaultConfig().coverageType 63 | } 64 | if (typeof this.opts.readmeFilename === 'string' && /readme\.md/i.test(this.opts.readmeFilename)) { 65 | this.readmeFilename = this.opts.readmeFilename 66 | } else { 67 | this.readmeFilename = null 68 | } 69 | this.readmeDir = this.opts.readmeDir || this.getDefaultConfig().readmeDir 70 | this.range = this.opts.range || this.getDefaultConfig().range 71 | } 72 | 73 | ShieldBadgeReporter.TYPE = 'shield-badge' 74 | util.inherits(ShieldBadgeReporter, Report) 75 | 76 | Report.mix(ShieldBadgeReporter, { 77 | synopsis: function() { 78 | return 'generates the url to get a shield.io badge representing the lines coverage percentage.' 79 | }, 80 | 81 | getDefaultConfig: function() { 82 | return { 83 | file: 'coverage.shield.badge.md', 84 | subject: 'coverage', 85 | range: [50, 80], 86 | readmeFilename: null, 87 | readmeDir: null, 88 | coverageType: 'lines' // Could be one of: statements, branches, functions, lines 89 | } 90 | }, 91 | 92 | generateBadgeUrl: function (metrics) { 93 | var level = metrics.pct >= this.range[1] ? 'high' : metrics.pct >= this.range[0] ? 'medium' : 'low' 94 | return 'https://img.shields.io/badge/' + encodeURIComponent(this.subject) + '-' + metrics.pct + '%25-' + colors[level] +'.svg' 95 | }, 96 | 97 | generateBadgeMarkdown: function (badgeUrl) { 98 | return '![' + this.subject + '-shield-badge-1](' + badgeUrl + ')' 99 | }, 100 | 101 | /* 102 | Useless at this moment, prototype to modify several files at once 103 | */ 104 | /* 105 | replaceInFile_Prototype: function (cb) { 106 | // 1. find 107 | // 2. if found = replace 108 | // 3. if not found = append at the top 109 | // regex: '!\[[ a-z0-9]+-shield-badge-[0-9]+\]\[[^\]]\]', 110 | var foundFiles = 0 111 | var error = '' 112 | var readmeFilename = this.readmeFilename 113 | if (readmeFilename !== null) { 114 | find('test123', '.', '\\.md$') 115 | .then(function (results) { 116 | if (results.hasOwnProperty(readmeFilename)) { 117 | foundFiles++ 118 | } 119 | }) 120 | .fail(function (err) { 121 | error = err 122 | foundFiles = -1 123 | }) 124 | .fin(function () { 125 | cb({error: error, foundFiles: foundFiles}) 126 | }) 127 | .done() 128 | } else { 129 | cb({error: 'The readmeFilename config property was not set', foundFiles: -1}) 130 | } 131 | }, 132 | */ 133 | 134 | replaceInFile: function (badgeMarkdown, cb) { 135 | var result = { 136 | error: '', 137 | success: false, 138 | create: false, 139 | update: false 140 | } 141 | var readmeFilename = this.readmeFilename 142 | if (readmeFilename === null) { 143 | result.error = 'The readmeFilename config property was not set (or not set properly)' 144 | cb(result) 145 | return 146 | } 147 | 148 | var readmeFile = path.resolve(__dirname, this.readmeFilename) 149 | if (this.readmeDir !== null) { 150 | var readmeFile = path.resolve(__dirname, this.readmeDir, this.readmeFilename) 151 | } 152 | fs.readFile(readmeFile, 'utf8', function (err, data) { 153 | if (err) { 154 | result.error = err 155 | cb(result) 156 | } else { 157 | var re = /!\[[^\]]+-shield-badge-[0-9]\]\(https:\/\/.+\.svg\)/gi // Not greedy but restrictive 158 | var resultData = data.replace(re, badgeMarkdown); 159 | 160 | // The badge markdown was not present, append the badge markdown at the beginning of the file 161 | if (!re.test(data)) { 162 | result.create = true 163 | result.update = false 164 | resultData = badgeMarkdown + '\n\n' + resultData 165 | } else { 166 | result.update = true 167 | result.create = false 168 | } 169 | result.data = resultData 170 | 171 | fs.writeFile(readmeFile, resultData, 'utf8', function (err) { 172 | if (err) { 173 | result.error = err 174 | cb(result) 175 | } else { 176 | result.success = true 177 | cb(result) 178 | } 179 | }) 180 | } 181 | }) 182 | }, 183 | 184 | writeReport: function (collector, sync) { 185 | var outputFile = path.resolve(this.opts.dir, this.opts.file) 186 | /* istanbul ignore next */ 187 | var writer = this.opts.writer || new FileWriter(sync) 188 | var that = this 189 | var summaries = [] 190 | var finalSummary 191 | var metrics 192 | var badgeUrl 193 | var badgeMarkdown 194 | 195 | writer.on('done', 196 | /* istanbul ignore next */ 197 | function () { 198 | that.emit('done') 199 | }) 200 | 201 | collector.files().forEach( 202 | /* istanbul ignore next */ 203 | function (file) { 204 | summaries.push(utils.summarizeFileCoverage(collector.fileCoverageFor(file))) 205 | }) 206 | finalSummary = utils.mergeSummaryObjects.apply(null, summaries) 207 | 208 | metrics = finalSummary[this.coverageType] 209 | badgeUrl = this.generateBadgeUrl(metrics) 210 | badgeMarkdown = this.generateBadgeMarkdown(badgeUrl) 211 | 212 | writer.writeFile(outputFile, function(contentWriter) { 213 | contentWriter.write(badgeUrl + '\n' + badgeMarkdown) 214 | that.replaceInFile(badgeMarkdown, 215 | /* istanbul ignore next */ 216 | function (result) { 217 | if (result.success) { 218 | var txt = result.create ? 'added' : 'updated' 219 | console.log('The shield badge has been ' + txt + ' in your ' + that.readmeFilename + ' file') 220 | } else { 221 | console.log('Error while adding/updating the shield badge in your ' + that.readmeFilename + ' file: ' + result.error) 222 | } 223 | that.emit('complete'); 224 | }) 225 | }) 226 | 227 | writer.done() 228 | } 229 | }) 230 | 231 | module.exports = ShieldBadgeReporter 232 | -------------------------------------------------------------------------------- /test/istanbul-reporter-shield-badge.spec.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var sinon = require('sinon') 3 | var fs = require('fs') 4 | var Collector = require('istanbul').Collector 5 | var FileWriter = require('istanbul').FileWriter 6 | var utils = require('istanbul').utils 7 | var ShieldBadgeReporter = require('../istanbul-reporter-shield-badge') 8 | // var findInFiles = require('../../find-in-files') 9 | require('chai').should() 10 | 11 | describe('ShieldBadgeReporter', function () { 12 | 'use strict' 13 | let sandbox 14 | beforeEach(() => { 15 | sandbox = sinon.sandbox.create() 16 | }) 17 | afterEach(() => { 18 | sandbox.restore() 19 | }) 20 | 21 | it('should return default config (empty object passed in parameter)', function () { 22 | var reporter = new ShieldBadgeReporter({}) 23 | expect(reporter.coverageType).to.equal('lines') 24 | expect(reporter.range).to.deep.equal([50,80]) 25 | expect(reporter.subject).to.equal('coverage') 26 | expect(reporter.readmeFilename).to.equal(null) 27 | expect(reporter.readmeDir).to.equal(null) 28 | expect(reporter.opts.file).to.equal('coverage.shield.badge.md') 29 | }) 30 | 31 | it('should return default config (no parameter)', function () { 32 | var reporter = new ShieldBadgeReporter() 33 | expect(reporter.coverageType).to.equal('lines') 34 | expect(reporter.range).to.deep.equal([50,80]) 35 | expect(reporter.subject).to.equal('coverage') 36 | expect(reporter.readmeFilename).to.equal(null) 37 | expect(reporter.readmeDir).to.equal(null) 38 | expect(reporter.opts.file).to.equal('coverage.shield.badge.md') 39 | }) 40 | 41 | it('should always return coverageType property in lowercase', function () { 42 | var reporter = new ShieldBadgeReporter({coverageType: 'Lines'}) 43 | expect(reporter.coverageType).to.equal('lines') 44 | }) 45 | 46 | it('should return readmeFilename = null if different than readme.md (case insensitive)', function () { 47 | var reporter = new ShieldBadgeReporter({readmeFilename: 'readme'}) 48 | expect(reporter.readmeFilename).to.equal(null) 49 | reporter = new ShieldBadgeReporter({readmeFilename: 'readme.txt'}) 50 | expect(reporter.readmeFilename).to.equal(null) 51 | }) 52 | 53 | it('should return readmeFilename properly set if equals to readme.md (case insensitive)', function () { 54 | var reporter = new ShieldBadgeReporter({readmeFilename: 'readme.md'}) 55 | expect(reporter.readmeFilename).to.equal('readme.md') 56 | reporter = new ShieldBadgeReporter({readmeFilename: 'README.md'}) 57 | expect(reporter.readmeFilename).to.equal('README.md') 58 | }) 59 | 60 | it('should return readmeDir properly set ', function () { 61 | var reporter = new ShieldBadgeReporter({readmeDir: '/test/test'}) 62 | expect(reporter.readmeDir).to.equal('/test/test') 63 | }) 64 | 65 | it('should return the sypnosis', function () { 66 | var reporter = new ShieldBadgeReporter({coverageType: 'Lines'}) 67 | expect(reporter.synopsis()).to.equal('generates the url to get a shield.io badge representing the lines coverage percentage.') 68 | }) 69 | 70 | it('should return default coverageType if other than (lines|statements|functions|branches)', function () { 71 | var reporter = new ShieldBadgeReporter({coverageType: 'Something Else'}) 72 | expect(reporter.coverageType).to.equal('lines') 73 | reporter = new ShieldBadgeReporter({coverageType: null}) 74 | expect(reporter.coverageType).to.equal('lines') 75 | reporter = new ShieldBadgeReporter({coverageType: true}) 76 | expect(reporter.coverageType).to.equal('lines') 77 | reporter = new ShieldBadgeReporter({coverageType: undefined}) 78 | expect(reporter.coverageType).to.equal('lines') 79 | reporter = new ShieldBadgeReporter({coverageType: {}}) 80 | expect(reporter.coverageType).to.equal('lines') 81 | reporter = new ShieldBadgeReporter({coverageType: []}) 82 | expect(reporter.coverageType).to.equal('lines') 83 | }) 84 | 85 | it('should return a badge url properly encoded', function () { 86 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'Test %-_/'}) 87 | var badgeUrl = reporter.generateBadgeUrl({pct: 0, }) 88 | expect(badgeUrl).to.equal('https://img.shields.io/badge/Test%20%25-_%2F-0%25-red.svg') 89 | }) 90 | 91 | it('should return a red badge', function () { 92 | var reporter = new ShieldBadgeReporter({coverageType: ''}) 93 | var badgeUrl = reporter.generateBadgeUrl({pct: 0, }) 94 | expect(badgeUrl).to.equal('https://img.shields.io/badge/coverage-0%25-red.svg') 95 | }) 96 | 97 | it('should return a yellow badge', function () { 98 | var reporter = new ShieldBadgeReporter({coverageType: ''}) 99 | var badgeUrl = reporter.generateBadgeUrl({pct: 60, }) 100 | expect(badgeUrl).to.equal('https://img.shields.io/badge/coverage-60%25-yellow.svg') 101 | }) 102 | 103 | it('should return a bright green badge', function () { 104 | var reporter = new ShieldBadgeReporter({coverageType: ''}) 105 | var badgeUrl = reporter.generateBadgeUrl({pct: 80, }) 106 | expect(badgeUrl).to.equal('https://img.shields.io/badge/coverage-80%25-brightgreen.svg') 107 | }) 108 | 109 | it('should return a yellow badge with custom range', function () { 110 | var reporter = new ShieldBadgeReporter({coverageType: '', range: [75, 90]}) 111 | var badgeUrl = reporter.generateBadgeUrl({pct: 80, }) 112 | expect(badgeUrl).to.equal('https://img.shields.io/badge/coverage-80%25-yellow.svg') 113 | }) 114 | 115 | it('should return the proper badge markdown', function () { 116 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90]}) 117 | var badgeMarkdown = reporter.generateBadgeMarkdown(reporter.generateBadgeUrl({pct: 80})) 118 | var badgeUrl = 'https://img.shields.io/badge/test-80%25-yellow.svg' 119 | expect(badgeMarkdown).to.equal('![test-shield-badge-1](' + badgeUrl + ')') 120 | }) 121 | 122 | it('should not replace the badge in the README.md if the config is not set', function(done) { 123 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90]}) 124 | reporter.replaceInFile('', function (result) { 125 | expect(result.success).to.equal(false) 126 | expect(result.error).to.equal('The readmeFilename config property was not set (or not set properly)') 127 | done() 128 | }) 129 | }) 130 | 131 | it('should not replace the badge in the README.md if the config is not properly set (other than readme.md; case insensitive)', function(done) { 132 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.txt'}) 133 | reporter.replaceInFile('', function (result) { 134 | expect(result.success).to.equal(false) 135 | expect(result.error).to.equal('The readmeFilename config property was not set (or not set properly)') 136 | done() 137 | }) 138 | }) 139 | 140 | it('should not replace the badge in the README.md if there is an error reading the file', function(done) { 141 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 142 | 143 | var readFileStub = sandbox.stub(fs, 'readFile') 144 | readFileStub.yields('error', null) 145 | 146 | reporter.replaceInFile('', function (result) { 147 | expect(result.success).to.equal(false) 148 | expect(result.error).to.equal('error') 149 | readFileStub.restore() 150 | done() 151 | }) 152 | }) 153 | 154 | it('should not replace the badge in the README.md if there is an error writing the file', function(done) { 155 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 156 | 157 | var readFileStub = sandbox.stub(fs, 'readFile') 158 | readFileStub.yields(null, '') 159 | 160 | var writeFileStub = sandbox.stub(fs, 'writeFile') 161 | writeFileStub.yields('error') 162 | 163 | reporter.replaceInFile('', function (result) { 164 | expect(result.success).to.equal(false) 165 | expect(result.error).to.equal('error') 166 | done() 167 | }) 168 | }) 169 | 170 | it('should update an existing badge in the README.md if the config is properly set', function(done) { 171 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 172 | var oldBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)' 173 | var newBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)' 174 | 175 | var readFileStub = sandbox.stub(fs, 'readFile') 176 | readFileStub.yields(null, oldBadgeMarkdown) 177 | 178 | var writeFileStub = sandbox.stub(fs, 'writeFile') 179 | writeFileStub.yields(null) 180 | 181 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 182 | expect(result.success).to.equal(true) 183 | expect(result.create).to.equal(false) 184 | expect(result.update).to.equal(true) 185 | expect(result.data).to.equal(newBadgeMarkdown) 186 | expect(result.error).to.equal('') 187 | done() 188 | }) 189 | }) 190 | 191 | it('should update an existing badge in the README.md if the config is properly set (subject is case sensitive!)', function(done) { 192 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'Test', range: [75, 90], readmeFilename: 'README.md'}) 193 | var oldBadgeMarkdown = '![Test-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)' 194 | var newBadgeMarkdown = '![Test-shield-badge-1](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)' 195 | 196 | var readFileStub = sandbox.stub(fs, 'readFile') 197 | readFileStub.yields(null, oldBadgeMarkdown) 198 | 199 | var writeFileStub = sandbox.stub(fs, 'writeFile') 200 | writeFileStub.yields(null) 201 | 202 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 203 | expect(result.success).to.equal(true) 204 | expect(result.create).to.equal(false) 205 | expect(result.update).to.equal(true) 206 | expect(result.data).to.equal(newBadgeMarkdown) 207 | expect(result.error).to.equal('') 208 | done() 209 | }) 210 | }) 211 | 212 | it('should update an existing badge in the README.md if the config is properly set (subject can accept any character!)', function(done) { 213 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'Test!', range: [75, 90], readmeFilename: 'README.md'}) 214 | var oldBadgeMarkdown = '![Test!-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)' 215 | var newBadgeMarkdown = '![Test!-shield-badge-1](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)' 216 | 217 | var readFileStub = sandbox.stub(fs, 'readFile') 218 | readFileStub.yields(null, oldBadgeMarkdown) 219 | 220 | var writeFileStub = sandbox.stub(fs, 'writeFile') 221 | writeFileStub.yields(null) 222 | 223 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 224 | expect(result.success).to.equal(true) 225 | expect(result.create).to.equal(false) 226 | expect(result.update).to.equal(true) 227 | expect(result.data).to.equal(newBadgeMarkdown) 228 | expect(result.error).to.equal('') 229 | done() 230 | }) 231 | }) 232 | 233 | it('should update an existing badge in the README.md if the config is properly set (subject may contain parentheses!)', function(done) { 234 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'Coverage (statements)', range: [75, 90], readmeFilename: 'README.md'}) 235 | var oldBadgeMarkdown = '![Coverage (statements)-shield-badge-1](https://img.shields.io/badge/Coverage%20(statements)-100%25-brightgreen.svg)' 236 | var newBadgeMarkdown = '![Coverage (statements)-shield-badge-1](https://img.shields.io/badge/Coverage%20(statements)-100%25-brightgreen.svg)' 237 | 238 | var readFileStub = sandbox.stub(fs, 'readFile') 239 | readFileStub.yields(null, oldBadgeMarkdown) 240 | 241 | var writeFileStub = sandbox.stub(fs, 'writeFile') 242 | writeFileStub.yields(null) 243 | 244 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 245 | expect(result.success).to.equal(true) 246 | expect(result.create).to.equal(false) 247 | expect(result.update).to.equal(true) 248 | expect(result.data).to.equal(newBadgeMarkdown) 249 | expect(result.error).to.equal('') 250 | done() 251 | }) 252 | }) 253 | 254 | it('should update an existing identical badge in the README.md if the config is properly set', function(done) { 255 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md', readmeDir: '.'}) 256 | var oldBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)' 257 | var newBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)' 258 | 259 | var readFileStub = sandbox.stub(fs, 'readFile') 260 | readFileStub.yields(null, oldBadgeMarkdown) 261 | 262 | var writeFileStub = sandbox.stub(fs, 'writeFile') 263 | writeFileStub.yields(null) 264 | 265 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 266 | expect(result.success).to.equal(true) 267 | expect(result.create).to.equal(false) 268 | expect(result.update).to.equal(true) 269 | expect(result.data).to.equal(newBadgeMarkdown) 270 | expect(result.error).to.equal('') 271 | done() 272 | }) 273 | }) 274 | 275 | 276 | 277 | it('should update multiple existing badges in the README.md if the config is properly set', function(done) { 278 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 279 | var oldBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)\n\nblabla![test-shield-badge-1](https://img.shields.io/badge/coverage-80%25-yellow.svg)' 280 | var newBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-88%25-yellow.svg)' 281 | 282 | var readFileStub = sandbox.stub(fs, 'readFile') 283 | readFileStub.yields(null, oldBadgeMarkdown) 284 | 285 | var writeFileStub = sandbox.stub(fs, 'writeFile') 286 | writeFileStub.yields(null) 287 | 288 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 289 | expect(result.success).to.equal(true) 290 | expect(result.create).to.equal(false) 291 | expect(result.update).to.equal(true) 292 | expect(result.error).to.equal('') 293 | expect(result.data).to.equal('![test-shield-badge-1](https://img.shields.io/badge/coverage-88%25-yellow.svg)\n\nblabla![test-shield-badge-1](https://img.shields.io/badge/coverage-88%25-yellow.svg)') 294 | expect(result.error).to.equal('') 295 | done() 296 | }) 297 | }) 298 | 299 | it('should append a badge at the beginning of the README.md if the config is properly set', function(done) { 300 | var reporter = new ShieldBadgeReporter({coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 301 | var newBadgeMarkdown = '![test-shield-badge-1](https://img.shields.io/badge/coverage-90%25-brightgreen.svg)' 302 | 303 | var readFileStub = sandbox.stub(fs, 'readFile') 304 | readFileStub.yields(null, '') 305 | 306 | var writeFileStub = sandbox.stub(fs, 'writeFile') 307 | writeFileStub.yields(null) 308 | 309 | reporter.replaceInFile(newBadgeMarkdown, function (result) { 310 | expect(result.success).to.equal(true) 311 | expect(result.create).to.equal(true) 312 | expect(result.update).to.equal(false) 313 | expect(result.error).to.equal('') 314 | done() 315 | }) 316 | }) 317 | 318 | it('should properly write the badge if the config is properly set', function() { 319 | var fileWriter = new FileWriter(true) 320 | sandbox.stub(fileWriter, 'on') 321 | sandbox.stub(fileWriter, 'done') 322 | var fileWriter_Write = sandbox.stub(fileWriter, 'writeFile') 323 | fileWriter_Write.yields({write: function () {}}) 324 | 325 | var reporter = new ShieldBadgeReporter({writer: fileWriter, coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 326 | 327 | var collector = new Collector() 328 | var collectorStub = sandbox.stub(collector, 'files') 329 | collectorStub.returns([]) 330 | 331 | var utilsStub = sandbox.stub(utils, 'mergeSummaryObjects') 332 | utilsStub.returns({lines: {pct: '90'}}) 333 | 334 | var replaceInFileStub = sandbox.stub(reporter, 'replaceInFile') 335 | 336 | reporter.writeReport(collector, true) 337 | 338 | expect(replaceInFileStub).has.been.calledOnce 339 | 340 | }) 341 | 342 | it('should emit complete event when writeReport is fully done', function(done) { 343 | var fileWriter = new FileWriter(true) 344 | sandbox.stub(fileWriter, 'on') 345 | sandbox.stub(fileWriter, 'done') 346 | var fileWriter_Write = sandbox.stub(fileWriter, 'writeFile') 347 | fileWriter_Write.yields({write: function () {}}) 348 | 349 | var reporter = new ShieldBadgeReporter({writer: fileWriter, coverageType: '', subject: 'test', range: [75, 90], readmeFilename: 'README.md'}) 350 | 351 | var collector = new Collector() 352 | var collectorStub = sandbox.stub(collector, 'files') 353 | collectorStub.returns([]) 354 | 355 | var utilsStub = sandbox.stub(utils, 'mergeSummaryObjects') 356 | utilsStub.returns({lines: {pct: '90'}}) 357 | 358 | var consoleStub = sandbox.stub(console, 'log'); 359 | 360 | var replaceInFileStub = sandbox.stub(reporter, 'replaceInFile', function(badgeMarkdown, cb) { 361 | cb({success: true, create: false}); 362 | }); 363 | 364 | reporter.on('complete', function() { 365 | consoleStub.restore(); 366 | expect(replaceInFileStub).has.been.calledOnce 367 | done(); 368 | }); 369 | 370 | reporter.writeReport(collector, true) 371 | }) 372 | 373 | }) 374 | --------------------------------------------------------------------------------