Skip to content

Commit

Permalink
fix(jest-runner): pass config by path if configPath provided
Browse files Browse the repository at this point in the history
This mimics behaviour of jest - if you want to use projects,
you need to pass the config by file. This teaches stryker to do
the same - if you set explicit config path in jest config,
that gets passed into jest and voila, multi project jest works :)

https://github.com/swist/stryker-test is the minimum reproducible
example repo for this
  • Loading branch information
swist committed Mar 5, 2021
1 parent 424aca4 commit fcdae68
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 10 deletions.
@@ -1,11 +1,14 @@
import { join } from 'path';

import { jestWrapper } from '../utils';
import { JestRunResult } from '../jest-run-result';

import { JestTestAdapter, RunSettings } from './jest-test-adapter';

export class JestGreaterThan25TestAdapter implements JestTestAdapter {
public async run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern }: RunSettings): Promise<JestRunResult> {
const config = JSON.stringify(jestConfig);
public async run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern, jestConfigPath }: RunSettings): Promise<JestRunResult> {
const config = jestConfigPath ? join(projectRoot, jestConfigPath) : JSON.stringify(jestConfig);

const result = await jestWrapper.runCLI(
{
$0: 'stryker',
Expand Down
@@ -1,3 +1,4 @@
import { join } from 'path';
import jest from 'jest';

import { JestRunResult } from '../jest-run-result';
Expand All @@ -9,8 +10,8 @@ import { RunSettings, JestTestAdapter } from './jest-test-adapter';
* It has a lot of `any` typings here, since the installed typings are not in sync.
*/
export class JestLessThan25TestAdapter implements JestTestAdapter {
public run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern }: RunSettings): Promise<JestRunResult> {
const config = JSON.stringify(jestConfig);
public run({ jestConfig, projectRoot, fileNameUnderTest, testNamePattern, jestConfigPath }: RunSettings): Promise<JestRunResult> {
const config = jestConfigPath ? join(projectRoot, jestConfigPath) : JSON.stringify(jestConfig);
return jest.runCLI(
{
...(fileNameUnderTest && { _: [fileNameUnderTest], findRelatedTests: true }),
Expand Down
Expand Up @@ -5,6 +5,7 @@ import { JestRunResult } from '../jest-run-result';
export interface RunSettings {
jestConfig: Config.InitialOptions;
projectRoot: string;
jestConfigPath?: string;
testNamePattern?: string;
fileNameUnderTest?: string;
}
Expand Down
20 changes: 14 additions & 6 deletions packages/jest-runner/src/jest-test-runner.ts
Expand Up @@ -60,6 +60,7 @@ export const jestTestRunnerFactory = createJestTestRunnerFactory();
export class JestTestRunner implements TestRunner {
private readonly jestConfig: jest.Config.InitialOptions;
private readonly enableFindRelatedTests: boolean;
private readonly jestRunnerOptions: JestRunnerOptionsWithStrykerOptions;

public static inject = tokens(
commonTokens.logger,
Expand All @@ -78,13 +79,13 @@ export class JestTestRunner implements TestRunner {
configLoader: JestConfigLoader,
private readonly globalNamespace: typeof INSTRUMENTER_CONSTANTS.NAMESPACE | '__stryker2__'
) {
const jestOptions = options as JestRunnerOptionsWithStrykerOptions;
this.jestRunnerOptions = 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 || {});
this.jestConfig = this.mergeConfigSettings(configFromFile, this.jestRunnerOptions.jest || {});

// Get enableFindRelatedTests from stryker jest options or default to true
this.enableFindRelatedTests = jestOptions.jest.enableFindRelatedTests;
this.enableFindRelatedTests = this.jestRunnerOptions.jest.enableFindRelatedTests;

if (this.enableFindRelatedTests) {
this.log.debug('Running jest with --findRelatedTests flag. Set jest.enableFindRelatedTests to false to run all tests on every mutant.');
Expand All @@ -109,6 +110,7 @@ export class JestTestRunner implements TestRunner {
const { dryRunResult, jestResult } = await this.run({
jestConfig: withCoverageAnalysis(this.jestConfig, coverageAnalysis),
projectRoot: process.cwd(),
jestConfigPath: this.jestRunnerOptions.jest?.configFile,
});
if (dryRunResult.status === DryRunStatus.Complete && coverageAnalysis !== 'off') {
const errorMessage = verifyAllTestFilesHaveCoverage(jestResult, fileNamesWithMutantCoverage);
Expand All @@ -132,11 +134,17 @@ export class JestTestRunner implements TestRunner {
state.coverageAnalysis = 'off';
let testNamePattern: string | undefined;
if (testFilter) {
testNamePattern = testFilter.map((testId) => `(${escapeRegExp(testId)})`).join('|');
testNamePattern = testFilter.map(testId => `(${escapeRegExp(testId)})`).join('|');
}
process.env[INSTRUMENTER_CONSTANTS.ACTIVE_MUTANT_ENV_VARIABLE] = activeMutant.id.toString();
try {
const { dryRunResult } = await this.run({ fileNameUnderTest, jestConfig: this.jestConfig, projectRoot: process.cwd(), testNamePattern });
const { dryRunResult } = await this.run({
fileNameUnderTest,
jestConfig: this.jestConfig,
projectRoot: process.cwd(),
testNamePattern,
jestConfigPath: this.jestRunnerOptions.jest?.configFile,
});
return toMutantRunResult(dryRunResult);
} finally {
delete process.env[INSTRUMENTER_CONSTANTS.ACTIVE_MUTANT_ENV_VARIABLE];
Expand All @@ -155,7 +163,7 @@ export class JestTestRunner implements TestRunner {
private collectRunResult(results: jestTestResult.AggregatedResult): DryRunResult {
if (results.numRuntimeErrorTestSuites) {
const errorMessage = results.testResults
.map((testSuite) => this.collectSerializableErrorText(testSuite.testExecError))
.map(testSuite => this.collectSerializableErrorText(testSuite.testExecError))
.filter(notEmpty)
.join(', ');
return {
Expand Down
Expand Up @@ -13,6 +13,7 @@ describe(JestGreaterThan25TestAdapter.name, () => {

const projectRoot = '/path/to/project';
const fileNameUnderTest = '/path/to/file';
const jestConfigPath = 'jest.config.js';
const jestConfig: Config.InitialOptions = { rootDir: projectRoot };

beforeEach(() => {
Expand All @@ -30,6 +31,42 @@ describe(JestGreaterThan25TestAdapter.name, () => {
expect(runCLIStub).calledWith(sinon.match.object, [projectRoot]);
});

describe('when jestConfigPath not provided', () => {
it('should call the runCLI method with the stringified jest config flag', async () => {
await sut.run({ jestConfig, projectRoot, fileNameUnderTest });

expect(runCLIStub).calledWith(
{
$0: 'stryker',
_: [fileNameUnderTest],
config: JSON.stringify({ rootDir: projectRoot }),
findRelatedTests: true,
runInBand: true,
silent: true,
testNamePattern: undefined,
},
[projectRoot]
);
});
});
describe('when jestConfigPath provided', () => {
it('should pass the config path instead jest config flag', async () => {
await sut.run({ jestConfig, projectRoot, fileNameUnderTest, jestConfigPath });

expect(runCLIStub).calledWith(
{
$0: 'stryker',
_: [fileNameUnderTest],
config: `${projectRoot}/${jestConfigPath}`,
findRelatedTests: true,
runInBand: true,
silent: true,
testNamePattern: undefined,
},
[projectRoot]
);
});
});
it('should call the runCLI method with the --findRelatedTests flag', async () => {
await sut.run({ jestConfig, projectRoot, fileNameUnderTest });

Expand Down

0 comments on commit fcdae68

Please sign in to comment.