diff --git a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap index 57e9f0d32837..c0cc76370e97 100644 --- a/integration_tests/__tests__/__snapshots__/show_config.test.js.snap +++ b/integration_tests/__tests__/__snapshots__/show_config.test.js.snap @@ -34,6 +34,7 @@ exports[`--showConfig outputs config info and exits 1`] = ` \\"roots\\": [ \\"<>\\" ], + \\"runner\\": \\"jest-runner\\", \\"setupFiles\\": [], \\"snapshotSerializers\\": [], \\"testEnvironment\\": \\"jest-environment-jsdom\\", diff --git a/packages/jest-cli/src/run_jest.js b/packages/jest-cli/src/run_jest.js index 820fce201526..99aa26746902 100644 --- a/packages/jest-cli/src/run_jest.js +++ b/packages/jest-cli/src/run_jest.js @@ -200,7 +200,7 @@ const runJest = async ({ const results = await new TestScheduler(globalConfig, { startRun, - }).runTests(allTests, testWatcher); + }).scheduleTests(allTests, testWatcher); sequencer.cacheResults(allTests, results); diff --git a/packages/jest-cli/src/test_scheduler.js b/packages/jest-cli/src/test_scheduler.js index 6934a5e0d839..60e1a6b89674 100644 --- a/packages/jest-cli/src/test_scheduler.js +++ b/packages/jest-cli/src/test_scheduler.js @@ -31,6 +31,10 @@ import VerboseReporter from './reporters/verbose_reporter'; const SLOW_TEST_TIME = 3000; +// The default jest-runner is required because it is the default test runner +// and required implicitly through the `runner` ProjectConfig option. +TestRunner; + export type TestSchedulerOptions = {| startRun: (globalConfig: GlobalConfig) => *, |}; @@ -39,12 +43,10 @@ class TestScheduler { _dispatcher: ReporterDispatcher; _globalConfig: GlobalConfig; _options: TestSchedulerOptions; - _testRunner: TestRunner; constructor(globalConfig: GlobalConfig, options: TestSchedulerOptions) { this._dispatcher = new ReporterDispatcher(); this._globalConfig = globalConfig; - this._testRunner = new TestRunner(globalConfig); this._options = options; this._setupReporters(); } @@ -57,7 +59,7 @@ class TestScheduler { this._dispatcher.unregister(ReporterClass); } - async runTests(tests: Array, watcher: TestWatcher) { + async scheduleTests(tests: Array, watcher: TestWatcher) { const onStart = this._dispatcher.onTestStart.bind(this._dispatcher); const timings = []; const contexts = new Set(); @@ -138,20 +140,36 @@ class TestScheduler { showStatus: !runInBand, }); - try { - await this._testRunner.runTests( - tests, - watcher, - onStart, - onResult, - onFailure, - { - serial: runInBand, - }, - ); - } catch (error) { - if (!watcher.isInterrupted()) { - throw error; + const testRunners = Object.create(null); + contexts.forEach(({config}) => { + if (!testRunners[config.runner]) { + // $FlowFixMe + testRunners[config.runner] = new (require(config.runner): TestRunner)( + this._globalConfig, + ); + } + }); + + const testsByRunner = this._partitionTests(testRunners, tests); + + if (testsByRunner) { + try { + for (const runner of Object.keys(testRunners)) { + await testRunners[runner].runTests( + testsByRunner[runner], + watcher, + onStart, + onResult, + onFailure, + { + serial: runInBand, + }, + ); + } + } catch (error) { + if (!watcher.isInterrupted()) { + throw error; + } } } @@ -174,6 +192,29 @@ class TestScheduler { return aggregatedResults; } + _partitionTests( + testRunners: {[key: string]: TestRunner}, + tests: Array, + ) { + if (Object.keys(testRunners).length > 1) { + return tests.reduce((testRuns, test) => { + const runner = test.context.config.runner; + if (!testRuns[runner]) { + testRuns[runner] = []; + } + testRuns[runner].push(test); + return testRuns; + }, Object.create(null)); + } else if (tests.length > 0 && tests[0] != null) { + // If there is only one runner, don't partition the tests. + return Object.assign(Object.create(null), { + [tests[0].context.config.runner]: tests, + }); + } else { + return null; + } + } + _shouldAddDefaultReporters(reporters?: Array): boolean { return ( !reporters || diff --git a/packages/jest-config/src/defaults.js b/packages/jest-config/src/defaults.js index a6814b4995c8..08c098309685 100644 --- a/packages/jest-config/src/defaults.js +++ b/packages/jest-config/src/defaults.js @@ -52,6 +52,7 @@ module.exports = ({ preset: null, resetMocks: false, resetModules: false, + runner: 'jest-runner', snapshotSerializers: [], testEnvironment: 'jest-environment-jsdom', testFailureExitCode: 1, diff --git a/packages/jest-config/src/index.js b/packages/jest-config/src/index.js index 029be103061b..d5ff0416e7c6 100644 --- a/packages/jest-config/src/index.js +++ b/packages/jest-config/src/index.js @@ -134,6 +134,7 @@ const getConfigs = ( resolver: options.resolver, rootDir: options.rootDir, roots: options.roots, + runner: options.runner, setupFiles: options.setupFiles, setupTestFrameworkScriptFile: options.setupTestFrameworkScriptFile, skipNodeResolution: options.skipNodeResolution, diff --git a/packages/jest-config/src/normalize.js b/packages/jest-config/src/normalize.js index 2ce502d145b7..6bf35faca52c 100644 --- a/packages/jest-config/src/normalize.js +++ b/packages/jest-config/src/normalize.js @@ -387,6 +387,7 @@ function normalize(options: InitialOptions, argv: Argv) { break; case 'moduleLoader': case 'resolver': + case 'runner': case 'setupTestFrameworkScriptFile': case 'testResultsProcessor': case 'testRunner': diff --git a/packages/jest-runner/src/run_test.js b/packages/jest-runner/src/run_test.js index 4c7fde3c18ca..b1e9a2d2869f 100644 --- a/packages/jest-runner/src/run_test.js +++ b/packages/jest-runner/src/run_test.js @@ -23,9 +23,14 @@ import { getConsoleOutput, setGlobal, } from 'jest-util'; +import jasmine2 from 'jest-jasmine2'; import {getTestEnvironment} from 'jest-config'; import docblock from 'jest-docblock'; +// The default jest-runner is required because it is the default test runner +// and required implicitly through the `testRunner` ProjectConfig option. +jasmine2; + function runTest( path: Path, globalConfig: GlobalConfig, diff --git a/test_utils.js b/test_utils.js index 72eedae13961..82f55427b889 100644 --- a/test_utils.js +++ b/test_utils.js @@ -76,6 +76,7 @@ const DEFAULT_PROJECT_CONFIG: ProjectConfig = { resolver: null, rootDir: '/test_root_dir/', roots: [], + runner: 'jest-runner', setupFiles: [], setupTestFrameworkScriptFile: null, skipNodeResolution: false, diff --git a/types/Config.js b/types/Config.js index e3f38882ef80..8d17d4b06ced 100644 --- a/types/Config.js +++ b/types/Config.js @@ -44,6 +44,7 @@ export type DefaultOptions = {| preset: ?string, resetMocks: boolean, resetModules: boolean, + runner: string, snapshotSerializers: Array, testEnvironment: string, testFailureExitCode: string | number, @@ -105,6 +106,7 @@ export type InitialOptions = { resolver?: ?Path, rootDir: Path, roots?: Array, + runner?: string, scriptPreprocessor?: string, setupFiles?: Array, setupTestFrameworkScriptFile?: Path, @@ -115,8 +117,8 @@ export type InitialOptions = { testFailureExitCode?: string | number, testMatch?: Array, testNamePattern?: string, - testPathIgnorePatterns?: Array, testPathDirs?: Array, + testPathIgnorePatterns?: Array, testRegex?: string, testResultsProcessor?: ?string, testRunner?: string, @@ -197,6 +199,7 @@ export type ProjectConfig = {| resolver: ?Path, rootDir: Path, roots: Array, + runner: string, setupFiles: Array, setupTestFrameworkScriptFile: ?Path, skipNodeResolution: boolean,