Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(jest): report test files and test positions #2808

Merged
merged 30 commits into from
Mar 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ff1b0bd
feat(test-runner api): add call site information
nicojs Feb 19, 2021
19c7fb8
feat(jest-runner): Add call site information to `DryRunResult`
nicojs Feb 19, 2021
0d84d61
fix(logging): log info about symlinking on debug (#2756)
nicojs Feb 19, 2021
660eca3
fix(reporting): report test name when a hook fails (#2757)
nicojs Feb 22, 2021
18b9119
WIP: new report model
nicojs Feb 24, 2021
2ad448f
WIP: new reporter model
nicojs Feb 24, 2021
e4794a4
Remove `ignoreReason` in favor of optional `status`
nicojs Mar 1, 2021
016e691
Fix all unit tests
nicojs Mar 4, 2021
1c239ca
Merge branch 'epic/v5' into feature/report-test-details
nicojs Mar 4, 2021
c36fc63
Re-enable unity tests for `MutationTestReportHelper`
nicojs Mar 4, 2021
4d72423
Implement mutant id as string in instrumenter and test runners
nicojs Mar 4, 2021
4b06ab6
Support mutant id as string in the karma runner
nicojs Mar 6, 2021
e1c117c
Add `metrics` as second parameter for `onMutationTestReportReady`
nicojs Mar 6, 2021
da693cf
update package-lock
nicojs Mar 6, 2021
7cfee49
fix no-coverage detection
nicojs Mar 6, 2021
744b5e9
test(e2e): fix coverage analysis e2e unit test
nicojs Mar 6, 2021
6a5e64c
Merge branch 'epic/v5' into feature/report-test-details
nicojs Mar 11, 2021
1845c93
report all properties
nicojs Mar 11, 2021
ba21498
Merge branch 'epic/v5' into feature/report-test-details
nicojs Mar 11, 2021
b1f6065
Report tests per test file if reported
nicojs Mar 11, 2021
50e204e
feat(report): add `framework`, `config` and `projectRoot` to report
nicojs Mar 16, 2021
d9e4db5
Merge branch 'epic/v5' into feat/jest-report-files-and-position
nicojs Mar 16, 2021
8b367e5
Remove `.only`
nicojs Mar 16, 2021
d4eaed6
Merge branch 'epic/v5' into feat/jest-report-files-and-position
nicojs Mar 20, 2021
4806bc6
report test locations in mutationTestReportReady if provided
nicojs Mar 20, 2021
769a06e
Use correct line number offsets
nicojs Mar 20, 2021
33783d9
update jest in e2e tests
nicojs Mar 26, 2021
4aaca09
Add remapping of lines when type checking is disabled
nicojs Mar 26, 2021
a7c265e
Fix tests
nicojs Mar 26, 2021
4ae0590
Removed plugin location
nicojs Mar 26, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9,295 changes: 5,511 additions & 3,784 deletions e2e/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion e2e/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"grunt": "~1.0.4",
"jasmine": "~3.6.2",
"jasmine-core": "~3.6.0",
"jest": "~26.4.2",
"jest": "~26.6.1",
"jest-environment-jsdom-sixteen": "^1.0.3",
"karma": "~6.0.1",
"karma-chai": "~0.1.0",
Expand Down
3 changes: 2 additions & 1 deletion e2e/test/jest-node/stryker.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"clear-text",
"progress",
"html",
"event-recorder"
"event-recorder",
"json"
]
}
24 changes: 23 additions & 1 deletion e2e/test/jest-node/verify/verify.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@

import { expect } from 'chai';
import path from 'path';
import { promises as fsPromises } from 'fs';
import { MutationTestResult, TestFileDefinitionDictionary } from 'mutation-testing-report-schema';
import { expectMetricsResult, produceMetrics } from '../../../helpers';

describe('After running stryker with test runner jest on test environment "node"', () => {
Expand All @@ -18,4 +21,23 @@ describe('After running stryker with test runner jest on test environment "node"
})
});
});

it('should report test files and test locations', async () => {
const report: MutationTestResult = require('../reports/mutation/mutation.json');
const expectedTestFiles: TestFileDefinitionDictionary = {
[path.resolve(__dirname, '..', 'src', 'sum.test.js')]: {
source: await fsPromises.readFile(path.resolve(__dirname, '..', 'src', 'sum.test.js'), 'utf-8'),
tests: [{
id: '0',
name: 'adds 1 + 2 to equal 3',
location: { start: { line: 3, column: 2 } } // columns should be 1, but for some reason aren't. Best guess if jest? 🤷‍♂️
}, {
id: '1',
name: 'sub 1 - 0 to equal 1',
location: { start: { line: 6, column: 2 } }
}]
}
};
expect(report.testFiles).deep.eq(expectedTestFiles);
});
});
22 changes: 22 additions & 0 deletions packages/core/src/config/file-matcher.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import path from 'path';

import minimatch from 'minimatch';

/**
* A helper class for matching files using the `disableTypeChecks` setting.
*/
export class FileMatcher {
private readonly pattern: string | false;

constructor(pattern: string | false) {
if (pattern !== false) {
this.pattern = path.resolve(pattern);
} else {
this.pattern = pattern;
}
}

public matches(fileName: string): boolean {
return !!this.pattern && minimatch(path.resolve(fileName), this.pattern);
}
}
1 change: 1 addition & 0 deletions packages/core/src/config/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './read-config';
export * from './options-validator';
export * from './build-schema-with-plugin-contributions';
export * from './file-matcher';
9 changes: 9 additions & 0 deletions packages/core/src/process/3-dry-run-executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { ConfigError } from '../errors';
import { findMutantTestCoverage } from '../mutants';
import { Pool, createTestRunnerPool } from '../concurrent/pool';
import { ConcurrencyTokenProvider } from '../concurrent';
import { FileMatcher } from '../config';

import { MutationTestContext } from './4-mutation-test-executor';
import { MutantInstrumenterContext } from './2-mutant-instrumenter-executor';
Expand Down Expand Up @@ -141,9 +142,17 @@ export class DryRunExecutor {
* @param dryRunResult the completed result
*/
private remapSandboxFilesToOriginalFiles(dryRunResult: CompleteDryRunResult) {
const disableTypeCheckingFileMatcher = new FileMatcher(this.options.disableTypeChecks);
dryRunResult.tests.forEach((test) => {
if (test.fileName) {
test.fileName = this.sandbox.originalFileFor(test.fileName);

// HACK line numbers of the tests can be offset by 1 because the disable type checks preprocessor could have added a `// @ts-nocheck` line.
// We correct for that here if needed
// If we do more complex stuff in sandbox preprocessing in the future, we might want to add a robust remapping logic
if (test.startPosition && disableTypeCheckingFileMatcher.matches(test.fileName)) {
test.startPosition.line--;
}
}
});
}
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/reporters/mutation-test-report-helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ export class MutationTestReportHelper {
return {
id: remapTestId(test.id),
name: test.name,
location: test.startPosition ? { start: this.toPosition(test.startPosition) } : undefined,
};
}

Expand Down
60 changes: 27 additions & 33 deletions packages/core/src/sandbox/disable-type-checks-preprocessor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import path from 'path';

import minimatch from 'minimatch';
import { commonTokens, tokens } from '@stryker-mutator/api/plugin';
import { File, StrykerOptions } from '@stryker-mutator/api/core';
import type { disableTypeChecks } from '@stryker-mutator/instrumenter';
Expand All @@ -9,6 +8,7 @@ import { propertyPath, PropertyPathBuilder } from '@stryker-mutator/util';

import { coreTokens } from '../di';
import { isWarningEnabled } from '../utils/object-utils';
import { FileMatcher } from '../config/file-matcher';

import { FilePreprocessor } from './file-preprocessor';

Expand All @@ -21,41 +21,35 @@ export class DisableTypeChecksPreprocessor implements FilePreprocessor {
constructor(private readonly log: Logger, private readonly options: StrykerOptions, private readonly impl: typeof disableTypeChecks) {}

public async preprocess(files: File[]): Promise<File[]> {
if (this.options.disableTypeChecks === false) {
return files;
} else {
const pattern = path.resolve(this.options.disableTypeChecks);
let warningLogged = false;
const outFiles = await Promise.all(
files.map(async (file) => {
if (minimatch(path.resolve(file.name), pattern)) {
try {
return await this.impl(file, { plugins: this.options.mutator.plugins });
} catch (err) {
if (isWarningEnabled('preprocessorErrors', this.options.warnings)) {
warningLogged = true;
this.log.warn(
`Unable to disable type checking for file "${
file.name
}". Shouldn't type checking be disabled for this file? Consider configuring a more restrictive "${propertyPath<StrykerOptions>(
'disableTypeChecks'
)}" settings (or turn it completely off with \`false\`)`,
err
);
}
return file;
const matcher = new FileMatcher(this.options.disableTypeChecks);
let warningLogged = false;
const outFiles = await Promise.all(
files.map(async (file) => {
if (matcher.matches(path.resolve(file.name))) {
try {
return await this.impl(file, { plugins: this.options.mutator.plugins });
} catch (err) {
if (isWarningEnabled('preprocessorErrors', this.options.warnings)) {
warningLogged = true;
this.log.warn(
`Unable to disable type checking for file "${
file.name
}". Shouldn't type checking be disabled for this file? Consider configuring a more restrictive "${propertyPath<StrykerOptions>(
'disableTypeChecks'
)}" settings (or turn it completely off with \`false\`)`,
err
);
}
} else {
return file;
}
})
);
if (warningLogged) {
this.log.warn(
`(disable "${PropertyPathBuilder.create<StrykerOptions>().prop('warnings').prop('preprocessorErrors')}" to ignore this warning`
);
}
return outFiles;
} else {
return file;
}
})
);
if (warningLogged) {
this.log.warn(`(disable "${PropertyPathBuilder.create<StrykerOptions>().prop('warnings').prop('preprocessorErrors')}" to ignore this warning`);
}
return outFiles;
}
}
24 changes: 24 additions & 0 deletions packages/core/test/unit/config/file-matcher.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { expect } from 'chai';

import { FileMatcher } from '../../../src/config';

describe(FileMatcher.name, () => {
describe(FileMatcher.prototype.matches.name, () => {
it('should match when the glob pattern matches', () => {
const sut = new FileMatcher('src/**/*.ts');
expect(sut.matches('src/foo.ts')).true;
});

it('should not match if the pattern is set to `false`', () => {
const sut = new FileMatcher(false);
expect(sut.matches('src/foo.js')).false;
});

it("should not match if the glob pattern doesn't match", () => {
const sut = new FileMatcher('src/**/*.js');
expect(sut.matches('test/foo.spec.js')).false;
});

// more tests would test the internals of minimatch itself. We expect that to work.
});
});
18 changes: 18 additions & 0 deletions packages/core/test/unit/process/3-dry-run-executor.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,24 @@ describe(DryRunExecutor.name, () => {
expect(sandbox.originalFileFor).calledWith('.stryker-tmp/sandbox-123/test/foo.spec.js');
});

it('should remap test locations when type checking was disabled for a test file', async () => {
runResult.tests.push(
factory.successTestResult({ fileName: '.stryker-tmp/sandbox-123/test/foo.spec.js', startPosition: { line: 3, column: 1 } }),
factory.successTestResult({ fileName: '.stryker-tmp/sandbox-123/testResources/foo.spec.js', startPosition: { line: 5, column: 1 } })
);
sandbox.originalFileFor
.withArgs('.stryker-tmp/sandbox-123/test/foo.spec.js')
.returns('test/foo.spec.js')
.withArgs('.stryker-tmp/sandbox-123/testResources/foo.spec.js')
.returns('testResources/foo.spec.js');
await sut.execute();
const actualDryRunResult = injectorMock.provideValue.getCalls().find((call) => call.args[0] === coreTokens.dryRunResult)!
.args[1] as DryRunResult;
assertions.expectCompleted(actualDryRunResult);
expect(actualDryRunResult.tests[0].startPosition).deep.eq({ line: 2, column: 1 });
expect(actualDryRunResult.tests[1].startPosition).deep.eq({ line: 5, column: 1 }); // should not have been remapped, since type checking wasn't disabled here
});

it('should have logged the amount of tests ran', async () => {
runResult.tests.push(factory.successTestResult({ timeSpentMs: 10 }));
runResult.tests.push(factory.successTestResult({ timeSpentMs: 10 }));
Expand Down
Loading