Skip to content

Commit

Permalink
CLI: Support a maxErrors option to limit the number of reported errors
Browse files Browse the repository at this point in the history
  • Loading branch information
mrjoelkemp committed Oct 7, 2014
1 parent 37cf0e8 commit 6de72ad
Show file tree
Hide file tree
Showing 14 changed files with 179 additions and 2 deletions.
17 changes: 17 additions & 0 deletions README.md
Expand Up @@ -85,6 +85,9 @@ jscs path[ path[...]] --reporter=./some-dir/my-reporter.js
### `--no-colors`
Clean output without colors.

### '--max-errors'
Set the maximum number of errors to report

### `--help`
Outputs usage information.

Expand Down Expand Up @@ -166,6 +169,20 @@ Values: A single file extension or an Array of file extensions, beginning with a
"fileExtensions": [".js"]
```

### maxErrors

Set the maximum number of errors to report

Type: `Number`

Default: 50

#### Example

```js
"maxErrors": 10
```

## Error Suppression

### Inline Comments
Expand Down
1 change: 1 addition & 0 deletions bin/jscs
Expand Up @@ -18,6 +18,7 @@ program
.option('-p, --preset <preset>', 'preset config')
.option('-r, --reporter <reporter>', 'error reporter, console - default, text, checkstyle, junit, inline')
.option('-v, --verbose', 'adds rule names to the error output')
.option('-m, --max-errors <number>', 'maximum number of errors to report')
.option('', 'Also accepts relative or absolute path to custom reporter')
.option('', 'For instance:')
.option('', '\t ../some-dir/my-reporter.js\t(relative path with extension)')
Expand Down
2 changes: 2 additions & 0 deletions lib/checker.js
Expand Up @@ -7,6 +7,7 @@ var path = require('path');
var additionalRules = require('./options/additional-rules');
var excludeFiles = require('./options/exclude-files');
var fileExtensions = require('./options/file-extensions');
var maxErrors = require('./options/max-errors');

/**
* Starts Code Style checking process.
Expand All @@ -30,6 +31,7 @@ Checker.prototype.configure = function(config) {
fileExtensions(config, this);
excludeFiles(config, this, cwd);
additionalRules(config, this, cwd);
maxErrors(config, this);

StringChecker.prototype.configure.apply(this, arguments);
};
Expand Down
8 changes: 8 additions & 0 deletions lib/cli.js
Expand Up @@ -82,6 +82,10 @@ module.exports = function(program) {
config.preset = program.preset;
}

if (program.maxErrors) {
config.maxErrors = Number(program.maxErrors);
}

if (program.reporter) {
reporterPath = path.resolve(process.cwd(), program.reporter);
returnArgs.reporter = reporterPath;
Expand Down Expand Up @@ -135,6 +139,10 @@ module.exports = function(program) {

reporter(errorsCollection);

if (checker.maxErrorsHit) {
console.log('Hi friend, there were more errors.');
}

errorsCollection.forEach(function(errors) {
if (!errors.isEmpty()) {
defer.reject(2);
Expand Down
36 changes: 36 additions & 0 deletions lib/errors.js
Expand Up @@ -14,13 +14,39 @@ var Errors = function(file, verbose) {
this._verbose = verbose || false;
};

/**
* Default maximum number of errors
*
* Dangling for tests to reset
*
* @type {Number}
*/
Errors._defaultMaxErrors = 50;

/**
* Tracks the number of errors found across all instances
*
* @type {Number}
*/
Errors._errorCount = 0;

/**
* Maximum number of errors possible across all instances
*
* @type {Number}
*/
Errors.maxErrors = Errors._defaultMaxErrors;

Errors.prototype = {
/**
* Adds style error to the list
*
* @param {String} message
* @param {Number|Object} line
* @param {Number} [column]
* @returns {Boolean|undefined} true if the addition was successful,
* false if the add limit was hit
* undefined if the rule is not enabled
*/
add: function(message, line, column) {
if (typeof line === 'object') {
Expand All @@ -32,12 +58,22 @@ Errors.prototype = {
return;
}

// We're trying to add more than the max number of errors
if (Errors._errorCount === Errors.maxErrors) {
this.maxErrorsHit = true;
return false;
}

this._errorList.push({
rule: this._currentRule,
message: this._verbose ? this._currentRule + ': ' + message : message,
line: line,
column: column || 0
});

Errors._errorCount++;

return true;
},

/**
Expand Down
18 changes: 18 additions & 0 deletions lib/options/max-errors.js
@@ -0,0 +1,18 @@
var Errors = require('../errors');

/**
* Limits the total number of errors reported
*
* @param {Object} config
* @param {lib/checker} instance
*/
module.exports = function(config, instance) {
if (config.maxErrors) {
Errors.maxErrors = Number(config.maxErrors);
}

Object.defineProperty(config, 'maxErrors', {
value: config.maxErrors,
enumerable: false
});
};
2 changes: 2 additions & 0 deletions lib/string-checker.js
Expand Up @@ -265,6 +265,8 @@ StringChecker.prototype = {

}, this);

this.maxErrorsHit = errors.maxErrorsHit;

// sort errors list to show errors as they appear in source
errors.getErrorList().sort(function(a, b) {
return (a.line - b.line) || (a.column - b.column);
Expand Down
26 changes: 26 additions & 0 deletions test/cli.js
Expand Up @@ -11,6 +11,7 @@ var exec = require('child_process').exec;

var cli = rewire('../lib/cli');
var startingDir = process.cwd();
var Errors = require('../lib/errors');

describe('modules/cli', function() {
before(function() {
Expand Down Expand Up @@ -524,4 +525,29 @@ describe('modules/cli', function() {
});
});
});

describe('maxErrors option', function() {
beforeEach(function() {
sinon.spy(console, 'log');
Errors._resetMaxErrors();
});

afterEach(function() {
console.log.restore();
Errors._resetMaxErrorsForTests();
});

it('should limit the number of errors reported to the provided amount', function(done) {
return cli({
maxErrors: 1,
args: ['test/data/cli/error.js'],
config: 'test/data/cli/maxErrors.json'
})
.promise.always(function() {
assert(console.log.getCall(1).args[0].indexOf('1 code style error found.') !== -1);
rAfter();
done();
});
});
});
});
5 changes: 5 additions & 0 deletions test/data/cli/maxErrors.json
@@ -0,0 +1,5 @@
{
"disallowKeywords": ["with"],
"requireSpaceBeforePostfixUnaryOperators": ["++"]
}

10 changes: 10 additions & 0 deletions test/errors.js
@@ -1,5 +1,7 @@
var Checker = require('../lib/checker');
var Errors = require('../lib/errors');
var assert = require('assert');
var fs = require('fs');

describe('modules/errors', function() {
var checker = new Checker();
Expand Down Expand Up @@ -88,4 +90,12 @@ describe('modules/errors', function() {

assert.ok(errors.isEmpty());
});

it('should report a maximum of 50 errors, by default', function() {
Errors._resetMaxErrors();
var fixture = fs.readFileSync(__dirname + '/data/validate-indentation.js', 'utf8');
checker.configure({ validateIndentation: 2 });
assert(checker.checkString(fixture).getErrorList().length === 50);
Errors._resetMaxErrorsForTests();
});
});
2 changes: 1 addition & 1 deletion test/mocha.opts
@@ -1,3 +1,3 @@
test/*.js test/rules/*.js test/options/*.js test/reporters/*.js
test/test-config.js test/*.js test/rules/*.js test/options/*.js test/reporters/*.js
-R dot
-u bdd
3 changes: 2 additions & 1 deletion test/rules/validate-indentation.js
Expand Up @@ -36,7 +36,8 @@ describe('rules/validate-indentation', function() {
});

it('should validate 2 spaces indentation properly', function() {
checker.configure({ validateIndentation: 2 });
// maxErrors set to avoid this test failing with the default value
checker.configure({ validateIndentation: 2, maxErrors: 500 });
checkErrors(fixture, [
5,
10,
Expand Down
27 changes: 27 additions & 0 deletions test/string-checker.js
@@ -1,4 +1,5 @@
var Checker = require('../lib/checker');
var Errors = require('../lib/errors');
var assert = require('assert');

describe('modules/string-checker', function() {
Expand Down Expand Up @@ -70,6 +71,32 @@ describe('modules/string-checker', function() {
}
});

describe('maxErrors', function() {
beforeEach(function() {
Errors._resetMaxErrors();
checker.configure({
requireSpaceBeforeBinaryOperators: ['='],
maxErrors: 1
});
});

afterEach(function() {
Errors._resetMaxErrorsForTests();
});

it('should allow a maximum number of reported errors to be set', function() {
var errors = checker.checkString('var foo=1;\n var bar=2;').getErrorList();
assert(errors.length === 1);
});

it('should not report more than the maximum errors across multiple checks', function() {
var errors = checker.checkString('var foo=1;\n var bar=2;').getErrorList();
var errors2 = checker.checkString('var baz=1;\n var qux=2;').getErrorList();
assert(errors.length === 1);
assert(errors2.length === 0);
});
});

describe('rules registration', function() {
it('should report rules in config which don\'t match any registered rules', function() {
var error;
Expand Down
24 changes: 24 additions & 0 deletions test/test-config.js
@@ -0,0 +1,24 @@
var Errors = require('../lib/errors');

var largeDefault = 1000000;

// Set an arbitrarily large number of errors for the tests to bypass
// the maxErrors default of 50 errors. Otherwise, the specs would
// hit the limit and fail the suite.
Errors.maxErrors = largeDefault;

/**
* Reset maxErrors to the large default value.
*
* Tests for maxError will need to manipulate the constructor's value
* at runtime and need a way to set the testing default
*/
Errors._resetMaxErrorsForTests = function() {
Errors._resetMaxErrors();
Errors.maxErrors = largeDefault;
};

Errors._resetMaxErrors = function() {
this._errorCount = 0;
this.maxErrors = this._defaultMaxErrors;
};

0 comments on commit 6de72ad

Please sign in to comment.