diff --git a/packages/stryker-api/src/config/Config.ts b/packages/stryker-api/src/config/Config.ts index 9714b7e81f..3ae672a72a 100644 --- a/packages/stryker-api/src/config/Config.ts +++ b/packages/stryker-api/src/config/Config.ts @@ -5,7 +5,12 @@ export default class Config implements StrykerOptions { [customConfig: string]: any; public files: string[]; - public mutate: string[]; + public mutate: string[] = [ + '{src,lib}/**/*.js?(x)', + '!{src,lib}/**/__tests__/**/*.js?(x)', + '!{src,lib}/**/?(*.)+(spec|test).js?(x)', + '!{src,lib}/**/*+(Spec|Test).js?(x)' + ]; public logLevel: LogLevel = LogLevel.Information; public fileLogLevel: LogLevel = LogLevel.Off; @@ -15,7 +20,7 @@ export default class Config implements StrykerOptions { public port = 9234; public reporter = []; public reporters: string[] = ['progress', 'clear-text']; - public coverageAnalysis: 'perTest' | 'all' | 'off' = 'perTest'; + public coverageAnalysis: 'perTest' | 'all' | 'off' = 'off'; public testRunner: string = 'command'; public testFramework: string; public mutator: string | MutatorDescriptor = 'es5'; diff --git a/packages/stryker-api/test/unit/config/ConfigSpec.ts b/packages/stryker-api/test/unit/config/ConfigSpec.ts index f9472307ce..8c1a644aab 100644 --- a/packages/stryker-api/test/unit/config/ConfigSpec.ts +++ b/packages/stryker-api/test/unit/config/ConfigSpec.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import { Config } from '../../../config'; +import * as minimatch from 'minimatch'; describe('Config', () => { @@ -32,4 +33,53 @@ describe('Config', () => { expect(sut.thresholds).not.be.undefined; }); }); + + describe('default value for `mutate` property', () => { + + let defaultMutatePatterns: string[]; + + beforeEach(() => { + defaultMutatePatterns = new Config().mutate; + }); + + it('should not match test files', () => { + actAssertNoMatch('test/bar.js'); + actAssertNoMatch('src/__tests__/foo.js'); + actAssertNoMatch('src/foo/__tests__/foo.js'); + actAssertNoMatch('src/fooSpec.js'); + actAssertNoMatch('src/foo.spec.js'); + actAssertNoMatch('src/bar/fooSpec.js'); + actAssertNoMatch('src/bar/foo.spec.js'); + }); + + it('should match production files', () => { + actAssertMatches('src/index.js'); + actAssertMatches('src/foo/bar/index.js'); + actAssertMatches('src/foo/bar/supertest.js'); + }); + + function actAssertNoMatch(testFileName: string) { + expect(matches(testFileName), `${testFileName} would be mutated, while it shouldn't.`).be.false; + } + + function actAssertMatches(productionFileName: string) { + expect(matches(productionFileName), `${productionFileName} would be mutated, while it shouldn't.`).be.true; + } + + function matches(fileName: string) { + let isMatch = false; + for (const pattern of defaultMutatePatterns) { + if (pattern.startsWith('!')) { + if (minimatch(fileName, pattern.substr(1))) { + isMatch = false; + } + } else { + if (minimatch(fileName, pattern)) { + isMatch = true; + } + } + } + return isMatch; + } + }); }); diff --git a/packages/stryker-babel-transpiler/test/unit/BabelTranspilerSpec.ts b/packages/stryker-babel-transpiler/test/unit/BabelTranspilerSpec.ts index 8223fad137..402739e37d 100644 --- a/packages/stryker-babel-transpiler/test/unit/BabelTranspilerSpec.ts +++ b/packages/stryker-babel-transpiler/test/unit/BabelTranspilerSpec.ts @@ -46,7 +46,8 @@ describe('BabelTranspiler', () => { expect(babelConfigReaderMock.readConfig).calledWith(config); }); - it('should throw if `produceSourceMaps` was true', () => { + it('should throw if `produceSourceMaps` was true and coverage analysis is "perTest"', () => { + config.coverageAnalysis = 'perTest'; expect(() => new BabelTranspiler({ produceSourceMaps: true, config })).throws('Invalid `coverageAnalysis` "perTest" is not supported by the stryker-babel-transpiler. Not able to produce source maps yet. Please set it to "off".'); }); }); diff --git a/packages/stryker-webpack-transpiler/test/unit/WebpackTranspilerSpec.ts b/packages/stryker-webpack-transpiler/test/unit/WebpackTranspilerSpec.ts index 1faca7a107..ffe809ce14 100644 --- a/packages/stryker-webpack-transpiler/test/unit/WebpackTranspilerSpec.ts +++ b/packages/stryker-webpack-transpiler/test/unit/WebpackTranspilerSpec.ts @@ -47,6 +47,7 @@ describe('WebpackTranspiler', () => { }); it('should throw an error if `produceSourceMaps` is `true`', () => { + config.coverageAnalysis = 'perTest'; expect(() => new WebpackTranspiler({ config, produceSourceMaps: true })).throws('Invalid `coverageAnalysis` "perTest" is not supported by the stryker-webpack-transpiler (yet). It is not able to produce source maps yet. Please set it "coverageAnalysis" to "off"'); }); diff --git a/packages/stryker/README.md b/packages/stryker/README.md index e08e82015f..d13f7c68c0 100644 --- a/packages/stryker/README.md +++ b/packages/stryker/README.md @@ -14,35 +14,35 @@ For an introduction to mutation testing and Stryker's features, see [stryker-mutator.io](https://stryker-mutator.io/). ## Getting started -Stryker is a mutation testing framework for JavaScript. It allows you to test your tests by temporarily inserting bugs. -To install Stryker, execute the command: -```sh -$ npm install stryker stryker-api --save-dev -``` +Please follow the [quickstart on the website](https://stryker-mutator.io/stryker/quickstart). -***Note:*** *During installation you may run into errors caused by [node-gyp](https://github.com/nodejs/node-gyp). It is safe to ignore them.* +For small js projects, you can try the following command: -To test if Stryker is installed correctly, execute the command: -```sh -$ node_modules/.bin/stryker --version ``` +npm install --save-dev stryker stryker-api +# Only for small projects: +npx stryker run +``` + +It will run stryker with default values: -This should print the latest version of Stryker. +* Uses `npm test` as your test command +* Searches for files to mutate in the `lib` and `src` directories ## Usage ```sh -$ node_modules/.bin/stryker [options] [stryker.conf.js] +$ npx stryker [options] [stryker.conf.js] ``` The main `command` for Stryker is `run`, which kicks off mutation testing. -By default, we expect a `stryker.conf.js` file in the current working directory. This can be overridden by specifying a different file as the last parameter. +Although Stryker can run without any configuration, it is recommended to configure it when you can, as it can greatly improve performance of the mutation testing process. By default, Stryker will look for a `stryker.conf.js` file in the current working directory (if it exists). This can be overridden by specifying a different file as the last parameter. Before your first run, we recommend you try the `init` command, which helps you to set up this `stryker.conf.js` file and install any missing packages needed for your specific configuration. We recommend you verify the contents of the configuration file after this initialization, to make sure everything is setup correctly. Of course, you can still make changes to it, before you run Stryker for the first time. -The following is an example `stryker.conf.js` file: +The following is an example `stryker.conf.js` file. It specifies running mocha tests with the mocha test runner. ```javascript module.exports = function(config){ @@ -53,7 +53,7 @@ module.exports = function(config){ ], testFramework: 'mocha', testRunner: 'mocha', - reporters: ['progress', 'clear-text', 'dots', 'html', 'event-recorder'], + reporters: ['progress', 'clear-text', 'html'], coverageAnalysis: 'perTest' }); } @@ -89,7 +89,7 @@ You can *ignore* files by adding an exclamation mark (`!`) at the start of an ex ### `mutate` [`string[]`] -Default: *none* +Default: `['{src,lib}/**/*.js?(x)', '!{src,lib}/**/__tests__/**/*.js?(x)', '!{src,lib}/**/?(*.)+(spec|test).js?(x)', '!{src,lib}/**/*+(Spec|Test).js?(x)']` Command line: `[--mutate|-m] src/**/*.js,a.js` Config file: `mutate: ['src/**/*.js', 'a.js']` @@ -112,6 +112,7 @@ For example: install and use the `stryker-karma-runner` to use `karma` as a test See the [list of plugins](https://stryker-mutator.io/plugins.html) for an up-to-date list of supported test runners and plugins. ### `testFramework` [`string`] + Default: *none* Command line: `--testFramework jasmine` Config file: `testFramework: 'jasmine'` @@ -124,9 +125,9 @@ Make sure the a plugin is installed for your chosen test framework. E.g. install ### `coverageAnalysis` [`string`] -Default: `perTest` -Full notation: `--coverageAnalysis perTest` -Config file key: `coverageAnalysis: 'perTest'` +Default: `off` +Command line: `--coverageAnalysis perTest` +Config file: `coverageAnalysis: 'perTest'` With `coverageAnalysis` you specify which coverage analysis strategy you want to use. @@ -153,7 +154,7 @@ In addition to requiring your test runner to be able to report the code coverage ### `mutator` [`object` | `string`] Default: `es5` Command line: `--mutator es5` -Config file: `mutator: 'es5' | mutator: { name: 'es5', excludedMutations: ['BooleanSubstitution', 'StringLiteral'] }` +Config file: `mutator: 'es5'` or `mutator: { name: 'es5', excludedMutations: ['BooleanSubstitution', 'StringLiteral'] }` With `mutator` you configure which mutator plugin you want to use, and optionally, which mutation types to exclude from the test run. The mutator plugin name defaults to `es5` if not specified. The list of excluded mutation types defaults to an empty array, meaning all mutation types will be included in the test. @@ -190,7 +191,7 @@ The `clear-text` reporter supports three additional config options: * `logTests` to log the names of unit tests that were run to allow mutants. By default, only the first three are logged. The config for your config file is: `clearTextReporter: { logTests: true },` * `maxTestsToLog` to show more tests that were executed to kill a mutant when `logTests` is true. The config for your config file is: `clearTextReporter: { logTests: true, maxTestsToLog: 7 },` -The `dashboard` reporter is a special kind of reporter. It sends a report to https://dashboard.stryker-mutator.io, enabling you to add a fancy mutation score badge to your readme! To make sure no unwanted results are sent to the dashboards, it will only send the report if it is run from a build server. The reporter currently detects [Travis](https://travis-ci.org/) and [CircleCI](https://circleci.com/). Please open an [issue](https://github.com/stryker-mutator/stryker/issues/new) if your build server is missing. On all these environments, it will ignore builds of pull requests. Apart from buildserver-specific environment variables, the reporter uses one environment variable: +The `dashboard` reporter is a special kind of reporter. It sends a report to https://dashboard.stryker-mutator.io, enabling you to add a fancy mutation score badge to your readme! To make sure no unwanted results are sent to the dashboards, it will only send the report if it is run from a build server. The reporter currently detects [Travis](https://travis-ci.org/) and [CircleCI](https://circleci.com/). Please open an [issue](https://github.com/stryker-mutator/stryker/issues/new) if your build server is missing. On all these environments, it will ignore builds of pull requests. Apart from build server specific environment variables, the reporter uses one environment variable: | Environment variable | Description | Example value | | ------------- | ------------- | ----- | @@ -206,7 +207,6 @@ Default: result of `git ls-files --others --exclude-standard --cached --exclude Command line: `[--files|-f] src/**/*.js,a.js,test/**/*.js` Config file: `files: ['src/**/*.js', '!src/**/index.js', 'test/**/*.js']` - With `files` you can choose which files should be included in your test runner sandbox. This is normally not needed as it defaults to all files not ignored by git. Try it out yourself with this command: `git ls-files --others --exclude-standard --cached --exclude .stryker-tmp`. diff --git a/packages/stryker/src/input/InputFileCollection.ts b/packages/stryker/src/input/InputFileCollection.ts index f2329beb57..0656b9a3b0 100644 --- a/packages/stryker/src/input/InputFileCollection.ts +++ b/packages/stryker/src/input/InputFileCollection.ts @@ -1,6 +1,7 @@ import { File } from 'stryker-api/core'; import { Logger } from 'stryker-api/logging'; import { normalizeWhiteSpaces } from '../utils/objectUtils'; +import os = require('os'); export default class InputFileCollection { public readonly files: ReadonlyArray; @@ -13,14 +14,15 @@ export default class InputFileCollection { public logFiles(log: Logger) { if (!this.files.length) { - log.warn(normalizeWhiteSpaces(` - No files selected. Please make sure you either run stryker a git repository context (and don't specify \`files\` in your stryker.conf.js file), - or specify the \`files\` property in your stryker config.`)); + log.warn(`No files selected. Please make sure you either${os.EOL}` + + ` (1) Run Stryker inside a Git repository; or${os.EOL}` + + ` (2) Specify the \`files\` property in your Stryker configuration (\`--files via command line\`).`); } else { if (this.filesToMutate.length) { log.info(`Found ${this.filesToMutate.length} of ${this.files.length} file(s) to be mutated.`); } else { - log.warn(`No files marked to be mutated, stryker will perform a dry-run without actually mutating anything.`); + log.warn(normalizeWhiteSpaces(`No files marked to be mutated, Stryker will perform a dry-run without actually mutating anything. + You can configure the \`mutate\` property in your stryker.conf.js file (or use \`--mutate\` via command line).`)); } if (log.isDebugEnabled) { log.debug(`All input files: ${JSON.stringify(this.files.map(file => file.name), null, 2)}`); diff --git a/packages/stryker/src/input/InputFileResolver.ts b/packages/stryker/src/input/InputFileResolver.ts index 4adf57eee2..887b78d178 100644 --- a/packages/stryker/src/input/InputFileResolver.ts +++ b/packages/stryker/src/input/InputFileResolver.ts @@ -13,6 +13,7 @@ import { filterEmpty, isErrnoException } from '../utils/objectUtils'; +import { Config } from 'stryker-api/config'; function toReportSourceFile(file: File): SourceFile { return { @@ -21,26 +22,28 @@ function toReportSourceFile(file: File): SourceFile { }; } +const IGNORE_PATTERN_CHARACTER = '!'; + export default class InputFileResolver { private readonly log = getLogger(InputFileResolver.name); - private readonly fileResolver: PatternResolver | undefined; - private readonly mutateResolver: PatternResolver; + private readonly mutatePatterns: ReadonlyArray; + private readonly filePatterns: ReadonlyArray | undefined; constructor( mutate: string[], files: string[] | undefined, private readonly reporter: StrictReporter ) { - this.mutateResolver = PatternResolver.parse(mutate || []); + this.mutatePatterns = this.normalize(mutate || []); if (files) { - this.fileResolver = PatternResolver.parse(files); + this.filePatterns = this.normalize(files); } } public async resolve(): Promise { const [inputFileNames, mutateFiles] = await Promise.all([ this.resolveInputFiles(), - this.mutateResolver.resolve() + this.resolveMutateFiles() ]); const files: File[] = await this.readFiles(inputFileNames); const inputFileCollection = new InputFileCollection(files, mutateFiles); @@ -50,34 +53,79 @@ export default class InputFileResolver { } private resolveInputFiles() { - if (this.fileResolver) { - return this.fileResolver.resolve(); + if (this.filePatterns) { + return this.expand(this.filePatterns); } else { return this.resolveFilesUsingGit(); } } - private resolveFilesUsingGit(): Promise { - return childProcessAsPromised - .exec( + private resolveMutateFiles() { + return this.expand(this.mutatePatterns, !shallowEquals(this.mutatePatterns, new Config().mutate)); + + function shallowEquals(arr1: ReadonlyArray, arr2: ReadonlyArray): boolean { + if (arr1.length !== arr2.length) { + return false; + } else { + for (let i = 0; i < arr1.length; i++) { + if (arr1[i] !== arr2[i]) { + return false; + } + } + return true; + } + } + } + + /** + * Takes a list of globbing patterns and expands them into files. + * If a patterns starts with a `!`, it negates the pattern. + * @param patterns The patterns to expand into files + */ + private async expand(patterns: ReadonlyArray, logAboutUselessPatterns = true): Promise { + const fileSet = new Set(); + for (const pattern of patterns) { + if (pattern.startsWith(IGNORE_PATTERN_CHARACTER)) { + const files = await this.expandPattern(pattern.substr(1), logAboutUselessPatterns); + files.forEach(fileName => fileSet.delete(fileName)); + } else { + const files = await this.expandPattern(pattern, logAboutUselessPatterns); + files.forEach(fileName => fileSet.add(fileName)); + } + } + return Array.from(fileSet); + } + + private async expandPattern(globbingExpression: string, logAboutUselessPatterns: boolean): Promise { + const fileNames = (await glob(globbingExpression)).map(relativeFile => path.resolve(relativeFile)); + if (!fileNames.length && logAboutUselessPatterns) { + this.log.warn( + `Globbing expression "${globbingExpression}" did not result in any files.` + ); + } + return fileNames; + } + + private async resolveFilesUsingGit(): Promise { + try { + const { stdout } = await childProcessAsPromised.exec( 'git ls-files --others --exclude-standard --cached --exclude .stryker-tmp', { maxBuffer: 10 * 1000 * 1024 } - ) - .then(({ stdout }: { stdout: string }) => stdout.toString()) - .then((output: string) => - output.split('\n').map(fileName => fileName.trim()) - ) - .then((fileNames: string[]) => - fileNames - .filter(fileName => fileName) - .map(fileName => path.resolve(fileName)) - ) - .catch((error: Error) => { - throw new StrykerError( - `Cannot determine input files. Either specify a \`files\` array in your stryker configuration, or make sure "${process.cwd()}" is located inside a git repository`, - error - ); - }); + ); + const fileNames = stdout.toString() + .split('\n') + .map(line => line.trim()) + .filter(line => line) // remove empty lines + .map(relativeFileName => path.resolve(relativeFileName)); + return fileNames; + } catch (error) { + throw new StrykerError(normalizeWhiteSpaces( + `Cannot determine input files. Either specify a \`files\` + array in your stryker configuration, or make sure "${process.cwd()}" + is located inside a git repository`), + error + ); + } } private reportAllSourceFilesRead(allFiles: File[]) { @@ -94,30 +142,7 @@ export default class InputFileResolver { ); } - private readFile(fileName: string): Promise { - return fsAsPromised - .readFile(fileName) - .then((content: Buffer) => new File(fileName, content)) - .then((file: File) => { - this.reportSourceFilesRead(file); - return file; - }) - .catch(error => { - if ( - (isErrnoException(error) && error.code === 'ENOENT') || - error.code === 'EISDIR' - ) { - return null; // file is deleted or a directory. This can be a valid result of the git command - } else { - // Rethrow - throw error; - } - }); - } -} - -class PatternResolver { - private static normalize( + private normalize( inputFileExpressions: (string | { pattern: string })[] ): string[] { const inputFileDescriptorObjects: { pattern: string }[] = []; @@ -130,16 +155,12 @@ class PatternResolver { } }); if (inputFileDescriptorObjects.length) { - new PatternResolver('').log.warn( + this.log.warn( normalizeWhiteSpaces(` DEPRECATED: Using the \`InputFileDescriptor\` syntax to - select files is no longer supported. We'll assume: ${JSON.stringify( - inputFileDescriptorObjects - )} can be migrated - to ${JSON.stringify( - inputFileDescriptorObjects.map(_ => _.pattern) - )} for this mutation run. - Please move any files to mutate into the \`mutate\` array (top level stryker option). + select files is no longer supported. We'll assume: ${JSON.stringify(inputFileDescriptorObjects)} + can be migrated to ${JSON.stringify(inputFileDescriptorObjects.map(_ => _.pattern))} for this + mutation run. Please move any files to mutate into the \`mutate\` array (top level stryker option). You can fix this warning in 2 ways: 1) If your project is under git version control, you can remove the "files" patterns all together. Stryker can figure it out for you. @@ -149,85 +170,25 @@ class PatternResolver { } return globExpressions; } - private readonly log = getLogger(InputFileResolver.name); - private ignore = false; - private readonly globExpression: string; - constructor( - globExpression: string, - private readonly previous?: PatternResolver - ) { - this.ignore = globExpression.indexOf('!') === 0; - if (this.ignore) { - this.globExpression = globExpression.substring(1); - } else { - this.globExpression = globExpression; - } - } - - public async resolve(): Promise { - // When the first expression starts with an '!', we skip that one - if (this.ignore && !this.previous) { - return Promise.resolve([]); - } else { - // Start the globbing task for the current descriptor - const globbingTask = this.resolveGlobbingExpression(this.globExpression); - - // If there is a previous globbing expression, resolve that one as well - if (this.previous) { - const results = await Promise.all([ - this.previous.resolve(), - globbingTask - ]); - const previousFiles = results[0]; - const currentFiles = results[1]; - // If this expression started with a '!', exclude current files - if (this.ignore) { - return previousFiles.filter(previousFile => - currentFiles.every(currentFile => previousFile !== currentFile) - ); + private readFile(fileName: string): Promise { + return fsAsPromised + .readFile(fileName) + .then((content: Buffer) => new File(fileName, content)) + .then((file: File) => { + this.reportSourceFilesRead(file); + return file; + }) + .catch(error => { + if ( + (isErrnoException(error) && error.code === 'ENOENT') || + error.code === 'EISDIR' + ) { + return null; // file is deleted or a directory. This can be a valid result of the git command } else { - // Only add files which were not already added - return previousFiles.concat( - currentFiles.filter( - currentFile => !previousFiles.some(file => file === currentFile) - ) - ); + // Rethrow + throw error; } - } else { - return globbingTask; - } - } - } - - private static empty(): PatternResolver { - const emptyResolver = new PatternResolver(''); - emptyResolver.ignore = true; - return emptyResolver; - } - - public static parse(inputFileExpressions: string[]): PatternResolver { - const expressions = this.normalize(inputFileExpressions); - let current = PatternResolver.empty(); - let expression = expressions.shift(); - while (expression) { - current = new PatternResolver(expression, current); - expression = expressions.shift(); - } - return current; - } - - private async resolveGlobbingExpression(pattern: string): Promise { - const files = await glob(pattern); - if (files.length === 0) { - this.reportEmptyGlobbingExpression(pattern); - } - return files.map(f => path.resolve(f)); - } - - private reportEmptyGlobbingExpression(expression: string) { - this.log.warn( - `Globbing expression "${expression}" did not result in any files.` - ); + }); } } diff --git a/packages/stryker/test/unit/input/InputFileResolverSpec.ts b/packages/stryker/test/unit/input/InputFileResolverSpec.ts index 896ce14e49..90f8843773 100644 --- a/packages/stryker/test/unit/input/InputFileResolverSpec.ts +++ b/packages/stryker/test/unit/input/InputFileResolverSpec.ts @@ -1,9 +1,11 @@ +import os = require('os'); import * as path from 'path'; import { expect } from 'chai'; import { childProcessAsPromised } from '@stryker-mutator/util'; import { Logger } from 'stryker-api/logging'; import { File } from 'stryker-api/core'; import { SourceFile } from 'stryker-api/report'; +import { Config } from 'stryker-api/config'; import InputFileResolver from '../../../src/input/InputFileResolver'; import * as sinon from 'sinon'; import * as fileUtils from '../../../src/utils/fileUtils'; @@ -77,8 +79,8 @@ describe('InputFileResolver', () => { it('should log a warning if no files were resolved', async () => { sut = new InputFileResolver([], [], reporter); await sut.resolve(); - expect(log.warn).calledWith(sinon.match('No files selected. Please make sure you either run stryker a git repository context')); - expect(log.warn).calledWith(sinon.match('or specify the \`files\` property in your stryker config')); + expect(log.warn).calledWith(sinon.match(`No files selected. Please make sure you either${os.EOL} (1) Run Stryker inside a Git repository`) + .and(sinon.match('(2) Specify the \`files\` property in your Stryker configuration'))); }); it('should be able to handle deleted files reported by `git ls-files`', async () => { @@ -168,7 +170,7 @@ describe('InputFileResolver', () => { it('should warn about dry-run', async () => { await sut.resolve(); - expect(log.warn).to.have.been.calledWith('No files marked to be mutated, stryker will perform a dry-run without actually mutating anything.'); + expect(log.warn).calledWith(sinon.match('No files marked to be mutated, Stryker will perform a dry-run without actually mutating anything.')); }); }); @@ -222,14 +224,21 @@ describe('InputFileResolver', () => { }); describe('when a globbing expression does not result in a result', () => { - beforeEach(() => { - sut = new InputFileResolver(['file1'], ['file1', 'notExists'], reporter); - }); it('should log a warning', async () => { + sut = new InputFileResolver(['file1'], ['file1', 'notExists'], reporter); await sut.resolve(); expect(log.warn).to.have.been.calledWith('Globbing expression "notExists" did not result in any files.'); }); + + it('should not log a warning if the globbing expression was the default logging expression', async () => { + const config = new Config(); + sut = new InputFileResolver(config.mutate, config.files, reporter); + childProcessExecStub.resolves({ stdout: Buffer.from(`src/foobar.js`) }); + globStub.withArgs(config.mutate[0]).returns(['src/foobar.js']); + await sut.resolve(); + expect(log.warn).not.called; + }); }); it('should reject when a globbing expression results in a reject', () => { diff --git a/packages/stryker/test/unit/process/InitialTestExecutorSpec.ts b/packages/stryker/test/unit/process/InitialTestExecutorSpec.ts index 166f17f59d..753cf147c4 100644 --- a/packages/stryker/test/unit/process/InitialTestExecutorSpec.ts +++ b/packages/stryker/test/unit/process/InitialTestExecutorSpec.ts @@ -194,12 +194,14 @@ describe('InitialTestExecutor run', () => { }); it('should also add a collectCoveragePerTest file when coverage analysis is "perTest" and there is a testFramework', async () => { + options.coverageAnalysis = 'perTest'; sandbox.stub(coverageHooks, 'coveragePerTestHooks').returns('test hook foobar'); await sut.run(); expect(strykerSandboxMock.run).calledWith(EXPECTED_INITIAL_TIMEOUT, 'test hook foobar'); }); it('should result log a warning if coverage analysis is "perTest" and there is no testFramework', async () => { + options.coverageAnalysis = 'perTest'; sut = new InitialTestExecutor(options, inputFiles, /* test framework */ null, timer as any, LOGGING_CONTEXT); sandbox.stub(coverageHooks, 'coveragePerTestHooks').returns('test hook foobar'); await sut.run(); diff --git a/packages/stryker/test/unit/transpiler/SourceMapperSpec.ts b/packages/stryker/test/unit/transpiler/SourceMapperSpec.ts index f5b7e0a8c9..8f891f4126 100644 --- a/packages/stryker/test/unit/transpiler/SourceMapperSpec.ts +++ b/packages/stryker/test/unit/transpiler/SourceMapperSpec.ts @@ -44,6 +44,7 @@ describe('SourceMapper', () => { }); it('should create a Transpiled source mapper when a transpiler was configured', () => { config.transpilers = ['a transpiler']; + config.coverageAnalysis = 'perTest'; expect(SourceMapper.create([], config)).instanceOf(TranspiledSourceMapper); }); }); diff --git a/tsconfig.json b/tsconfig.json index 6c17741f8a..8a1bdbbf5b 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,6 +19,8 @@ "es5", "es2015.promise", "es2015.core", + "es2015.collection", + "es2015.iterable", "es2015.symbol", "es2015.symbol.wellknown", "es2015.proxy"