Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

introduce enforce-min-coverage rule #84

Merged
merged 2 commits into from
May 15, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 46 additions & 0 deletions src/collect.js
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,52 @@ function executeFlow(stdin, root, filepath) {
: true;
}

export function coverage(stdin, root, filepath) {
let stdout;

switch (stdin && root && filepath && stdin !== '') {
case true:
stdout = childProcess.spawnSync(getFlowBin(), [
'coverage',
'--json',
`--root=${root}`,
filepath
], {
input: stdin,
encoding: 'utf-8'
}).stdout;
break;
default:
stdout = childProcess.spawnSync(getFlowBin(), ['--json']).stdout;
}

//
// This serves as a temporary HACK to prevent 32 bit OS's from failing. Flow does not
// support 32 bit OS's at the moment.
// This pretends as if there are now flow errors
//
// Ideally, there would be a preinstall npm event to check if the user is on a 32 bit OS
//

if (!stdout) {
return true;
}

const stringifiedStdout = stdout.toString();
let parsedJSON;

try {
parsedJSON = JSON.parse(stringifiedStdout);
} catch (e) {
parsedJSON = fatalError(stringifiedStdout);
}

return {
coveredCount: parsedJSON.expressions.covered_count,
uncoveredCount: parsedJSON.expressions.uncovered_count
};
}

export default function collect(stdin, root, filepath) {
return executeFlow(stdin, root, filepath);
}
51 changes: 42 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import fs from 'fs';
import collect from './collect';
import collect, { coverage } from './collect';


let runOnAllFiles;
Expand All @@ -11,8 +11,48 @@ function hasFlowPragma(source) {
.some(comment => /@flow/.test(comment.value));
}

function lookupFlowDir(context) {
const root = process.cwd();
const flowDirSetting = context.settings
&& context.settings['flowtype-errors']
&& context.settings['flowtype-errors'].flowDir || '.';

return fs.existsSync(path.join(root, flowDirSetting, '.flowconfig'))
? path.join(root, flowDirSetting)
: root;
}

export default {
rules: {
'enforce-min-coverage': function enforceMinCoverage(context) {
return {
Program() {
const source = context.getSourceCode();
const flowDir = lookupFlowDir(context);
const requiredCoverage = context.options[0];

if (hasFlowPragma(source)) {
const res = coverage(source.getText(), flowDir, context.getFilename());

if (res === true) {
return;
}

const { coveredCount, uncoveredCount } = res;

/* eslint prefer-template: 0 */
const percentage = Number(Math.round((coveredCount / (coveredCount + uncoveredCount)) * 100 + 'e2') + 'e-2');

if (percentage < requiredCoverage) {
context.report({
loc: 1,
message: `Expected coverage to be at least ${requiredCoverage}%, but is: ${percentage}%`
});
}
}
}
};
},
'show-errors': function showErrors(context) {
return {
Program() {
Expand All @@ -21,14 +61,7 @@ export default {

if (onTheFly) {
const source = context.getSourceCode();
const root = process.cwd();
const flowDirSetting = context.settings
&& context.settings['flowtype-errors']
&& context.settings['flowtype-errors'].flowDir || '.';

const flowDir = fs.existsSync(path.join(root, flowDirSetting, '.flowconfig'))
? path.join(root, flowDirSetting)
: root;
const flowDir = lookupFlowDir(context);

// Check to see if we should run on every file
if (runOnAllFiles === undefined) {
Expand Down
24 changes: 24 additions & 0 deletions test/__snapshots__/format.spec.js.snap
Original file line number Diff line number Diff line change
@@ -1,3 +1,27 @@
exports[`Check codebases coverage-fail - eslint should give expected output 1`] = `
"
./example.js
0:0 error Expected coverage to be at least 50%, but is: 0% flowtype-errors/enforce-min-coverage

✖ 1 problem (1 error, 0 warnings)

"
`;

exports[`Check codebases coverage-fail2 - eslint should give expected output 1`] = `
"
./example.js
0:0 error Expected coverage to be at least 50%, but is: 33.33% flowtype-errors/enforce-min-coverage

✖ 1 problem (1 error, 0 warnings)

"
`;

exports[`Check codebases coverage-ok - eslint should give expected output 1`] = `""`;

exports[`Check codebases coverage-ok2 - eslint should give expected output 1`] = `""`;

exports[`Check codebases flow-pragma-1 - eslint should give expected output 1`] = `
"
./example.js
Expand Down
3 changes: 3 additions & 0 deletions test/codebases/coverage-fail/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

let x;
5 changes: 5 additions & 0 deletions test/codebases/coverage-fail2/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @flow

let x: number;
let y;
let z;
3 changes: 3 additions & 0 deletions test/codebases/coverage-ok/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// @flow

let x: number = 100;
5 changes: 5 additions & 0 deletions test/codebases/coverage-ok2/example.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
// @flow

let x: number;
let y: number;
let z;
17 changes: 13 additions & 4 deletions test/format.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,11 @@ const codebases = [
'no-flow-pragma',
'project-1',
'run-all',
'run-all-flowdir'
'run-all-flowdir',
'coverage-ok',
'coverage-ok2',
'coverage-fail',
'coverage-fail2'
];

const issue81 = process.platform === 'win32' ? 'folder with spaces' : 'folder-with-spaces';
Expand All @@ -80,7 +84,7 @@ try {
// Already exists
}

const eslintConfig = `
const eslintConfig = (enforceMinCoverage) => `
var Module = require('module');
var path = require('path');
var original = Module._resolveFilename;
Expand All @@ -107,7 +111,12 @@ const eslintConfig = `
}
},
rules: {
'flowtype-errors/show-errors': 2
${enforceMinCoverage ? `
'flowtype-errors/show-errors': 2,
'flowtype-errors/enforce-min-coverage': [2, ${enforceMinCoverage}]
` : `
'flowtype-errors/show-errors': 2
`}
}
};
`;
Expand All @@ -130,7 +139,7 @@ describe('Check codebases', () => {
const configPath = path.resolve(fullFolder, '.eslintrc.js');

// Write config file
writeFileSync(configPath, eslintConfig);
writeFileSync(configPath, eslintConfig(folder.match(/^coverage-/) ? 50 : 0));

// Spawn a eslint process
const { stdout, stderr } = runEslint(fullFolder);
Expand Down