From 5ba5f3ba6e0279dc251f801fa86535ac8fe8b24a Mon Sep 17 00:00:00 2001 From: Dmitrii Abramov Date: Tue, 18 Jul 2017 12:39:57 -0700 Subject: [PATCH] --testFailureExitCode argument --- .../__snapshots__/show_config.test.js.snap | 1 + .../__tests__/test_failure_exit_code.test.js | 46 ++++++++++++++++++ packages/jest-cli/src/cli/args.js | 4 ++ packages/jest-cli/src/cli/index.js | 48 +++++++++++-------- packages/jest-config/src/defaults.js | 1 + packages/jest-config/src/index.js | 1 + packages/jest-config/src/normalize.js | 3 ++ packages/jest-config/src/valid_config.js | 1 + types/Argv.js | 1 + types/Config.js | 3 ++ 10 files changed, 90 insertions(+), 19 deletions(-) create mode 100644 integration_tests/__tests__/test_failure_exit_code.test.js diff --git a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap index ea4364270fb3..5911376175f3 100644 --- a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap +++ b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap @@ -69,6 +69,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"nonFlagArgs\\": [], \\"notify\\": false, \\"rootDir\\": \\"<>\\", + \\"testFailureExitCode\\": 1, \\"testPathPattern\\": \\"\\", \\"testResultsProcessor\\": null, \\"updateSnapshot\\": \\"all\\", diff --git a/integration_tests/__tests__/test_failure_exit_code.test.js b/integration_tests/__tests__/test_failure_exit_code.test.js new file mode 100644 index 000000000000..b4253a95eb23 --- /dev/null +++ b/integration_tests/__tests__/test_failure_exit_code.test.js @@ -0,0 +1,46 @@ +/** + * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. + * + * This source code is licensed under the BSD-style license found in the + * LICENSE file in the root directory of this source tree. An additional grant + * of patent rights can be found in the PATENTS file in the same directory. + */ + +'use strict'; + +const path = require('path'); +const os = require('os'); +const skipOnWindows = require('skipOnWindows'); +const {cleanup, writeFiles} = require('../utils'); +const runJest = require('../runJest'); + +const DIR = path.resolve(os.tmpdir(), 'test_failure_exit_code_test'); + +skipOnWindows.suite(); + +beforeEach(() => cleanup(DIR)); +afterAll(() => cleanup(DIR)); + +test('exits with a specified code when test fail', () => { + writeFiles(DIR, { + '__tests__/test.test.js': `test('test', () => { expect(1).toBe(2); });`, + 'package.json': JSON.stringify({ + jest: {testEnvironment: 'node', testFailureExitCode: 99}, + }), + }); + + let {status} = runJest(DIR); + expect(status).toBe(99); + + ({status} = runJest(DIR, ['--testFailureExitCode', '77'])); + expect(status).toBe(77); + + writeFiles(DIR, { + '__tests__/test.test.js': `test('test', () => { expect(1).toBe(2); });`, + 'package.json': JSON.stringify({ + jest: {testEnvironment: 'node'}, + }), + }); + ({status} = runJest(DIR)); + expect(status).toBe(1); +}); diff --git a/packages/jest-cli/src/cli/args.js b/packages/jest-cli/src/cli/args.js index 9c1c03312ade..d320b8f616ab 100644 --- a/packages/jest-cli/src/cli/args.js +++ b/packages/jest-cli/src/cli/args.js @@ -407,6 +407,10 @@ const options = { description: 'Alias for --env', type: 'string', }, + testFailureExitCode: { + description: 'Exit code of `jest` command if the test run failed', + type: 'string', // number + }, testMatch: { description: 'The glob patterns Jest uses to detect test files.', type: 'array', diff --git a/packages/jest-cli/src/cli/index.js b/packages/jest-cli/src/cli/index.js index 12400fa058b6..a9a51ac17565 100644 --- a/packages/jest-cli/src/cli/index.js +++ b/packages/jest-cli/src/cli/index.js @@ -34,21 +34,22 @@ import TestWatcher from '../test_watcher'; import watch from '../watch'; import yargs from 'yargs'; -function run(maybeArgv?: Argv, project?: Path) { +async function run(maybeArgv?: Argv, project?: Path) { const argv: Argv = _buildArgv(maybeArgv, project); const projects = _getProjectListFromCLIArgs(argv, project); // If we're running a single Jest project, we might want to use another // version of Jest (the one that is specified in this project's package.json) const runCLIFn = _getRunCLIFn(projects); - runCLIFn(argv, projects, result => _readResultsAndExit(argv, result)); + const {results, globalConfig} = await runCLIFn(argv, projects); + _readResultsAndExit(argv, results, globalConfig); } const runCLI = async ( argv: Argv, projects: Array, - onComplete: (results: ?AggregatedResult) => void, -) => { +): Promise<{results: AggregatedResult, globalConfig: GlobalConfig}> => { + let results; // Optimize 'fs' module and make it more compatible with multiple platforms. _patchGlobalFSModule(); @@ -56,7 +57,7 @@ const runCLI = async ( // it'll break the JSON structure and it won't be valid. const outputStream = argv.json ? process.stderr : process.stdout; - argv.version && _printVersionAndExit(outputStream, onComplete); + argv.version && _printVersionAndExit(outputStream); try { const {globalConfig, configs, hasDeprecationWarnings} = _getConfigs( @@ -64,27 +65,37 @@ const runCLI = async ( argv, outputStream, ); + await _run( globalConfig, configs, hasDeprecationWarnings, outputStream, - onComplete, + (r: AggregatedResult) => (results = r), ); + + if (!results) { + throw new Error( + 'AggregatedResult must be present after test run is complete', + ); + } + + return Promise.resolve({globalConfig, results}); } catch (error) { - _printErrorAndExit(error); + clearLine(process.stderr); + clearLine(process.stdout); + console.error(chalk.red(error.stack)); + process.exit(1); + throw error; } }; -const _printErrorAndExit = error => { - clearLine(process.stderr); - clearLine(process.stdout); - console.error(chalk.red(error.stack)); - process.exit(1); -}; - -const _readResultsAndExit = (argv: Argv, result: ?AggregatedResult) => { - const code = !result || result.success ? 0 : 1; +const _readResultsAndExit = ( + argv: Argv, + result: ?AggregatedResult, + globalConfig: GlobalConfig, +) => { + const code = !result || result.success ? 0 : globalConfig.testFailureExitCode; process.on('exit', () => process.exit(code)); if (argv && argv.forceExit) { process.exit(code); @@ -136,10 +147,9 @@ const _printDebugInfoAndExitIfNeeded = ( } }; -const _printVersionAndExit = (outputStream, onComplete) => { +const _printVersionAndExit = outputStream => { outputStream.write(`v${VERSION}\n`); - onComplete && onComplete(); - return; + process.exit(0); }; // Possible scenarios: diff --git a/packages/jest-config/src/defaults.js b/packages/jest-config/src/defaults.js index 5e29c41fda12..a6814b4995c8 100644 --- a/packages/jest-config/src/defaults.js +++ b/packages/jest-config/src/defaults.js @@ -54,6 +54,7 @@ module.exports = ({ resetModules: false, snapshotSerializers: [], testEnvironment: 'jest-environment-jsdom', + testFailureExitCode: 1, testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)(spec|test).js?(x)'], testPathIgnorePatterns: [NODE_MODULES_REGEXP], testRegex: '', diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index efcd6c7d8665..da835ee3710a 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -89,6 +89,7 @@ const getConfigs = ( reporters: options.reporters, rootDir: options.rootDir, silent: options.silent, + testFailureExitCode: options.testFailureExitCode, testNamePattern: options.testNamePattern, testPathPattern: options.testPathPattern, testResultsProcessor: options.testResultsProcessor, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 8f0084fd1090..6a9558e8b4f7 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -471,6 +471,7 @@ function normalize(options: InitialOptions, argv: Argv) { case 'silent': case 'skipNodeResolution': case 'testEnvironment': + case 'testFailureExitCode': case 'testNamePattern': case 'testRegex': case 'testURL': @@ -492,6 +493,8 @@ function normalize(options: InitialOptions, argv: Argv) { newOptions.json = argv.json; newOptions.lastCommit = argv.lastCommit; + newOptions.testFailureExitCode = parseInt(newOptions.testFailureExitCode); + if (argv.all) { newOptions.onlyChanged = false; } diff --git a/packages/jest-config/src/valid_config.js b/packages/jest-config/src/valid_config.js index 060f918438ab..6d6f89f23bbd 100644 --- a/packages/jest-config/src/valid_config.js +++ b/packages/jest-config/src/valid_config.js @@ -74,6 +74,7 @@ module.exports = ({ skipNodeResolution: false, snapshotSerializers: ['my-serializer-module'], testEnvironment: 'jest-environment-jsdom', + testFailureExitCode: 1, testMatch: ['**/__tests__/**/*.js?(x)', '**/?(*.)(spec|test).js?(x)'], testNamePattern: 'test signature', testPathIgnorePatterns: [NODE_MODULES_REGEXP], diff --git a/types/Argv.js b/types/Argv.js index 25ae5c7a83b9..820153e01bef 100644 --- a/types/Argv.js +++ b/types/Argv.js @@ -65,6 +65,7 @@ export type Argv = {| silent: boolean, snapshotSerializers: Array, testEnvironment: string, + testFailureExitCode: ?string, testMatch: Array, testNamePattern: string, testPathIgnorePatterns: Array, diff --git a/types/Config.js b/types/Config.js index c1f07060ef35..5e1b2c6f1520 100644 --- a/types/Config.js +++ b/types/Config.js @@ -46,6 +46,7 @@ export type DefaultOptions = {| resetModules: boolean, snapshotSerializers: Array, testEnvironment: string, + testFailureExitCode: string | number, testMatch: Array, testPathIgnorePatterns: Array, testRegex: string, @@ -109,6 +110,7 @@ export type InitialOptions = {| skipNodeResolution: boolean, snapshotSerializers?: Array, testEnvironment?: string, + testFailureExitCode?: string | number, testMatch?: Array, testNamePattern?: string, testPathIgnorePatterns?: Array, @@ -157,6 +159,7 @@ export type GlobalConfig = {| reporters: Array, rootDir: Path, silent: boolean, + testFailureExitCode: number, testNamePattern: string, testPathPattern: string, testResultsProcessor: ?string,