Create an Eslint reporter (#6921)
This commit is contained in:
Родитель
acdb2678f0
Коммит
5a055623d1
10
README.md
10
README.md
|
@ -98,6 +98,16 @@ If you want to run all tests and exit, type:
|
|||
yarn test-once
|
||||
```
|
||||
|
||||
### Eslint
|
||||
|
||||
As you run tests you will see a report of Eslint errors at the end of the test output:
|
||||
|
||||
yarn test
|
||||
|
||||
If you would like to run tests without Eslint checks, set an environment variable:
|
||||
|
||||
NO_ESLINT=1 yarn test
|
||||
|
||||
### Flow
|
||||
|
||||
There is limited support for using [Flow](https://flowtype.org/) to validate the intention of our program.
|
||||
|
|
|
@ -21,6 +21,7 @@ module.exports = {
|
|||
'<rootDir>/tests/jest-reporters/fingers-crossed.js',
|
||||
'<rootDir>/tests/jest-reporters/summary.js',
|
||||
'<rootDir>/tests/jest-reporters/flow-check.js',
|
||||
'<rootDir>/tests/jest-reporters/eslint-check.js',
|
||||
],
|
||||
setupTestFrameworkScriptFile: '<rootDir>/tests/setup.js',
|
||||
testPathIgnorePatterns: [
|
||||
|
|
|
@ -176,7 +176,8 @@
|
|||
"NODE_ICU_DATA": "./node_modules/full-icu",
|
||||
"NODE_PATH": "./:./src",
|
||||
"NODE_ENV": "test",
|
||||
"NO_FLOW": "1"
|
||||
"NO_FLOW": "1",
|
||||
"NO_ESLINT": "1"
|
||||
}
|
||||
},
|
||||
"webpack-dev-server": {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
/* eslint-disable no-console */
|
||||
const { CLIEngine } = require('eslint');
|
||||
|
||||
const { getChangedFiles } = require('./utils');
|
||||
|
||||
const NO_ESLINT_ENV_VAR = 'NO_ESLINT';
|
||||
|
||||
class EslintCheckReporter {
|
||||
constructor() {
|
||||
this.eslint = new CLIEngine();
|
||||
this.eslintOutput = null;
|
||||
}
|
||||
|
||||
isDisabled() {
|
||||
return process.env[NO_ESLINT_ENV_VAR] === '1';
|
||||
}
|
||||
|
||||
async onRunStart() {
|
||||
if (this.isDisabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const files = await getChangedFiles();
|
||||
|
||||
if (!files) {
|
||||
throw new Error(`Failed to retrieve files in the eslint check reporter.`);
|
||||
}
|
||||
|
||||
const report = this.eslint.executeOnFiles(files);
|
||||
|
||||
if (report.errorCount === 0 && report.warningCount === 0) {
|
||||
// All good.
|
||||
this.eslintOutput = null;
|
||||
} else {
|
||||
this.eslintOutput = CLIEngine.getFormatter()(report.results);
|
||||
}
|
||||
}
|
||||
|
||||
getLastError() {
|
||||
if (this.isDisabled()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
console.log('');
|
||||
if (this.eslintOutput) {
|
||||
console.log(this.eslintOutput);
|
||||
console.log(
|
||||
`Set ${NO_ESLINT_ENV_VAR}=1 in the environment to disable eslint checks`,
|
||||
);
|
||||
return new Error('eslint errors');
|
||||
}
|
||||
console.log('Eslint: no errors 💄 ✨');
|
||||
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EslintCheckReporter;
|
|
@ -0,0 +1,45 @@
|
|||
const util = require('util');
|
||||
const exec = util.promisify(require('child_process').exec);
|
||||
|
||||
const filterFileNamesFromGitStatusOutput = (output) => {
|
||||
const files = output
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
// Make sure we ignore deleted files.
|
||||
.filter((line) => !line.startsWith('D'))
|
||||
.map((line) => {
|
||||
// Return the new name of a renamed file.
|
||||
if (line.startsWith('RM')) {
|
||||
return line.substring(line.indexOf('->'));
|
||||
}
|
||||
|
||||
return line;
|
||||
})
|
||||
.map((line) => line.split(' '))
|
||||
// .flatMap()
|
||||
.reduce((chunks, chunk) => chunks.concat(chunk), [])
|
||||
// We assume no filename can be smaller than 3 to filter the short format
|
||||
// statuses: https://git-scm.com/docs/git-status#_short_format.
|
||||
.filter((chunk) => chunk.length > 3)
|
||||
// Allow directories OR JS/JSX files
|
||||
.filter((filename) => /(\/|\.jsx?)$/.test(filename));
|
||||
|
||||
return files;
|
||||
};
|
||||
|
||||
const getChangedFiles = async () => {
|
||||
// We use the Porcelain Format Version 1,
|
||||
// See: https://git-scm.com/docs/git-status#_porcelain_format_version_1
|
||||
const { stdout, stderr } = await exec('git status --porcelain=1');
|
||||
|
||||
if (stderr) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return filterFileNamesFromGitStatusOutput(stdout);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
filterFileNamesFromGitStatusOutput,
|
||||
getChangedFiles,
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
import { filterFileNamesFromGitStatusOutput } from 'tests/jest-reporters/utils';
|
||||
|
||||
describe(__filename, () => {
|
||||
describe('_filterFileNamesFromGitStatusOutput', () => {
|
||||
const _filterFileNamesFromGitStatusOutput = (lines = []) => {
|
||||
return filterFileNamesFromGitStatusOutput(lines.join('\n'));
|
||||
};
|
||||
|
||||
it('does not return deleted files', () => {
|
||||
const files = _filterFileNamesFromGitStatusOutput([
|
||||
'D src/core/browserWindow.js',
|
||||
' D src/core/browserWindow.js',
|
||||
]);
|
||||
|
||||
expect(files).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('only returned the new filename of a renamed file', () => {
|
||||
const files = _filterFileNamesFromGitStatusOutput([
|
||||
'RM src/amo/sagas/categories.js -> src/core/sagas/categories.js',
|
||||
]);
|
||||
|
||||
expect(files).toEqual(['src/core/sagas/categories.js']);
|
||||
});
|
||||
|
||||
it('returns the filenames without the git statuses', () => {
|
||||
const files = _filterFileNamesFromGitStatusOutput([
|
||||
'M jest.config.js',
|
||||
'A tests/jest-reporters/eslint-check.js',
|
||||
'AM tests/jest-reporters/utils.js',
|
||||
'?? tests/unit/jest-reporters/',
|
||||
]);
|
||||
|
||||
expect(files).toEqual([
|
||||
'jest.config.js',
|
||||
'tests/jest-reporters/eslint-check.js',
|
||||
'tests/jest-reporters/utils.js',
|
||||
'tests/unit/jest-reporters/',
|
||||
]);
|
||||
});
|
||||
|
||||
it('only returns JavaScript files', () => {
|
||||
const files = _filterFileNamesFromGitStatusOutput([
|
||||
'M README.md',
|
||||
'M package.json',
|
||||
'M src/test.jsx',
|
||||
' M tests/unit/jest-reporters/test_utils.js',
|
||||
]);
|
||||
|
||||
expect(files).toEqual([
|
||||
'src/test.jsx',
|
||||
'tests/unit/jest-reporters/test_utils.js',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
Загрузка…
Ссылка в новой задаче