Skip to content

Commit

Permalink
fix(Jest): Notify users of lacking Jest support (#2322)
Browse files Browse the repository at this point in the history
  • Loading branch information
simondel committed Jul 17, 2020
1 parent 7b53ca9 commit 0bbc0c1
Show file tree
Hide file tree
Showing 4 changed files with 334 additions and 331 deletions.
2 changes: 1 addition & 1 deletion packages/jest-runner/package.json
Expand Up @@ -4,7 +4,7 @@
"description": "A plugin to use the jest test runner and framework in Stryker, the JavaScript mutation testing framework",
"main": "src/index.js",
"scripts": {
"test": "nyc --exclude-after-remap=false --check-coverage --reporter=html --report-dir=reports/coverage --lines 80 --functions 80 --branches 75 npm run mocha",
"test": "nyc --exclude-after-remap=false --check-coverage --reporter=html --report-dir=reports/coverage --lines 80 --functions 80 --branches 70 npm run mocha",
"mocha": "mocha \"test/helpers/**/*.js\" \"test/unit/**/*.js\" && mocha --timeout 30000 \"test/helpers/**/*.js\" \"test/integration/**/*.js\"",
"stryker": "node ../core/bin/stryker run"
},
Expand Down
73 changes: 38 additions & 35 deletions packages/jest-runner/src/JestTestRunner.ts
Expand Up @@ -5,11 +5,9 @@ import { RunOptions, RunResult, RunStatus, TestResult, TestRunner, TestStatus }

import { jestTestAdapterFactory } from './jestTestAdapters';
import JestTestAdapter from './jestTestAdapters/JestTestAdapter';
import { JestRunnerOptionsWithStrykerOptions } from './JestRunnerOptionsWithStrykerOptions';
import JestConfigLoader from './configLoaders/JestConfigLoader';
import { configLoaderToken, processEnvToken, jestTestAdapterToken, jestVersionToken } from './pluginTokens';
import { configLoaderFactory } from './configLoaders';
import JEST_OVERRIDE_OPTIONS from './jestOverrideOptions';

export function jestTestRunnerFactory(injector: Injector<OptionsContext>) {
return injector
Expand All @@ -34,30 +32,35 @@ export default class JestTestRunner implements TestRunner {
private readonly jestTestAdapter: JestTestAdapter,
configLoader: JestConfigLoader
) {
const jestOptions = options as JestRunnerOptionsWithStrykerOptions;
// Get jest configuration from stryker options and assign it to jestConfig
const configFromFile = configLoader.loadConfig();
this.jestConfig = this.mergeConfigSettings(configFromFile, (jestOptions.jest.config as any) || {});

// Get enableFindRelatedTests from stryker jest options or default to true
this.enableFindRelatedTests = jestOptions.jest.enableFindRelatedTests;
if (this.enableFindRelatedTests === undefined) {
this.enableFindRelatedTests = true;
}

if (this.enableFindRelatedTests) {
this.log.debug('Running jest with --findRelatedTests flag. Set jest.enableFindRelatedTests to false to run all tests on every mutant.');
} else {
this.log.debug(
'Running jest without --findRelatedTests flag. Set jest.enableFindRelatedTests to true to run only relevant tests on every mutant.'
);
}

// basePath will be used in future releases of Stryker as a way to define the project root
// Default to process.cwd when basePath is not set for now, should be removed when issue is solved
// https://github.com/stryker-mutator/stryker/issues/650
this.jestConfig.rootDir = (options.basePath as string) || process.cwd();
this.log.debug(`Project root is ${this.jestConfig.rootDir}`);
const errorMessage =
'This version of Stryker does not (yet) support Jest, sorry! Follow https://github.com/stryker-mutator/stryker/issues/2321 for the latest status.';
this.log.error(errorMessage);
throw new Error(errorMessage);

// const jestOptions = options as JestRunnerOptionsWithStrykerOptions;
// // Get jest configuration from stryker options and assign it to jestConfig
// const configFromFile = configLoader.loadConfig();
// this.jestConfig = this.mergeConfigSettings(configFromFile, (jestOptions.jest.config as any) || {});

// // Get enableFindRelatedTests from stryker jest options or default to true
// this.enableFindRelatedTests = jestOptions.jest.enableFindRelatedTests;
// if (this.enableFindRelatedTests === undefined) {
// this.enableFindRelatedTests = true;
// }

// if (this.enableFindRelatedTests) {
// this.log.debug('Running jest with --findRelatedTests flag. Set jest.enableFindRelatedTests to false to run all tests on every mutant.');
// } else {
// this.log.debug(
// 'Running jest without --findRelatedTests flag. Set jest.enableFindRelatedTests to true to run only relevant tests on every mutant.'
// );
// }

// // basePath will be used in future releases of Stryker as a way to define the project root
// // Default to process.cwd when basePath is not set for now, should be removed when issue is solved
// // https://github.com/stryker-mutator/stryker/issues/650
// this.jestConfig.rootDir = (options.basePath as string) || process.cwd();
// this.log.debug(`Project root is ${this.jestConfig.rootDir}`);
}

public async run(options: RunOptions): Promise<RunResult> {
Expand Down Expand Up @@ -116,13 +119,13 @@ export default class JestTestRunner implements TestRunner {
}
}

private mergeConfigSettings(configFromFile: Jest.Configuration, config: Jest.Configuration) {
const stringify = (obj: Record<string, any>) => JSON.stringify(obj, null, 2);
this.log.trace(
`Merging file-based config ${stringify(configFromFile)}
with custom config ${stringify(config)}
and default (internal) stryker config ${JEST_OVERRIDE_OPTIONS}`
);
return Object.assign(configFromFile, config, JEST_OVERRIDE_OPTIONS);
}
// private mergeConfigSettings(configFromFile: Jest.Configuration, config: Jest.Configuration) {
// const stringify = (obj: Record<string, any>) => JSON.stringify(obj, null, 2);
// this.log.trace(
// `Merging file-based config ${stringify(configFromFile)}
// with custom config ${stringify(config)}
// and default (internal) stryker config ${JEST_OVERRIDE_OPTIONS}`
// );
// return Object.assign(configFromFile, config, JEST_OVERRIDE_OPTIONS);
// }
}
222 changes: 111 additions & 111 deletions packages/jest-runner/test/integration/JestTestRunner.it.spec.ts
@@ -1,111 +1,111 @@
import * as path from 'path';

import { expect } from 'chai';
import { RunOptions, RunStatus, TestStatus } from '@stryker-mutator/api/test_runner';
import * as sinon from 'sinon';
import { commonTokens } from '@stryker-mutator/api/plugin';
import { factory, testInjector } from '@stryker-mutator/test-helpers';

import JestTestRunner, { jestTestRunnerFactory } from '../../src/JestTestRunner';
import { JestRunnerOptionsWithStrykerOptions } from '../../src/JestRunnerOptionsWithStrykerOptions';
import { JestOptions } from '../../src-generated/jest-runner-options';
import { createJestOptions } from '../helpers/producers';

const paths = require('react-scripts-ts/config/paths');
// It's a bit hacky, but we need to tell create-react-app-ts to pick a different tsconfig.test.json
paths.appTsTestConfig = require.resolve('../../testResources/reactTsProject/tsconfig.test.json');

// Get the actual project root, since we will stub process.cwd later on
const jestProjectRoot = process.cwd();

// Needed for Jest in order to run tests
process.env.BABEL_ENV = 'test';

describe(`${JestTestRunner.name} integration test`, () => {
// Set timeout for integration tests to 10 seconds for travis
let processCwdStub: sinon.SinonStub;

const runOptions: RunOptions = { timeout: 0 };

// Names of the tests in the example projects
const testNames = [
'Add should be able to add two numbers',
'Add should be able to add one to a number',
'Add should be able negate a number',
'Add should be able to recognize a negative number',
'Add should be able to recognize that 0 is not a negative number',
'Circle should have a circumference of 2PI when the radius is 1',
];

beforeEach(() => {
processCwdStub = sinon.stub(process, 'cwd');
});

function createSut(overrides?: Partial<JestOptions>) {
const options: JestRunnerOptionsWithStrykerOptions = factory.strykerWithPluginOptions({
jest: createJestOptions(overrides),
});
return testInjector.injector.provideValue(commonTokens.options, options).injectFunction(jestTestRunnerFactory);
}

it('should run tests on the example React + TypeScript project', async () => {
processCwdStub.returns(getProjectRoot('reactTsProject'));
const jestTestRunner = createSut({ projectType: 'react-ts' });

const result = await jestTestRunner.run(runOptions);

expect(result.status).to.equal(RunStatus.Complete);
expect(result).to.have.property('tests');
expect(result.tests).to.be.an('array').that.is.not.empty;
expect(result.tests[0].name).to.equal('renders without crashing');
expect(result.tests[0].status).to.equal(TestStatus.Success);
expect(result.tests[0].timeSpentMs).to.be.above(-1);
expect(result.tests[0].failureMessages).to.be.an('array').that.is.empty;
expect(result.status).to.equal(RunStatus.Complete);
});

it('should run tests on the example custom project using package.json', async () => {
processCwdStub.returns(getProjectRoot('exampleProject'));
const jestTestRunner = createSut();

const result = await jestTestRunner.run(runOptions);

expect(result.errorMessages, `Errors were: ${result.errorMessages}`).lengthOf(0);
expect(result).to.have.property('tests');
expect(result.tests).to.be.an('array').with.length(testNames.length);

for (const test of result.tests) {
expect(testNames).to.include(test.name);
expect(test.status).to.equal(TestStatus.Success);
expect(test.timeSpentMs).to.be.above(-1);
expect(test.failureMessages).to.be.an('array').that.is.empty;
}

expect(result.status).to.equal(RunStatus.Complete);
});

it('should run tests on the example custom project using jest.config.js', async () => {
processCwdStub.returns(getProjectRoot('exampleProjectWithExplicitJestConfig'));

const jestTestRunner = createSut();

const result = await jestTestRunner.run(runOptions);

expect(result.errorMessages, `Errors were: ${result.errorMessages}`).lengthOf(0);
expect(result).to.have.property('tests');
expect(result.tests).to.be.an('array').with.length(testNames.length);

for (const test of result.tests) {
expect(testNames).to.include(test.name);
expect(test.status).to.equal(TestStatus.Success);
expect(test.timeSpentMs).to.be.above(-1);
expect(test.failureMessages).to.be.an('array').that.is.empty;
}

expect(result.status).to.equal(RunStatus.Complete);
});
});

function getProjectRoot(testResource: string) {
return path.join(jestProjectRoot, 'testResources', testResource);
}
// import * as path from 'path';

// import { expect } from 'chai';
// import { RunOptions, RunStatus, TestStatus } from '@stryker-mutator/api/test_runner';
// import * as sinon from 'sinon';
// import { commonTokens } from '@stryker-mutator/api/plugin';
// import { factory, testInjector } from '@stryker-mutator/test-helpers';

// import JestTestRunner, { jestTestRunnerFactory } from '../../src/JestTestRunner';
// import { JestRunnerOptionsWithStrykerOptions } from '../../src/JestRunnerOptionsWithStrykerOptions';
// import { JestOptions } from '../../src-generated/jest-runner-options';
// import { createJestOptions } from '../helpers/producers';

// const paths = require('react-scripts-ts/config/paths');
// // It's a bit hacky, but we need to tell create-react-app-ts to pick a different tsconfig.test.json
// paths.appTsTestConfig = require.resolve('../../testResources/reactTsProject/tsconfig.test.json');

// // Get the actual project root, since we will stub process.cwd later on
// const jestProjectRoot = process.cwd();

// // Needed for Jest in order to run tests
// process.env.BABEL_ENV = 'test';

// describe(`${JestTestRunner.name} integration test`, () => {
// // Set timeout for integration tests to 10 seconds for travis
// let processCwdStub: sinon.SinonStub;

// const runOptions: RunOptions = { timeout: 0 };

// // Names of the tests in the example projects
// const testNames = [
// 'Add should be able to add two numbers',
// 'Add should be able to add one to a number',
// 'Add should be able negate a number',
// 'Add should be able to recognize a negative number',
// 'Add should be able to recognize that 0 is not a negative number',
// 'Circle should have a circumference of 2PI when the radius is 1',
// ];

// beforeEach(() => {
// processCwdStub = sinon.stub(process, 'cwd');
// });

// function createSut(overrides?: Partial<JestOptions>) {
// const options: JestRunnerOptionsWithStrykerOptions = factory.strykerWithPluginOptions({
// jest: createJestOptions(overrides),
// });
// return testInjector.injector.provideValue(commonTokens.options, options).injectFunction(jestTestRunnerFactory);
// }

// it('should run tests on the example React + TypeScript project', async () => {
// processCwdStub.returns(getProjectRoot('reactTsProject'));
// const jestTestRunner = createSut({ projectType: 'react-ts' });

// const result = await jestTestRunner.run(runOptions);

// expect(result.status).to.equal(RunStatus.Complete);
// expect(result).to.have.property('tests');
// expect(result.tests).to.be.an('array').that.is.not.empty;
// expect(result.tests[0].name).to.equal('renders without crashing');
// expect(result.tests[0].status).to.equal(TestStatus.Success);
// expect(result.tests[0].timeSpentMs).to.be.above(-1);
// expect(result.tests[0].failureMessages).to.be.an('array').that.is.empty;
// expect(result.status).to.equal(RunStatus.Complete);
// });

// it('should run tests on the example custom project using package.json', async () => {
// processCwdStub.returns(getProjectRoot('exampleProject'));
// const jestTestRunner = createSut();

// const result = await jestTestRunner.run(runOptions);

// expect(result.errorMessages, `Errors were: ${result.errorMessages}`).lengthOf(0);
// expect(result).to.have.property('tests');
// expect(result.tests).to.be.an('array').with.length(testNames.length);

// for (const test of result.tests) {
// expect(testNames).to.include(test.name);
// expect(test.status).to.equal(TestStatus.Success);
// expect(test.timeSpentMs).to.be.above(-1);
// expect(test.failureMessages).to.be.an('array').that.is.empty;
// }

// expect(result.status).to.equal(RunStatus.Complete);
// });

// it('should run tests on the example custom project using jest.config.js', async () => {
// processCwdStub.returns(getProjectRoot('exampleProjectWithExplicitJestConfig'));

// const jestTestRunner = createSut();

// const result = await jestTestRunner.run(runOptions);

// expect(result.errorMessages, `Errors were: ${result.errorMessages}`).lengthOf(0);
// expect(result).to.have.property('tests');
// expect(result.tests).to.be.an('array').with.length(testNames.length);

// for (const test of result.tests) {
// expect(testNames).to.include(test.name);
// expect(test.status).to.equal(TestStatus.Success);
// expect(test.timeSpentMs).to.be.above(-1);
// expect(test.failureMessages).to.be.an('array').that.is.empty;
// }

// expect(result.status).to.equal(RunStatus.Complete);
// });
// });

// function getProjectRoot(testResource: string) {
// return path.join(jestProjectRoot, 'testResources', testResource);
// }

0 comments on commit 0bbc0c1

Please sign in to comment.