UNPKG

6.92 kBJavaScriptView Raw
1const istanbul = require('istanbul-api');
2const util = require('./util');
3
4const BROWSER_PLACEHOLDER = '%browser%';
5
6function checkThresholds(thresholds, summary) {
7 const failedTypes = [];
8
9 Object.keys(thresholds).forEach(key => {
10 const coverage = summary[key].pct;
11 if (coverage < thresholds[key]) {
12 failedTypes.push(key);
13 }
14 });
15
16 return failedTypes;
17}
18
19function CoverageIstanbulReporter(baseReporterDecorator, logger, config) {
20 baseReporterDecorator(this);
21
22 // Copied from https://github.com/angular/angular-cli/pull/9529/files
23 // Fixes https://github.com/mattlewis92/karma-coverage-istanbul-reporter/issues/44
24 const reporterName = 'coverage-istanbul';
25 const hasTrailingReporters =
26 config.reporters.slice(-1).pop() !== reporterName;
27 if (hasTrailingReporters) {
28 this.writeCommonMsg = () => {};
29 }
30
31 const log = logger.create('reporter.coverage-istanbul');
32 const browserCoverage = new WeakMap();
33 const coverageConfig = Object.assign({}, config.coverageIstanbulReporter);
34
35 function addCoverage(coverageMap, browser) {
36 const coverage = browserCoverage.get(browser);
37 browserCoverage.delete(browser);
38
39 if (!coverage) {
40 return;
41 }
42
43 Object.keys(coverage).forEach(filename => {
44 const fileCoverage = coverage[filename];
45 if (fileCoverage.inputSourceMap && coverageConfig.fixWebpackSourcePaths) {
46 fileCoverage.inputSourceMap = util.fixWebpackSourcePaths(
47 fileCoverage.inputSourceMap,
48 config.webpack
49 );
50 }
51
52 if (
53 coverageConfig.skipFilesWithNoCoverage &&
54 Object.keys(fileCoverage.statementMap).length === 0 &&
55 Object.keys(fileCoverage.fnMap).length === 0 &&
56 Object.keys(fileCoverage.branchMap).length === 0
57 ) {
58 log.debug(`File [${filename}] ignored, nothing could be mapped`);
59 } else {
60 coverageMap.addFileCoverage(fileCoverage);
61 }
62 });
63 }
64
65 function logThresholdMessage(thresholds, message) {
66 if (thresholds.emitWarning) {
67 log.warn(message);
68 } else {
69 log.error(message);
70 }
71 }
72
73 function createReport(browserOrBrowsers, results) {
74 const reportConfigOverride =
75 !coverageConfig.combineBrowserReports && coverageConfig.dir
76 ? {
77 dir: coverageConfig.dir.replace(
78 BROWSER_PLACEHOLDER,
79 browserOrBrowsers.name
80 )
81 }
82 : {};
83
84 const reportConfig = istanbul.config.loadObject({
85 verbose: coverageConfig.verbose === true,
86 reporting: Object.assign({}, coverageConfig, reportConfigOverride)
87 });
88 const reportTypes = reportConfig.reporting.config.reports;
89
90 const reporter = istanbul.createReporter(reportConfig);
91 reporter.addAll(reportTypes);
92
93 const coverageMap = istanbul.libCoverage.createCoverageMap();
94 const sourceMapStore = istanbul.libSourceMaps.createSourceMapStore();
95
96 if (coverageConfig.combineBrowserReports) {
97 browserOrBrowsers.forEach(browser => addCoverage(coverageMap, browser));
98 } else {
99 addCoverage(coverageMap, browserOrBrowsers);
100 }
101
102 const {
103 sourceFinder,
104 map: remappedCoverageMap
105 } = sourceMapStore.transformCoverage(coverageMap);
106
107 if (!coverageConfig.skipFilesWithNoCoverage) {
108 // On Windows, istanbul returns files with mixed forward/backslashes in them
109 const fixedFilePaths = {};
110 remappedCoverageMap.files().forEach(path => {
111 fixedFilePaths[util.fixPathSeparators(path)] = true;
112 });
113 coverageMap.files().forEach(path => {
114 if (!(util.fixPathSeparators(path) in fixedFilePaths)) {
115 // Re-add empty coverage record
116 remappedCoverageMap.addFileCoverage(path);
117 }
118 });
119 }
120
121 log.debug('Writing coverage reports:', reportTypes);
122 reporter.write(remappedCoverageMap, { sourceFinder });
123
124 const userThresholds = coverageConfig.thresholds;
125
126 const thresholds = {
127 emitWarning: false,
128 global: {
129 statements: 0,
130 lines: 0,
131 branches: 0,
132 functions: 0
133 },
134 each: {
135 statements: 0,
136 lines: 0,
137 branches: 0,
138 functions: 0,
139 overrides: {}
140 }
141 };
142
143 if (userThresholds) {
144 if (userThresholds.global || userThresholds.each) {
145 Object.assign(thresholds.global, userThresholds.global);
146 Object.assign(thresholds.each, userThresholds.each);
147 if (userThresholds.emitWarning === true) {
148 thresholds.emitWarning = true;
149 }
150 } else {
151 Object.assign(thresholds.global, userThresholds);
152 }
153 }
154
155 let thresholdCheckFailed = false;
156
157 // Adapted from https://github.com/istanbuljs/nyc/blob/98ebdff573be91e1098bb7259776a9082a5c1ce1/index.js#L463-L478
158 const globalSummary = remappedCoverageMap.getCoverageSummary();
159 const failedGlobalTypes = checkThresholds(thresholds.global, globalSummary);
160 failedGlobalTypes.forEach(type => {
161 thresholdCheckFailed = true;
162 logThresholdMessage(
163 thresholds,
164 `Coverage for ${type} (${globalSummary[type].pct}%) does not meet global threshold (${thresholds.global[type]}%)`
165 );
166 });
167
168 remappedCoverageMap.files().forEach(file => {
169 const fileThresholds = Object.assign(
170 {},
171 thresholds.each,
172 util.overrideThresholds(
173 file,
174 thresholds.each.overrides,
175 config.basePath
176 )
177 );
178 delete fileThresholds.overrides;
179 const fileSummary = remappedCoverageMap.fileCoverageFor(file).toSummary()
180 .data;
181 const failedFileTypes = checkThresholds(fileThresholds, fileSummary);
182
183 failedFileTypes.forEach(type => {
184 thresholdCheckFailed = true;
185 if (coverageConfig.fixWebpackSourcePaths) {
186 file = util.fixWebpackFilePath(file);
187 }
188
189 logThresholdMessage(
190 thresholds,
191 `Coverage for ${type} (${fileSummary[type].pct}%) in file ${file} does not meet per file threshold (${fileThresholds[type]}%)`
192 );
193 });
194 });
195
196 if (thresholdCheckFailed && results && !thresholds.emitWarning) {
197 results.exitCode = 1;
198 }
199 }
200
201 this.onBrowserComplete = function(browser, result) {
202 if (result && result.coverage) {
203 browserCoverage.set(browser, result.coverage);
204 }
205 };
206
207 const baseReporterOnRunComplete = this.onRunComplete;
208 this.onRunComplete = function(browsers, results) {
209 // eslint-disable-next-line prefer-rest-params
210 baseReporterOnRunComplete.apply(this, arguments);
211
212 if (coverageConfig.combineBrowserReports) {
213 createReport(browsers, results);
214 } else {
215 browsers.forEach(browser => {
216 createReport(browser, results);
217 });
218 }
219 };
220}
221
222CoverageIstanbulReporter.$inject = [
223 'baseReporterDecorator',
224 'logger',
225 'config'
226];
227
228module.exports = {
229 'reporter:coverage-istanbul': ['type', CoverageIstanbulReporter]
230};