Skip to content

Commit

Permalink
feat: support reporters written in ESM (#11427)
Browse files Browse the repository at this point in the history
  • Loading branch information
SimenB committed May 20, 2021
1 parent 59f42d8 commit 9633a26
Show file tree
Hide file tree
Showing 7 changed files with 72 additions and 31 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -2,7 +2,6 @@

### Features

- `[jest-config, jest-haste-map, jest-resolve, jest-runner, jest-runtime, jest-test-sequencer, jest-transform, jest-types]` [**BREAKING**] Add custom HasteMap class implementation config option ([#11107](https://github.com/facebook/jest/pull/11107))
- `[babel-jest]` Add async transformation ([#11192](https://github.com/facebook/jest/pull/11192))
- `[jest-changed-files]` Use '--' to separate paths from revisions ([#11160](https://github.com/facebook/jest/pull/11160))
- `[jest-circus]` [**BREAKING**] Fail tests when multiple `done()` calls are made ([#10624](https://github.com/facebook/jest/pull/10624))
Expand All @@ -13,13 +12,15 @@
- `[jest-config, jest-runtime]` Support ESM for files other than `.js` and `.mjs` ([#10823](https://github.com/facebook/jest/pull/10823))
- `[jest-config, jest-runtime]` [**BREAKING**] Use "modern" implementation as default for fake timers ([#10874](https://github.com/facebook/jest/pull/10874) & [#11197](https://github.com/facebook/jest/pull/11197))
- `[jest-config` Allow passing `forceNodeFilesystemAPI` through to `jest-haste-map` ([#11264](https://github.com/facebook/jest/pull/11264))
- `[jest-config, jest-haste-map, jest-resolve, jest-runner, jest-runtime, jest-test-sequencer, jest-transform, jest-types]` [**BREAKING**] Add custom HasteMap class implementation config option ([#11107](https://github.com/facebook/jest/pull/11107))
- `[jest-core]` make `TestWatcher` extend `emittery` ([#10324](https://github.com/facebook/jest/pull/10324))
- `[jest-core]` Run failed tests interactively the same way we do with snapshots ([#10858](https://github.com/facebook/jest/pull/10858))
- `[jest-core]` more `TestSequencer` methods can be async ([#10980](https://github.com/facebook/jest/pull/10980))
- `[jest-core]` Add support for `testSequencer` written in ESM ([#11207](https://github.com/facebook/jest/pull/11207))
- `[jest-core]` Add support for `globalSetup` and `globalTeardown` written in ESM ([#11267](https://github.com/facebook/jest/pull/11267))
- `[jest-core]` Add support for `watchPlugins` written in ESM ([#11315](https://github.com/facebook/jest/pull/11315))
- `[jest-core]` Add support for `runner` written in ESM ([#11232](https://github.com/facebook/jest/pull/11232))
- `[jest-core]` Add support for `reporters` written in ESM ([#11427](https://github.com/facebook/jest/pull/11427))
- `[jest-each]` Add support for interpolation with object properties ([#11388](https://github.com/facebook/jest/pull/11388))
- `[jest-environment-node]` Add AbortController to globals ([#11182](https://github.com/facebook/jest/pull/11182))
- `[@jest/fake-timers]` Update to `@sinonjs/fake-timers` to v7 ([#11198](https://github.com/facebook/jest/pull/11198))
Expand Down Expand Up @@ -117,6 +118,7 @@
- `[babel-jest]` [**BREAKING**] Migrate to ESM ([#11193](https://github.com/facebook/jest/pull/11193))
- `[docs]` Correct example using `browser-resolve` ([#11140](https://github.com/facebook/jest/pull/11140))
- `[docs]` Clarify `timers` configuration property ([#11376](https://github.com/facebook/jest/pull/11376))
- `[jest, jest-core]` [**BREAKING**] Replace `TestScheduler` export with `createTestScheduler` ([#11427](https://github.com/facebook/jest/pull/11427))
- `[jest-config]` [**BREAKING**] Remove `enabledTestsMap` config, use `filter` instead ([#10787](https://github.com/facebook/jest/pull/10787))
- `[jest-console]` [**BREAKING**] Move `root` into `config` and take `GlobalConfig` as mandatory parameter for `getConsoleOutput` ([#10126](https://github.com/facebook/jest/pull/10126))
- `[jest-console]` Export LogEntry ([#11017](https://github.com/facebook/jest/pull/11017))
Expand Down
26 changes: 26 additions & 0 deletions e2e/__tests__/customReporters.test.ts
Expand Up @@ -8,6 +8,7 @@
import {tmpdir} from 'os';
import * as path from 'path';
import {wrap} from 'jest-snapshot-serializer-raw';
import {onNodeVersions} from '@jest/test-utils';
import {cleanup, extractSummary, writeFiles} from '../Utils';
import runJest from '../runJest';

Expand Down Expand Up @@ -159,4 +160,29 @@ describe('Custom Reporters Integration', () => {
expect(stderr).toMatch(/ON_RUN_START_ERROR/);
expect(exitCode).toBe(1);
});

onNodeVersions('^12.17.0 || >=13.2.0', () => {
test('supports reporter written in ESM', () => {
writeFiles(DIR, {
'__tests__/test.test.js': `test('test', () => {});`,
'package.json': JSON.stringify({
jest: {
reporters: ['default', '<rootDir>/reporter.mjs'],
testEnvironment: 'node',
},
}),
'reporter.mjs': `
export default class Reporter {
onRunStart() {
throw new Error('ON_RUN_START_ERROR');
}
};
`,
});

const {stderr, exitCode} = runJest(DIR);
expect(stderr).toMatch(/ON_RUN_START_ERROR/);
expect(exitCode).toBe(1);
});
});
});
33 changes: 22 additions & 11 deletions packages/jest-core/src/TestScheduler.ts
Expand Up @@ -31,7 +31,7 @@ import {formatExecError} from 'jest-message-util';
import TestRunner, {Test} from 'jest-runner';
import type {Context} from 'jest-runtime';
import snapshot = require('jest-snapshot');
import {interopRequireDefault} from 'jest-util';
import {requireOrImportModule} from 'jest-util';
import ReporterDispatcher from './ReporterDispatcher';
import type TestWatcher from './TestWatcher';
import {shouldRunInBand} from './testSchedulerHelper';
Expand All @@ -49,7 +49,20 @@ export type TestSchedulerContext = {
changedFiles?: Set<Config.Path>;
sourcesRelatedToTestsInChangedFiles?: Set<Config.Path>;
};
export default class TestScheduler {

export async function createTestScheduler(
globalConfig: Config.GlobalConfig,
options: TestSchedulerOptions,
context: TestSchedulerContext,
): Promise<TestScheduler> {
const scheduler = new TestScheduler(globalConfig, options, context);

await scheduler._setupReporters();

return scheduler;
}

class TestScheduler {
private readonly _dispatcher: ReporterDispatcher;
private readonly _globalConfig: Config.GlobalConfig;
private readonly _options: TestSchedulerOptions;
Expand All @@ -64,7 +77,6 @@ export default class TestScheduler {
this._globalConfig = globalConfig;
this._options = options;
this._context = context;
this._setupReporters();
}

addReporter(reporter: Reporter): void {
Expand Down Expand Up @@ -337,7 +349,7 @@ export default class TestScheduler {
);
}

private _setupReporters() {
async _setupReporters() {
const {collectCoverage, notify, reporters} = this._globalConfig;
const isDefault = this._shouldAddDefaultReporters(reporters);

Expand Down Expand Up @@ -366,7 +378,7 @@ export default class TestScheduler {
}

if (reporters && Array.isArray(reporters)) {
this._addCustomReporters(reporters);
await this._addCustomReporters(reporters);
}
}

Expand All @@ -390,17 +402,16 @@ export default class TestScheduler {
this.addReporter(new SummaryReporter(this._globalConfig));
}

private _addCustomReporters(
private async _addCustomReporters(
reporters: Array<string | Config.ReporterConfig>,
) {
reporters.forEach(reporter => {
for (const reporter of reporters) {
const {options, path} = this._getReporterProps(reporter);

if (path === 'default') return;
if (path === 'default') continue;

try {
// TODO: Use `requireAndTranspileModule` for Jest 26
const Reporter = interopRequireDefault(require(path)).default;
const Reporter = await requireOrImportModule<any>(path, true);
this.addReporter(new Reporter(this._globalConfig, options));
} catch (error) {
error.message =
Expand All @@ -410,7 +421,7 @@ export default class TestScheduler {
error.message;
throw error;
}
});
}
}

/**
Expand Down
28 changes: 14 additions & 14 deletions packages/jest-core/src/__tests__/TestScheduler.test.js
Expand Up @@ -8,7 +8,7 @@

import {SummaryReporter} from '@jest/reporters';
import {makeProjectConfig} from '@jest/test-utils';
import TestScheduler from '../TestScheduler';
import {createTestScheduler} from '../TestScheduler';
import * as testSchedulerHelper from '../testSchedulerHelper';

jest.mock('@jest/reporters');
Expand All @@ -35,8 +35,8 @@ beforeEach(() => {
spyShouldRunInBand.mockClear();
});

test('config for reporters supports `default`', () => {
const undefinedReportersScheduler = new TestScheduler(
test('config for reporters supports `default`', async () => {
const undefinedReportersScheduler = await createTestScheduler(
{
reporters: undefined,
},
Expand All @@ -45,7 +45,7 @@ test('config for reporters supports `default`', () => {
const numberOfReporters =
undefinedReportersScheduler._dispatcher._reporters.length;

const stringDefaultReportersScheduler = new TestScheduler(
const stringDefaultReportersScheduler = await createTestScheduler(
{
reporters: ['default'],
},
Expand All @@ -55,7 +55,7 @@ test('config for reporters supports `default`', () => {
numberOfReporters,
);

const defaultReportersScheduler = new TestScheduler(
const defaultReportersScheduler = await createTestScheduler(
{
reporters: [['default', {}]],
},
Expand All @@ -65,7 +65,7 @@ test('config for reporters supports `default`', () => {
numberOfReporters,
);

const emptyReportersScheduler = new TestScheduler(
const emptyReportersScheduler = await createTestScheduler(
{
reporters: [],
},
Expand All @@ -74,8 +74,8 @@ test('config for reporters supports `default`', () => {
expect(emptyReportersScheduler._dispatcher._reporters.length).toBe(0);
});

test('.addReporter() .removeReporter()', () => {
const scheduler = new TestScheduler({}, {});
test('.addReporter() .removeReporter()', async () => {
const scheduler = await createTestScheduler({}, {});
const reporter = new SummaryReporter();
scheduler.addReporter(reporter);
expect(scheduler._dispatcher._reporters).toContain(reporter);
Expand All @@ -84,7 +84,7 @@ test('.addReporter() .removeReporter()', () => {
});

test('schedule tests run in parallel per default', async () => {
const scheduler = new TestScheduler({}, {});
const scheduler = await createTestScheduler({}, {});
const test = {
context: {
config: makeProjectConfig({
Expand All @@ -107,7 +107,7 @@ test('schedule tests run in parallel per default', async () => {
});

test('schedule tests run in serial if the runner flags them', async () => {
const scheduler = new TestScheduler({}, {});
const scheduler = await createTestScheduler({}, {});
const test = {
context: {
config: makeProjectConfig({
Expand All @@ -130,7 +130,7 @@ test('schedule tests run in serial if the runner flags them', async () => {
});

test('should bail after `n` failures', async () => {
const scheduler = new TestScheduler({bail: 2}, {});
const scheduler = await createTestScheduler({bail: 2}, {});
const test = {
context: {
config: makeProjectConfig({
Expand Down Expand Up @@ -162,7 +162,7 @@ test('should bail after `n` failures', async () => {
});

test('should not bail if less than `n` failures', async () => {
const scheduler = new TestScheduler({bail: 2}, {});
const scheduler = await createTestScheduler({bail: 2}, {});
const test = {
context: {
config: makeProjectConfig({
Expand Down Expand Up @@ -194,7 +194,7 @@ test('should not bail if less than `n` failures', async () => {
});

test('should set runInBand to run in serial', async () => {
const scheduler = new TestScheduler({}, {});
const scheduler = await createTestScheduler({}, {});
const test = {
context: {
config: makeProjectConfig({
Expand All @@ -220,7 +220,7 @@ test('should set runInBand to run in serial', async () => {
});

test('should set runInBand to not run in serial', async () => {
const scheduler = new TestScheduler({}, {});
const scheduler = await createTestScheduler({}, {});
const test = {
context: {
config: makeProjectConfig({
Expand Down
2 changes: 1 addition & 1 deletion packages/jest-core/src/jest.ts
Expand Up @@ -6,7 +6,7 @@
*/

export {default as SearchSource} from './SearchSource';
export {default as TestScheduler} from './TestScheduler';
export {createTestScheduler} from './TestScheduler';
export {default as TestWatcher} from './TestWatcher';
export {runCLI} from './cli';
export {default as getVersion} from './version';
8 changes: 5 additions & 3 deletions packages/jest-core/src/runJest.ts
Expand Up @@ -24,7 +24,7 @@ import {requireOrImportModule, tryRealpath} from 'jest-util';
import {JestHook, JestHookEmitter} from 'jest-watcher';
import type FailedTestsCache from './FailedTestsCache';
import SearchSource from './SearchSource';
import TestScheduler, {TestSchedulerContext} from './TestScheduler';
import {TestSchedulerContext, createTestScheduler} from './TestScheduler';
import type TestWatcher from './TestWatcher';
import collectNodeHandles, {HandleCollectionResult} from './collectHandles';
import getNoTestsFoundMessage from './getNoTestsFoundMessage';
Expand Down Expand Up @@ -268,11 +268,13 @@ export default async function runJest({
}
}

const results = await new TestScheduler(
const scheduler = await createTestScheduler(
globalConfig,
{startRun},
testSchedulerContext,
).scheduleTests(allTests, testWatcher);
);

const results = await scheduler.scheduleTests(allTests, testWatcher);

await sequencer.cacheResults(allTests, results);

Expand Down
2 changes: 1 addition & 1 deletion packages/jest/src/jest.ts
Expand Up @@ -7,8 +7,8 @@

export {
SearchSource,
TestScheduler,
TestWatcher,
createTestScheduler,
getVersion,
runCLI,
} from '@jest/core';
Expand Down

0 comments on commit 9633a26

Please sign in to comment.