From 0976e278b9991bd2fd7c867b2739315b3c56748c Mon Sep 17 00:00:00 2001 From: Rogelio Guzman Date: Thu, 27 Sep 2018 16:34:40 -0700 Subject: [PATCH] Small clean up in runESLint and improve test setup (#51) * Add tests for normalizing maxWarnings * Add jest-watch-typeahead and split use multi project in jest config * Remove extra checks for existance of cliOptions * WIP * Add jest-runner-eslint as a runner * Fix eslint issues * Add jest-watch-select-projects * Small refactor to runESLint * Extract computed fix value --- jest.config.js | 40 ++++++- package.json | 1 + src/__tests__/runESLint.test.js | 114 ++++++++++++++++++++ src/runESLint.js | 67 ++++++------ src/utils/__tests__/normalizeConfig.test.js | 14 +++ src/utils/normalizeConfig.js | 11 +- yarn.lock | 16 ++- 7 files changed, 221 insertions(+), 42 deletions(-) create mode 100644 src/__tests__/runESLint.test.js diff --git a/jest.config.js b/jest.config.js index d3cdf76..365ca77 100644 --- a/jest.config.js +++ b/jest.config.js @@ -2,11 +2,41 @@ module.exports = { watchPlugins: [ 'jest-watch-typeahead/filename', 'jest-watch-typeahead/testname', + 'jest-watch-select-projects', ], - testPathIgnorePatterns: ['/examples/', '/node_modules/', '/__eslint__/'], - testMatch: [ - '/src/**/__tests__/**/*.js', - '/integrationTests/*.test.js', - '/integrationTests/**/*.test.js', + projects: [ + { + displayName: 'e2e', + testPathIgnorePatterns: [ + '/examples/', + '/node_modules/', + '/__eslint__/', + '/__fixtures__/', + ], + testMatch: [ + '/integrationTests/*.test.js', + '/integrationTests/**/*.test.js', + ], + }, + { + displayName: 'tests', + testMatch: ['/src/**/__tests__/**/*.js'], + }, + { + displayName: 'lint', + runner: './', + testPathIgnorePatterns: [ + '/examples/', + '/node_modules/', + '/__eslint__/', + '/__fixtures__/', + ], + testMatch: [ + '/src/*.js', + '/src/**/*.js', + '/integrationTests/*.js', + '/integrationTests/**/*.js', + ], + }, ], }; diff --git a/package.json b/package.json index bafd305..df15907 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "eslint-plugin-prettier": "2.2.0", "execa": "0.8.0", "jest": "23.6.0", + "jest-watch-select-projects": "^0.1.1", "jest-watch-typeahead": "^0.2.0", "prettier": "1.5.3" } diff --git a/src/__tests__/runESLint.test.js b/src/__tests__/runESLint.test.js new file mode 100644 index 0000000..bc2d443 --- /dev/null +++ b/src/__tests__/runESLint.test.js @@ -0,0 +1,114 @@ +/* eslint-disable class-methods-use-this, global-require */ +const path = require('path'); + +const runESLintRunnerWithMockedEngine = options => { + jest.resetModules(); + jest.doMock('../utils/getLocalESLint', () => () => { + return { + CLIEngine: class { + isPathIgnored(file) { + return options.cliEngine.ignoredFiles.includes(file); + } + + executeOnFiles() { + return { + errorCount: options.cliEngine.errorCount, + }; + } + + getFormatter() { + return () => {}; + } + }, + }; + }); + const runESLint = require('../runESLint'); + + return runESLint(options.runESLint); +}; + +it('Requires the config setupTestFrameworkScriptFile when specified', () => { + const setupFile = path.join(__dirname, './path/to/setupFile.js'); + + let setupFileWasLoaded = false; + jest.doMock( + setupFile, + () => { + setupFileWasLoaded = true; + }, + { virtual: true }, + ); + + runESLintRunnerWithMockedEngine({ + cliEngine: { + ignoredFiles: ['/path/to/file.test.js'], + errorCount: 0, + }, + runESLint: { + testPath: 'path/to/file.test.js', + config: { + setupTestFrameworkScriptFile: setupFile, + }, + }, + }); + + expect(setupFileWasLoaded).toBeTruthy(); +}); + +it('Returns "skipped" when the test path is ignored', () => { + const result = runESLintRunnerWithMockedEngine({ + cliEngine: { + ignoredFiles: ['/path/to/file.test.js'], + errorCount: 0, + }, + runESLint: { + testPath: '/path/to/file.test.js', + config: {}, + }, + }); + + expect(result).toMatchObject({ + numFailingTests: 0, + numPassingTests: 0, + numPendingTests: 1, + skipped: true, + }); +}); + +it('Returns "passed" when the test passed', () => { + const result = runESLintRunnerWithMockedEngine({ + cliEngine: { + ignoredFiles: [], + errorCount: 0, + }, + runESLint: { + testPath: '/path/to/file.test.js', + config: {}, + }, + }); + + expect(result).toMatchObject({ + numFailingTests: 0, + numPassingTests: 1, + numPendingTests: 0, + }); +}); + +it('Returns "fail" when the test failed', () => { + const result = runESLintRunnerWithMockedEngine({ + cliEngine: { + ignoredFiles: [], + errorCount: 1, + }, + runESLint: { + testPath: '/path/to/file.test.js', + config: {}, + }, + }); + + expect(result).toMatchObject({ + numFailingTests: 1, + numPassingTests: 0, + numPendingTests: 0, + }); +}); diff --git a/src/runESLint.js b/src/runESLint.js index 1944828..d9a9e79 100644 --- a/src/runESLint.js +++ b/src/runESLint.js @@ -2,6 +2,13 @@ const { pass, fail, skip } = require('create-jest-runner'); const getLocalESLint = require('./utils/getLocalESLint'); const getESLintOptions = require('./utils/getESLintOptions'); +const getComputedFixValue = ({ fix, quiet, fixDryRun }) => { + if (fix || fixDryRun) { + return quiet ? ({ severity }) => severity === 2 : true; + } + return undefined; +}; + const runESLint = ({ testPath, config }) => { const start = Date.now(); @@ -11,16 +18,13 @@ const runESLint = ({ testPath, config }) => { } const { CLIEngine } = getLocalESLint(config); - const options = getESLintOptions(config); - const quiet = options.cliOptions && options.cliOptions.quiet; + const { cliOptions } = getESLintOptions(config); const cli = new CLIEngine( - Object.assign({}, options.cliOptions, { - fix: - options.cliOptions && - (options.cliOptions.fix || options.cliOptions.fixDryRun) && - (quiet ? ({ severity }) => severity === 2 : true), + Object.assign({}, cliOptions, { + fix: getComputedFixValue(cliOptions), }), ); + if (cli.isPathIgnored(testPath)) { const end = Date.now(); return skip({ start, end, test: { path: testPath, title: 'ESLint' } }); @@ -28,40 +32,37 @@ const runESLint = ({ testPath, config }) => { const report = cli.executeOnFiles([testPath]); - if ( - options.cliOptions && - options.cliOptions.fix && - !options.cliOptions.fixDryRun - ) { + if (cliOptions.fix && !cliOptions.fixDryRun) { CLIEngine.outputFixes(report); } const end = Date.now(); - const tooManyWarnings = - options.cliOptions && - options.cliOptions.maxWarnings != null && - options.cliOptions.maxWarnings >= 0 && - report.warningCount > options.cliOptions.maxWarnings; - - const format = () => { - const formatter = cli.getFormatter(options.cliOptions.format); - return formatter( - quiet ? CLIEngine.getErrorResults(report.results) : report.results, - ); - }; - - if (report.errorCount > 0 || tooManyWarnings) { - let errorMessage = format(); + const message = cli.getFormatter(cliOptions.format)( + cliOptions.quiet + ? CLIEngine.getErrorResults(report.results) + : report.results, + ); - if (!report.errorCount && tooManyWarnings) - errorMessage += `\nESLint found too many warnings (maximum: ${options - .cliOptions.maxWarnings}).`; + if (report.errorCount > 0) { + return fail({ + start, + end, + test: { path: testPath, title: 'ESLint', errorMessage: message }, + }); + } + const tooManyWarnings = + cliOptions.maxWarnings >= 0 && report.warningCount > cliOptions.maxWarnings; + if (tooManyWarnings) { return fail({ start, end, - test: { path: testPath, title: 'ESLint', errorMessage }, + test: { + path: testPath, + title: 'ESLint', + errorMessage: `${message}\nESLint found too many warnings (maximum: ${cliOptions.maxWarnings}).`, + }, }); } @@ -71,8 +72,8 @@ const runESLint = ({ testPath, config }) => { test: { path: testPath, title: 'ESLint' }, }); - if (!quiet && report.warningCount > 0) { - result.console = [{ message: format(), origin: '', type: 'warn' }]; + if (!cliOptions.quiet && report.warningCount > 0) { + result.console = [{ message, origin: '', type: 'warn' }]; } return result; diff --git a/src/utils/__tests__/normalizeConfig.test.js b/src/utils/__tests__/normalizeConfig.test.js index fcae01e..e2fedab 100644 --- a/src/utils/__tests__/normalizeConfig.test.js +++ b/src/utils/__tests__/normalizeConfig.test.js @@ -97,6 +97,20 @@ it('normalizes global', () => { }); }); +it('normalizes maxWarnings', () => { + expect(normalizeCLIOptions({})).toMatchObject({ + maxWarnings: -1, + }); + + expect(normalizeCLIOptions({ maxWarnings: '10' })).toMatchObject({ + maxWarnings: 10, + }); + + expect(() => normalizeCLIOptions({ maxWarnings: 'not-an-int' })).toThrowError( + `'not-an-int' cannot be converted to a number`, + ); +}); + it('normalizes noIgnore', () => { expect(normalizeCLIOptions({})).toMatchObject({ ignore: true, diff --git a/src/utils/normalizeConfig.js b/src/utils/normalizeConfig.js index e85924b..a68677b 100644 --- a/src/utils/normalizeConfig.js +++ b/src/utils/normalizeConfig.js @@ -1,7 +1,16 @@ const identity = v => v; const negate = v => !v; const asArray = v => (typeof v === 'string' ? [v] : v); -const asInt = v => (typeof v === 'number' ? v : parseInt(v, 10)); +const asInt = v => { + if (typeof v === 'number') { + return v; + } + const int = parseInt(v, 10); + if (Number.isNaN(int)) { + throw new Error(`'${v}' cannot be converted to a number`); + } + return int; +}; const BASE_CONFIG = { cache: { diff --git a/yarn.lock b/yarn.lock index 0cb2568..906383f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -102,7 +102,7 @@ ansi-escapes@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-2.0.0.tgz#5bae52be424878dd9783e8910e3fc2922e83c81b" -ansi-escapes@^3.0.0: +ansi-escapes@^3.0.0, ansi-escapes@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.1.0.tgz#f73207bb81207d75fd6c83f125af26eea378ca30" @@ -2213,7 +2213,7 @@ jest-cli@^23.6.0: which "^1.2.12" yargs "^11.0.0" -jest-config@^23.6.0: +jest-config@^23.0.1, jest-config@^23.6.0: version "23.6.0" resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-23.6.0.tgz#f82546a90ade2d8c7026fbf6ac5207fc22f8eb1d" dependencies: @@ -2439,6 +2439,16 @@ jest-validate@^23.6.0: leven "^2.1.0" pretty-format "^23.6.0" +jest-watch-select-projects@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jest-watch-select-projects/-/jest-watch-select-projects-0.1.1.tgz#08acd5df2ba5328f3183277d5c9f18acfc69a02a" + dependencies: + ansi-escapes "^3.1.0" + chalk "^2.4.1" + eslint "^4.5.0" + jest-config "^23.0.1" + prompts "^0.1.8" + jest-watch-typeahead@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/jest-watch-typeahead/-/jest-watch-typeahead-0.2.0.tgz#dd0cef120360ac75b93c0afbcdcf21a54b3e43c9" @@ -3163,7 +3173,7 @@ progress@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.0.tgz#8a1be366bf8fc23db2bd23f10c6fe920b4389d1f" -prompts@^0.1.9: +prompts@^0.1.8, prompts@^0.1.9: version "0.1.14" resolved "https://registry.yarnpkg.com/prompts/-/prompts-0.1.14.tgz#a8e15c612c5c9ec8f8111847df3337c9cbd443b2" dependencies: