Skip to content

Commit

Permalink
Thresholds now can be set up for individual files the same way they a…
Browse files Browse the repository at this point in the history
…re currently setup for 'global'. Supports globs along absolute and relative paths. Each path expanded from a glob gets assigned the threshold from a glob. Tests now use mock-fs to mock filesystem for glob testing. If match is not found, thresholds would not be met.
  • Loading branch information
krishnagoth committed Aug 24, 2017
1 parent 84b2502 commit 7e4686f
Show file tree
Hide file tree
Showing 5 changed files with 188 additions and 26 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"micromatch": "^2.3.11",
"mkdirp": "^0.5.1",
"mocha": "^3.4.2",
"mock-fs": "^4.4.1",
"prettier": "^1.5.2",
"progress": "^1.1.8",
"react": "^15.4.2",
Expand Down Expand Up @@ -141,4 +142,4 @@
"**/*.test.js"
]
}
}
}
132 changes: 115 additions & 17 deletions packages/jest-cli/src/reporters/__tests__/coverage_reporter.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
'use strict';

jest
.mock('fs')
.mock('istanbul-lib-coverage')
.mock('istanbul-lib-source-maps')
.mock('istanbul-api');
Expand All @@ -18,6 +17,9 @@ let libSourceMaps;
let CoverageReporter;
let istanbulApi;

import path from 'path';
import mock from 'mock-fs';

beforeEach(() => {
istanbulApi = require('istanbul-api');
istanbulApi.createReporter = jest.fn(() => ({
Expand All @@ -28,6 +30,23 @@ beforeEach(() => {
CoverageReporter = require('../coverage_reporter');
libCoverage = require('istanbul-lib-coverage');
libSourceMaps = require('istanbul-lib-source-maps');

const fileTree = {};
fileTree[process.cwd() + '/path-test-files'] = {
'full_path_file.js': '',
'glob-path': {
'file1.js': '',
'file2.js': '',
},
'non_covered_file.js': '',
'relative_path_file.js': '',
};

mock(fileTree);
});

afterEach(() => {
mock.restore();
});

describe('onRunComplete', () => {
Expand All @@ -50,18 +69,37 @@ describe('onRunComplete', () => {
};

libCoverage.createCoverageMap = jest.fn(() => {
const files = [
'./path-test-files/covered_file_without_threshold.js',
'./path-test-files/full_path_file.js',
'./path-test-files/relative_path_file.js',
'./path-test-files/glob-path/file1.js',
'./path-test-files/glob-path/file2.js',
].map(p => path.resolve(p));

return {
getCoverageSummary() {
return {
toJSON() {
return {
branches: {covered: 0, pct: 0, skipped: 0, total: 0},
functions: {covered: 0, pct: 0, skipped: 0, total: 0},
lines: {covered: 0, pct: 0, skipped: 0, total: 0},
statements: {covered: 0, pct: 50, skipped: 0, total: 0},
};
},
};
fileCoverageFor(path) {
if (files.indexOf(path) !== -1) {
const covSummary = {
branches: {covered: 0, pct: 0, skipped: 0, total: 0},
functions: {covered: 0, pct: 0, skipped: 0, total: 0},
lines: {covered: 0, pct: 0, skipped: 0, total: 0},
merge(other) {
return covSummary;
},
statements: {covered: 0, pct: 50, skipped: 0, total: 0},
};
return {
toSummary() {
return covSummary;
},
};
} else {
return undefined;
}
},
files() {
return files;
},
};
});
Expand All @@ -75,7 +113,7 @@ describe('onRunComplete', () => {
});
});

it('getLastError() returns an error when threshold is not met', () => {
it('getLastError() returns an error when threshold is not met for global', () => {
const testReporter = new CoverageReporter(
{
collectCoverage: true,
Expand All @@ -93,17 +131,77 @@ describe('onRunComplete', () => {
return testReporter
.onRunComplete(new Set(), {}, mockAggResults)
.then(() => {
expect(testReporter.getLastError()).toBeTruthy();
expect(testReporter.getLastError().message.split('\n')).toHaveLength(1);
});
});

it('getLastError() returns an error when threshold is not met for file', () => {
const covThreshold = {};
[
'global',
path.resolve(`${process.cwd()}/path-test-files/full_path_file.js`),
'./path-test-files/relative_path_file.js',
'path-test-files/glob-*/*.js',
].forEach(path => {
covThreshold[path] = {
statements: 100,
};
});

const testReporter = new CoverageReporter(
{
collectCoverage: true,
coverageThreshold: covThreshold,
},
{
maxWorkers: 2,
},
);
testReporter.log = jest.fn();
return testReporter
.onRunComplete(new Set(), {}, mockAggResults)
.then(() => {
expect(testReporter.getLastError().message.split('\n')).toHaveLength(5);
});
});

it('getLastError() returns `undefined` when threshold is met', () => {
const covThreshold = {};
[
'global',
path.resolve(`${process.cwd()}/path-test-files/full_path_file.js`),
'./path-test-files/relative_path_file.js',
'path-test-files/glob-*/*.js',
].forEach(path => {
covThreshold[path] = {
statements: 50,
};
});

const testReporter = new CoverageReporter(
{
collectCoverage: true,
coverageThreshold: covThreshold,
},
{
maxWorkers: 2,
},
);
testReporter.log = jest.fn();
return testReporter
.onRunComplete(new Set(), {}, mockAggResults)
.then(() => {
expect(testReporter.getLastError()).toBeUndefined();
});
});

it('getLastError() returns an error when threshold is for non-covered file', () => {
const testReporter = new CoverageReporter(
{
collectCoverage: true,
coverageThreshold: {
global: {
statements: 50,
'path-test-files/non_covered_file.js': {
statements: 100,
},
},
},
Expand All @@ -115,7 +213,7 @@ describe('onRunComplete', () => {
return testReporter
.onRunComplete(new Set(), {}, mockAggResults)
.then(() => {
expect(testReporter.getLastError()).toBeUndefined();
expect(testReporter.getLastError().message.split('\n')).toHaveLength(1);
});
});
});
70 changes: 64 additions & 6 deletions packages/jest-cli/src/reporters/coverage_reporter.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
AggregatedResult,
CoverageMap,
FileCoverage,
CoverageSummary,
SerializableError,
TestResult,
} from 'types/TestResult';
Expand All @@ -29,6 +30,8 @@ import pify from 'pify';
import workerFarm from 'worker-farm';
import BaseReporter from './base_reporter';
import CoverageWorker from './coverage_worker';
import path from 'path';
import glob from 'glob';

const FAIL_COLOR = chalk.bold.red;
const RUNNING_TEST_COLOR = chalk.bold.dim;
Expand Down Expand Up @@ -204,8 +207,6 @@ class CoverageReporter extends BaseReporter {

_checkThreshold(globalConfig: GlobalConfig, map: CoverageMap) {
if (globalConfig.coverageThreshold) {
const results = map.getCoverageSummary().toJSON();

function check(name, thresholds, actuals) {
return [
'statements',
Expand Down Expand Up @@ -235,10 +236,67 @@ class CoverageReporter extends BaseReporter {
return errors;
}, []);
}
const errors = check(
'global',
globalConfig.coverageThreshold.global,
results,

const expandedThresholds = {};
Object.keys(globalConfig.coverageThreshold).forEach(filePathOrGlob => {
if (filePathOrGlob !== 'global') {
const pathArray = glob.sync(filePathOrGlob);
pathArray.forEach(filePath => {
expandedThresholds[path.resolve(filePath)] =
globalConfig.coverageThreshold[filePathOrGlob];
});
} else {
expandedThresholds.global = globalConfig.coverageThreshold.global;
}
});

const filteredCoverageSummary = map
.files()
.filter(
filePath => Object.keys(expandedThresholds).indexOf(filePath) === -1,
)
.map(filePath => map.fileCoverageFor(filePath))
.reduce((summary: ?CoverageSummary, fileCov: FileCoverage) => {
return summary === undefined || summary === null
? (summary = fileCov.toSummary())
: summary.merge(fileCov.toSummary());
}, undefined);

const errors = [].concat.apply(
[],
Object.keys(expandedThresholds)
.map(thresholdKey => {
if (thresholdKey === 'global') {
if (filteredCoverageSummary !== undefined) {
return check(
'global',
expandedThresholds.global,
filteredCoverageSummary,
);
} else {
return [];
}
} else {
if (map.files().indexOf(thresholdKey) !== -1) {
return check(
thresholdKey,
expandedThresholds[thresholdKey],
map.fileCoverageFor(thresholdKey).toSummary(),
);
} else {
return [
`Jest: Coverage data for ${thresholdKey} was not found.`,
];
}
}
})
.filter(errorArray => {
return (
errorArray !== undefined &&
errorArray !== null &&
errorArray.length > 0
);
}),
);

if (errors.length > 0) {
Expand Down
5 changes: 3 additions & 2 deletions types/TestResult.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ type FileCoverageTotal = {|
total: number,
covered: number,
skipped: number,
pct?: number,
pct: number,
|};

type CoverageSummary = {|
export type CoverageSummary = {|
lines: FileCoverageTotal,
statements: FileCoverageTotal,
branches: FileCoverageTotal,
functions: FileCoverageTotal,
merge: (other: CoverageSummary) => void,
|};

export type FileCoverage = {|
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4380,6 +4380,10 @@ mocha@^3.4.2:
mkdirp "0.5.1"
supports-color "3.1.2"

mock-fs@^4.4.1:
version "4.4.1"
resolved "https://registry.yarnpkg.com/mock-fs/-/mock-fs-4.4.1.tgz#f285fa025b42a4031faf75b66f632b21e7056683"

modify-values@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/modify-values/-/modify-values-1.0.0.tgz#e2b6cdeb9ce19f99317a53722f3dbf5df5eaaab2"
Expand Down

0 comments on commit 7e4686f

Please sign in to comment.