1 | 'use strict';
|
2 |
|
3 | Object.defineProperty(exports, '__esModule', {
|
4 | value: true
|
5 | });
|
6 | exports.default = void 0;
|
7 | function path() {
|
8 | const data = _interopRequireWildcard(require('path'));
|
9 | path = function () {
|
10 | return data;
|
11 | };
|
12 | return data;
|
13 | }
|
14 | function _v8Coverage() {
|
15 | const data = require('@bcoe/v8-coverage');
|
16 | _v8Coverage = function () {
|
17 | return data;
|
18 | };
|
19 | return data;
|
20 | }
|
21 | function _chalk() {
|
22 | const data = _interopRequireDefault(require('chalk'));
|
23 | _chalk = function () {
|
24 | return data;
|
25 | };
|
26 | return data;
|
27 | }
|
28 | function _glob() {
|
29 | const data = _interopRequireDefault(require('glob'));
|
30 | _glob = function () {
|
31 | return data;
|
32 | };
|
33 | return data;
|
34 | }
|
35 | function fs() {
|
36 | const data = _interopRequireWildcard(require('graceful-fs'));
|
37 | fs = function () {
|
38 | return data;
|
39 | };
|
40 | return data;
|
41 | }
|
42 | function _istanbulLibCoverage() {
|
43 | const data = _interopRequireDefault(require('istanbul-lib-coverage'));
|
44 | _istanbulLibCoverage = function () {
|
45 | return data;
|
46 | };
|
47 | return data;
|
48 | }
|
49 | function _istanbulLibReport() {
|
50 | const data = _interopRequireDefault(require('istanbul-lib-report'));
|
51 | _istanbulLibReport = function () {
|
52 | return data;
|
53 | };
|
54 | return data;
|
55 | }
|
56 | function _istanbulLibSourceMaps() {
|
57 | const data = _interopRequireDefault(require('istanbul-lib-source-maps'));
|
58 | _istanbulLibSourceMaps = function () {
|
59 | return data;
|
60 | };
|
61 | return data;
|
62 | }
|
63 | function _istanbulReports() {
|
64 | const data = _interopRequireDefault(require('istanbul-reports'));
|
65 | _istanbulReports = function () {
|
66 | return data;
|
67 | };
|
68 | return data;
|
69 | }
|
70 | function _v8ToIstanbul() {
|
71 | const data = _interopRequireDefault(require('v8-to-istanbul'));
|
72 | _v8ToIstanbul = function () {
|
73 | return data;
|
74 | };
|
75 | return data;
|
76 | }
|
77 | function _jestUtil() {
|
78 | const data = require('jest-util');
|
79 | _jestUtil = function () {
|
80 | return data;
|
81 | };
|
82 | return data;
|
83 | }
|
84 | function _jestWorker() {
|
85 | const data = require('jest-worker');
|
86 | _jestWorker = function () {
|
87 | return data;
|
88 | };
|
89 | return data;
|
90 | }
|
91 | var _BaseReporter = _interopRequireDefault(require('./BaseReporter'));
|
92 | var _getWatermarks = _interopRequireDefault(require('./getWatermarks'));
|
93 | function _interopRequireDefault(obj) {
|
94 | return obj && obj.__esModule ? obj : {default: obj};
|
95 | }
|
96 | function _getRequireWildcardCache(nodeInterop) {
|
97 | if (typeof WeakMap !== 'function') return null;
|
98 | var cacheBabelInterop = new WeakMap();
|
99 | var cacheNodeInterop = new WeakMap();
|
100 | return (_getRequireWildcardCache = function (nodeInterop) {
|
101 | return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
|
102 | })(nodeInterop);
|
103 | }
|
104 | function _interopRequireWildcard(obj, nodeInterop) {
|
105 | if (!nodeInterop && obj && obj.__esModule) {
|
106 | return obj;
|
107 | }
|
108 | if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
|
109 | return {default: obj};
|
110 | }
|
111 | var cache = _getRequireWildcardCache(nodeInterop);
|
112 | if (cache && cache.has(obj)) {
|
113 | return cache.get(obj);
|
114 | }
|
115 | var newObj = {};
|
116 | var hasPropertyDescriptor =
|
117 | Object.defineProperty && Object.getOwnPropertyDescriptor;
|
118 | for (var key in obj) {
|
119 | if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
|
120 | var desc = hasPropertyDescriptor
|
121 | ? Object.getOwnPropertyDescriptor(obj, key)
|
122 | : null;
|
123 | if (desc && (desc.get || desc.set)) {
|
124 | Object.defineProperty(newObj, key, desc);
|
125 | } else {
|
126 | newObj[key] = obj[key];
|
127 | }
|
128 | }
|
129 | }
|
130 | newObj.default = obj;
|
131 | if (cache) {
|
132 | cache.set(obj, newObj);
|
133 | }
|
134 | return newObj;
|
135 | }
|
136 |
|
137 |
|
138 |
|
139 |
|
140 |
|
141 |
|
142 |
|
143 | const FAIL_COLOR = _chalk().default.bold.red;
|
144 | const RUNNING_TEST_COLOR = _chalk().default.bold.dim;
|
145 | class CoverageReporter extends _BaseReporter.default {
|
146 | _context;
|
147 | _coverageMap;
|
148 | _globalConfig;
|
149 | _sourceMapStore;
|
150 | _v8CoverageResults;
|
151 | static filename = __filename;
|
152 | constructor(globalConfig, context) {
|
153 | super();
|
154 | this._context = context;
|
155 | this._coverageMap = _istanbulLibCoverage().default.createCoverageMap({});
|
156 | this._globalConfig = globalConfig;
|
157 | this._sourceMapStore =
|
158 | _istanbulLibSourceMaps().default.createSourceMapStore();
|
159 | this._v8CoverageResults = [];
|
160 | }
|
161 | onTestResult(_test, testResult) {
|
162 | if (testResult.v8Coverage) {
|
163 | this._v8CoverageResults.push(testResult.v8Coverage);
|
164 | return;
|
165 | }
|
166 | if (testResult.coverage) {
|
167 | this._coverageMap.merge(testResult.coverage);
|
168 | }
|
169 | }
|
170 | async onRunComplete(testContexts, aggregatedResults) {
|
171 | await this._addUntestedFiles(testContexts);
|
172 | const {map, reportContext} = await this._getCoverageResult();
|
173 | try {
|
174 | const coverageReporters = this._globalConfig.coverageReporters || [];
|
175 | if (!this._globalConfig.useStderr && coverageReporters.length < 1) {
|
176 | coverageReporters.push('text-summary');
|
177 | }
|
178 | coverageReporters.forEach(reporter => {
|
179 | let additionalOptions = {};
|
180 | if (Array.isArray(reporter)) {
|
181 | [reporter, additionalOptions] = reporter;
|
182 | }
|
183 | _istanbulReports()
|
184 | .default.create(reporter, {
|
185 | maxCols: process.stdout.columns || Infinity,
|
186 | ...additionalOptions
|
187 | })
|
188 | .execute(reportContext);
|
189 | });
|
190 | aggregatedResults.coverageMap = map;
|
191 | } catch (e) {
|
192 | console.error(
|
193 | _chalk().default.red(`
|
194 | Failed to write coverage reports:
|
195 | ERROR: ${e.toString()}
|
196 | STACK: ${e.stack}
|
197 | `)
|
198 | );
|
199 | }
|
200 | this._checkThreshold(map);
|
201 | }
|
202 | async _addUntestedFiles(testContexts) {
|
203 | const files = [];
|
204 | testContexts.forEach(context => {
|
205 | const config = context.config;
|
206 | if (
|
207 | this._globalConfig.collectCoverageFrom &&
|
208 | this._globalConfig.collectCoverageFrom.length
|
209 | ) {
|
210 | context.hasteFS
|
211 | .matchFilesWithGlob(
|
212 | this._globalConfig.collectCoverageFrom,
|
213 | config.rootDir
|
214 | )
|
215 | .forEach(filePath =>
|
216 | files.push({
|
217 | config,
|
218 | path: filePath
|
219 | })
|
220 | );
|
221 | }
|
222 | });
|
223 | if (!files.length) {
|
224 | return;
|
225 | }
|
226 | if (_jestUtil().isInteractive) {
|
227 | process.stderr.write(
|
228 | RUNNING_TEST_COLOR('Running coverage on untested files...')
|
229 | );
|
230 | }
|
231 | let worker;
|
232 | if (this._globalConfig.maxWorkers <= 1) {
|
233 | worker = require('./CoverageWorker');
|
234 | } else {
|
235 | worker = new (_jestWorker().Worker)(require.resolve('./CoverageWorker'), {
|
236 | enableWorkerThreads: this._globalConfig.workerThreads,
|
237 | exposedMethods: ['worker'],
|
238 | forkOptions: {
|
239 | serialization: 'json'
|
240 | },
|
241 | maxRetries: 2,
|
242 | numWorkers: this._globalConfig.maxWorkers
|
243 | });
|
244 | }
|
245 | const instrumentation = files.map(async fileObj => {
|
246 | const filename = fileObj.path;
|
247 | const config = fileObj.config;
|
248 | const hasCoverageData = this._v8CoverageResults.some(v8Res =>
|
249 | v8Res.some(innerRes => innerRes.result.url === filename)
|
250 | );
|
251 | if (
|
252 | !hasCoverageData &&
|
253 | !this._coverageMap.data[filename] &&
|
254 | 'worker' in worker
|
255 | ) {
|
256 | try {
|
257 | const result = await worker.worker({
|
258 | config,
|
259 | context: {
|
260 | changedFiles:
|
261 | this._context.changedFiles &&
|
262 | Array.from(this._context.changedFiles),
|
263 | sourcesRelatedToTestsInChangedFiles:
|
264 | this._context.sourcesRelatedToTestsInChangedFiles &&
|
265 | Array.from(this._context.sourcesRelatedToTestsInChangedFiles)
|
266 | },
|
267 | globalConfig: this._globalConfig,
|
268 | path: filename
|
269 | });
|
270 | if (result) {
|
271 | if (result.kind === 'V8Coverage') {
|
272 | this._v8CoverageResults.push([
|
273 | {
|
274 | codeTransformResult: undefined,
|
275 | result: result.result
|
276 | }
|
277 | ]);
|
278 | } else {
|
279 | this._coverageMap.addFileCoverage(result.coverage);
|
280 | }
|
281 | }
|
282 | } catch (error) {
|
283 | console.error(
|
284 | _chalk().default.red(
|
285 | [
|
286 | `Failed to collect coverage from ${filename}`,
|
287 | `ERROR: ${error.message}`,
|
288 | `STACK: ${error.stack}`
|
289 | ].join('\n')
|
290 | )
|
291 | );
|
292 | }
|
293 | }
|
294 | });
|
295 | try {
|
296 | await Promise.all(instrumentation);
|
297 | } catch {
|
298 |
|
299 | }
|
300 | if (_jestUtil().isInteractive) {
|
301 | (0, _jestUtil().clearLine)(process.stderr);
|
302 | }
|
303 | if (worker && 'end' in worker && typeof worker.end === 'function') {
|
304 | await worker.end();
|
305 | }
|
306 | }
|
307 | _checkThreshold(map) {
|
308 | const {coverageThreshold} = this._globalConfig;
|
309 | if (coverageThreshold) {
|
310 | function check(name, thresholds, actuals) {
|
311 | return ['statements', 'branches', 'lines', 'functions'].reduce(
|
312 | (errors, key) => {
|
313 | const actual = actuals[key].pct;
|
314 | const actualUncovered = actuals[key].total - actuals[key].covered;
|
315 | const threshold = thresholds[key];
|
316 | if (threshold !== undefined) {
|
317 | if (threshold < 0) {
|
318 | if (threshold * -1 < actualUncovered) {
|
319 | errors.push(
|
320 | `Jest: Uncovered count for ${key} (${actualUncovered}) ` +
|
321 | `exceeds ${name} threshold (${-1 * threshold})`
|
322 | );
|
323 | }
|
324 | } else if (actual < threshold) {
|
325 | errors.push(
|
326 | `Jest: "${name}" coverage threshold for ${key} (${threshold}%) not met: ${actual}%`
|
327 | );
|
328 | }
|
329 | }
|
330 | return errors;
|
331 | },
|
332 | []
|
333 | );
|
334 | }
|
335 | const THRESHOLD_GROUP_TYPES = {
|
336 | GLOB: 'glob',
|
337 | GLOBAL: 'global',
|
338 | PATH: 'path'
|
339 | };
|
340 | const coveredFiles = map.files();
|
341 | const thresholdGroups = Object.keys(coverageThreshold);
|
342 | const groupTypeByThresholdGroup = {};
|
343 | const filesByGlob = {};
|
344 | const coveredFilesSortedIntoThresholdGroup = coveredFiles.reduce(
|
345 | (files, file) => {
|
346 | const pathOrGlobMatches = thresholdGroups.reduce(
|
347 | (agg, thresholdGroup) => {
|
348 |
|
349 |
|
350 | const resolvedThresholdGroup = path().resolve(thresholdGroup);
|
351 | const suffix =
|
352 | (thresholdGroup.endsWith(path().sep) ||
|
353 | (process.platform === 'win32' &&
|
354 | thresholdGroup.endsWith('/'))) &&
|
355 | !resolvedThresholdGroup.endsWith(path().sep)
|
356 | ? path().sep
|
357 | : '';
|
358 | const absoluteThresholdGroup = `${resolvedThresholdGroup}${suffix}`;
|
359 |
|
360 |
|
361 |
|
362 | if (file.indexOf(absoluteThresholdGroup) === 0) {
|
363 | groupTypeByThresholdGroup[thresholdGroup] =
|
364 | THRESHOLD_GROUP_TYPES.PATH;
|
365 | return agg.concat([[file, thresholdGroup]]);
|
366 | }
|
367 |
|
368 |
|
369 |
|
370 |
|
371 |
|
372 |
|
373 | if (filesByGlob[absoluteThresholdGroup] === undefined) {
|
374 | filesByGlob[absoluteThresholdGroup] = _glob()
|
375 | .default.sync(absoluteThresholdGroup)
|
376 | .map(filePath => path().resolve(filePath));
|
377 | }
|
378 | if (filesByGlob[absoluteThresholdGroup].indexOf(file) > -1) {
|
379 | groupTypeByThresholdGroup[thresholdGroup] =
|
380 | THRESHOLD_GROUP_TYPES.GLOB;
|
381 | return agg.concat([[file, thresholdGroup]]);
|
382 | }
|
383 | return agg;
|
384 | },
|
385 | []
|
386 | );
|
387 | if (pathOrGlobMatches.length > 0) {
|
388 | return files.concat(pathOrGlobMatches);
|
389 | }
|
390 |
|
391 |
|
392 | if (thresholdGroups.indexOf(THRESHOLD_GROUP_TYPES.GLOBAL) > -1) {
|
393 | groupTypeByThresholdGroup[THRESHOLD_GROUP_TYPES.GLOBAL] =
|
394 | THRESHOLD_GROUP_TYPES.GLOBAL;
|
395 | return files.concat([[file, THRESHOLD_GROUP_TYPES.GLOBAL]]);
|
396 | }
|
397 |
|
398 |
|
399 | return files.concat([[file, undefined]]);
|
400 | },
|
401 | []
|
402 | );
|
403 | const getFilesInThresholdGroup = thresholdGroup =>
|
404 | coveredFilesSortedIntoThresholdGroup
|
405 | .filter(fileAndGroup => fileAndGroup[1] === thresholdGroup)
|
406 | .map(fileAndGroup => fileAndGroup[0]);
|
407 | function combineCoverage(filePaths) {
|
408 | return filePaths
|
409 | .map(filePath => map.fileCoverageFor(filePath))
|
410 | .reduce((combinedCoverage, nextFileCoverage) => {
|
411 | if (combinedCoverage === undefined || combinedCoverage === null) {
|
412 | return nextFileCoverage.toSummary();
|
413 | }
|
414 | return combinedCoverage.merge(nextFileCoverage.toSummary());
|
415 | }, undefined);
|
416 | }
|
417 | let errors = [];
|
418 | thresholdGroups.forEach(thresholdGroup => {
|
419 | switch (groupTypeByThresholdGroup[thresholdGroup]) {
|
420 | case THRESHOLD_GROUP_TYPES.GLOBAL: {
|
421 | const coverage = combineCoverage(
|
422 | getFilesInThresholdGroup(THRESHOLD_GROUP_TYPES.GLOBAL)
|
423 | );
|
424 | if (coverage) {
|
425 | errors = errors.concat(
|
426 | check(
|
427 | thresholdGroup,
|
428 | coverageThreshold[thresholdGroup],
|
429 | coverage
|
430 | )
|
431 | );
|
432 | }
|
433 | break;
|
434 | }
|
435 | case THRESHOLD_GROUP_TYPES.PATH: {
|
436 | const coverage = combineCoverage(
|
437 | getFilesInThresholdGroup(thresholdGroup)
|
438 | );
|
439 | if (coverage) {
|
440 | errors = errors.concat(
|
441 | check(
|
442 | thresholdGroup,
|
443 | coverageThreshold[thresholdGroup],
|
444 | coverage
|
445 | )
|
446 | );
|
447 | }
|
448 | break;
|
449 | }
|
450 | case THRESHOLD_GROUP_TYPES.GLOB:
|
451 | getFilesInThresholdGroup(thresholdGroup).forEach(
|
452 | fileMatchingGlob => {
|
453 | errors = errors.concat(
|
454 | check(
|
455 | fileMatchingGlob,
|
456 | coverageThreshold[thresholdGroup],
|
457 | map.fileCoverageFor(fileMatchingGlob).toSummary()
|
458 | )
|
459 | );
|
460 | }
|
461 | );
|
462 | break;
|
463 | default:
|
464 |
|
465 | if (thresholdGroup !== THRESHOLD_GROUP_TYPES.GLOBAL) {
|
466 | errors = errors.concat(
|
467 | `Jest: Coverage data for ${thresholdGroup} was not found.`
|
468 | );
|
469 | }
|
470 |
|
471 |
|
472 |
|
473 | }
|
474 | });
|
475 |
|
476 | errors = errors.filter(
|
477 | err => err !== undefined && err !== null && err.length > 0
|
478 | );
|
479 | if (errors.length > 0) {
|
480 | this.log(`${FAIL_COLOR(errors.join('\n'))}`);
|
481 | this._setError(new Error(errors.join('\n')));
|
482 | }
|
483 | }
|
484 | }
|
485 | async _getCoverageResult() {
|
486 | if (this._globalConfig.coverageProvider === 'v8') {
|
487 | const mergedCoverages = (0, _v8Coverage().mergeProcessCovs)(
|
488 | this._v8CoverageResults.map(cov => ({
|
489 | result: cov.map(r => r.result)
|
490 | }))
|
491 | );
|
492 | const fileTransforms = new Map();
|
493 | this._v8CoverageResults.forEach(res =>
|
494 | res.forEach(r => {
|
495 | if (r.codeTransformResult && !fileTransforms.has(r.result.url)) {
|
496 | fileTransforms.set(r.result.url, r.codeTransformResult);
|
497 | }
|
498 | })
|
499 | );
|
500 | const transformedCoverage = await Promise.all(
|
501 | mergedCoverages.result.map(async res => {
|
502 | const fileTransform = fileTransforms.get(res.url);
|
503 | let sourcemapContent = undefined;
|
504 | if (
|
505 | fileTransform?.sourceMapPath &&
|
506 | fs().existsSync(fileTransform.sourceMapPath)
|
507 | ) {
|
508 | sourcemapContent = JSON.parse(
|
509 | fs().readFileSync(fileTransform.sourceMapPath, 'utf8')
|
510 | );
|
511 | }
|
512 | const converter = (0, _v8ToIstanbul().default)(
|
513 | res.url,
|
514 | fileTransform?.wrapperLength ?? 0,
|
515 | fileTransform && sourcemapContent
|
516 | ? {
|
517 | originalSource: fileTransform.originalCode,
|
518 | source: fileTransform.code,
|
519 | sourceMap: {
|
520 | sourcemap: {
|
521 | file: res.url,
|
522 | ...sourcemapContent
|
523 | }
|
524 | }
|
525 | }
|
526 | : {
|
527 | source: fs().readFileSync(res.url, 'utf8')
|
528 | }
|
529 | );
|
530 | await converter.load();
|
531 | converter.applyCoverage(res.functions);
|
532 | const istanbulData = converter.toIstanbul();
|
533 | return istanbulData;
|
534 | })
|
535 | );
|
536 | const map = _istanbulLibCoverage().default.createCoverageMap({});
|
537 | transformedCoverage.forEach(res => map.merge(res));
|
538 | const reportContext = _istanbulLibReport().default.createContext({
|
539 | coverageMap: map,
|
540 | dir: this._globalConfig.coverageDirectory,
|
541 | watermarks: (0, _getWatermarks.default)(this._globalConfig)
|
542 | });
|
543 | return {
|
544 | map,
|
545 | reportContext
|
546 | };
|
547 | }
|
548 | const map = await this._sourceMapStore.transformCoverage(this._coverageMap);
|
549 | const reportContext = _istanbulLibReport().default.createContext({
|
550 | coverageMap: map,
|
551 | dir: this._globalConfig.coverageDirectory,
|
552 | sourceFinder: this._sourceMapStore.sourceFinder,
|
553 | watermarks: (0, _getWatermarks.default)(this._globalConfig)
|
554 | });
|
555 | return {
|
556 | map,
|
557 | reportContext
|
558 | };
|
559 | }
|
560 | }
|
561 | exports.default = CoverageReporter;
|