diff --git a/.editorconfig b/.editorconfig index 8f467bb9a1..ae54398377 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,4 +1,4 @@ -# 2 space indentation -[{*.ts,*.js,*.json}] +[{*.ts,*.js,*jsx,*tsx,*.json,*.code-workspace}] +insert_final_newline = true indent_style = space indent_size = 2 \ No newline at end of file diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..6164662455 --- /dev/null +++ b/.eslintignore @@ -0,0 +1,7 @@ +.gitignore +/e2e/** +/helpers/** +/perf/** +**/node_modules/** +*.d.ts +/packages/*/testResources/** diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000000..e5041bfb69 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,118 @@ + +module.exports = { + env: { + node: true + }, + parserOptions: { + sourceType: 'module', + project: [require.resolve('./tsconfig.lint.json')] + }, + parser: '@typescript-eslint/parser', + extends: ['prettier', 'eslint:recommended'], + plugins: ['@typescript-eslint', 'prettier'], + rules: { + 'prettier/prettier': ['error'], + 'sort-imports': 'off', // No auto-fix! + 'no-case-declarations': 'off', + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/array-type': [ + 'error', + { + default: 'array-simple' + } + ], + '@typescript-eslint/await-thenable': 'off', + '@typescript-eslint/ban-ts-ignore': 'error', + '@typescript-eslint/ban-types': 'error', + 'brace-style': 'off', + '@typescript-eslint/brace-style': 'error', + camelcase: 'off', + '@typescript-eslint/camelcase': 'error', + '@typescript-eslint/class-name-casing': 'error', + '@typescript-eslint/consistent-type-assertions': 'error', + '@typescript-eslint/consistent-type-definitions': 'error', + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/explicit-member-accessibility': 'off', + 'func-call-spacing': 'off', + '@typescript-eslint/func-call-spacing': 'error', + '@typescript-eslint/generic-type-naming': 'off', + indent: 'off', + '@typescript-eslint/indent': 'off', + '@typescript-eslint/interface-name-prefix': ['error', 'never'], + '@typescript-eslint/member-delimiter-style': 'off', + '@typescript-eslint/member-naming': 'error', + '@typescript-eslint/member-ordering': 'off', + 'no-array-constructor': 'off', + '@typescript-eslint/no-array-constructor': 'error', + 'no-empty-function': 'off', + '@typescript-eslint/no-empty-function': 'off', + '@typescript-eslint/no-empty-interface': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'no-extra-parens': 'off', + '@typescript-eslint/no-extra-parens': ['error', 'functions'], + '@typescript-eslint/no-extraneous-class': 'off', + '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-for-in-array': 'error', + '@typescript-eslint/no-inferrable-types': 'off', + 'no-magic-numbers': 'off', + '@typescript-eslint/no-magic-numbers': 'off', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-misused-promises': 'off', + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/no-non-null-assertion': 'error', + '@typescript-eslint/no-parameter-properties': 'off', + '@typescript-eslint/no-require-imports': 'off', + '@typescript-eslint/no-this-alias': 'off', + '@typescript-eslint/no-type-alias': 'off', + '@typescript-eslint/no-unnecessary-condition': 'off', + '@typescript-eslint/no-unnecessary-qualifier': 'error', + '@typescript-eslint/no-unnecessary-type-arguments': 'error', + '@typescript-eslint/no-unnecessary-type-assertion': 'error', + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': 'off', + 'no-use-before-define': 'off', + '@typescript-eslint/no-use-before-define': 'off', + 'no-useless-constructor': 'off', + '@typescript-eslint/no-useless-constructor': 'error', + '@typescript-eslint/no-var-requires': 'off', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-includes': 'error', + '@typescript-eslint/prefer-namespace-keyword': 'error', + '@typescript-eslint/prefer-readonly': 'error', + '@typescript-eslint/prefer-regexp-exec': 'error', + '@typescript-eslint/prefer-string-starts-ends-with': 'error', + '@typescript-eslint/promise-function-async': 'off', + quotes: 'off', + '@typescript-eslint/quotes': ['error', 'single', { avoidEscape: true }], + '@typescript-eslint/require-array-sort-compare': 'error', + 'require-await': 'off', + '@typescript-eslint/require-await': 'off', + '@typescript-eslint/restrict-plus-operands': 'error', + semi: 'off', + '@typescript-eslint/semi': 'off', + '@typescript-eslint/strict-boolean-expressions': 'off', + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/type-annotation-spacing': 'error', + '@typescript-eslint/typedef': 'off', + '@typescript-eslint/unbound-method': 'off', + '@typescript-eslint/unified-signatures': 'error' + }, + overrides: [ + { + files: ['*.ts', '*.tsx'], + rules: { + 'no-undef': 'off', + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + overrides: { + constructors: 'no-public', + properties: 'explicit' + } + } + ] + } + } + ] +}; diff --git a/.gitignore b/.gitignore index e024c0408c..fc38222ba1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ node_modules lerna-debug.log npm-debug.log stryker.log -tslint.log +lint.log coverage reports diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..14db9c73d9 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "printWidth": 150, + "singleQuote": true +} diff --git a/.travis.yml b/.travis.yml index 5021540108..dd66f20f64 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ node_js: - 'node' - '8' before_script: +- npm prune - npm ls sudo: 'false' addons: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e79216bb02..b3da275596 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -19,10 +19,10 @@ Get in touch with us through twitter or via the [Stryker gitter](https://gitter. ## Code style -Please adhere to our [editorconfig](https://editorconfig.org) and [tslint](https://palantir.github.io/tslint/) rules. If you're using vscode, please install the following extensions: +Please adhere to our [editorconfig](https://editorconfig.org) and [eslint](https://eslint.org/) rules. If you're using vscode, please install the following extensions: * The [editorconfig extension](https://github.com/editorconfig/editorconfig-vscode#editorconfig-for-visual-studio-code) -* The [tslint extension](https://github.com/Microsoft/vscode-tslint) (at least v1.0.0) +* The [eslint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) We configured the tslint extension to run on save in or [vscode workspace](#vscode-environment-configuration). @@ -50,7 +50,7 @@ We've chosen to **check in in our vscode configuration**. This makes development We recommend you to install the following plugins: * [editorconfig](https://marketplace.visualstudio.com/items?itemName=EditorConfig.EditorConfig), to adhere to our white spacing rules. -* [tslint](https://marketplace.visualstudio.com/items?itemName=ms-vscode.vscode-typescript-tslint-plugin), to adhere to our tslint rules (as well as having auto fix-on-save) +* [eslint extension](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) * [code spell checker](https://marketplace.visualstudio.com/items?itemName=streetsidesoftware.code-spell-checker), no particular reason, just prevent common typo's. After cloning this repo, open the workspace with `$ code workspace.code-workspace` (or open code and use file -> Open Workspace...). diff --git a/package.json b/package.json index 0a0a934036..09f793506a 100644 --- a/package.json +++ b/package.json @@ -15,11 +15,17 @@ "@types/rimraf": "2.0.2", "@types/sinon": "5.0.5", "@types/sinon-chai": "3.2.3", + "@typescript-eslint/eslint-plugin": "~2.3.0", + "@typescript-eslint/parser": "~2.3.0", "acorn": "^7.0.0", "chai": "^4.1.1", "chai-as-promised": "^7.1.1", "concurrently": "^5.0.0", "cross-env": "^6.0.0", + "eslint": "~6.4.0", + "eslint-config-prettier": "~6.3.0", + "eslint-plugin-import": "~2.18.2", + "eslint-plugin-prettier": "~3.1.0", "execa": "^2.0.3", "glob": "^7.1.1", "if-node-version": "^1.1.1", @@ -30,25 +36,21 @@ "link-parent-bin": "~1.0.0", "mocha": "^6.1.2", "nyc": "^14.0.0", + "prettier": "~1.18.2", "rimraf": "^3.0.0", "rxjs": "^6.4.0", "sinon": "^7.2.0", "sinon-chai": "^3.2.0", "source-map-support": "^0.5.6", - "tslint": "~5.20.0", - "tslint-consistent-codestyle": "~1.15.1", "typescript": "~3.5.3" }, - "prettier": { - "singleQuote": true - }, "scripts": { "all": "npm run clean && npm run lint && npm run build && npm run test && npm run e2e", "postinstall": "lerna bootstrap --no-ci && link-parent-bin", - "lint-info": "tslint --out tslint.log --project tsconfig.lint.json", - "lint-fix": "tslint --fix --project tsconfig.lint.json", + "lint": "eslint . --ext .ts,.tsx", + "lint:log": "eslint . --ext .ts,.tsx -f compact -o lint.log", + "lint:fix": "eslint . --ext .ts,.tsx --fix", "clean": "rimraf \"packages/api/*+(.d.ts|.js|.map)\" \"packages/*/+(test|src)/**/*+(.d.ts|.js|.map)\" \"packages/*/{.nyc_output,reports,coverage,*.tsbuildinfo}\"", - "lint": "tslint -p tsconfig.lint.json", "build": "tsc -b && lerna run build", "test": "npm run mocha", "mocha": "lerna run test --stream --concurrency 4", diff --git a/packages/api/.eslintrc b/packages/api/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/api/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/api/src/config/Config.ts b/packages/api/src/config/Config.ts index 9eb8889a45..6333b83b7a 100644 --- a/packages/api/src/config/Config.ts +++ b/packages/api/src/config/Config.ts @@ -1,7 +1,6 @@ import { LogLevel, MutationScoreThresholds, MutatorDescriptor, StrykerOptions } from '../../core'; export default class Config implements StrykerOptions { - [customConfig: string]: any; public files: string[]; diff --git a/packages/api/src/core/File.ts b/packages/api/src/core/File.ts index 0dd47a0dd3..bc2e3f2472 100644 --- a/packages/api/src/core/File.ts +++ b/packages/api/src/core/File.ts @@ -2,7 +2,6 @@ * Represents a file within Stryker. Could be a strictly in-memory file. */ export default class File { - private _textContent: string | undefined; private readonly _content: Buffer; @@ -23,14 +22,14 @@ export default class File { /** * Gets the underlying content as buffer. */ - get content(): Buffer { + public get content(): Buffer { return this._content; } /** * Gets the underlying content as string using utf8 encoding. */ - get textContent(): string { + public get textContent(): string { if (!this._textContent) { this._textContent = this.content.toString(); } diff --git a/packages/api/src/mutant/Mutator.ts b/packages/api/src/mutant/Mutator.ts index f0f542911b..bea939660b 100644 --- a/packages/api/src/mutant/Mutator.ts +++ b/packages/api/src/mutant/Mutator.ts @@ -2,5 +2,5 @@ import { File } from '../../core'; import Mutant from './Mutant'; export default interface Mutator { - mutate(inputFiles: ReadonlyArray): ReadonlyArray; + mutate(inputFiles: readonly File[]): readonly Mutant[]; } diff --git a/packages/api/src/plugin/Contexts.ts b/packages/api/src/plugin/Contexts.ts index 7de8819907..82e299548d 100644 --- a/packages/api/src/plugin/Contexts.ts +++ b/packages/api/src/plugin/Contexts.ts @@ -32,7 +32,7 @@ export interface TranspilerPluginContext extends OptionsContext { * The dependency injection context for a `TestRunnerPlugin` */ export interface TestRunnerPluginContext extends OptionsContext { - [commonTokens.sandboxFileNames]: ReadonlyArray; + [commonTokens.sandboxFileNames]: readonly string[]; } /** diff --git a/packages/api/src/plugin/Plugins.ts b/packages/api/src/plugin/Plugins.ts index 9632236a45..58277293ba 100644 --- a/packages/api/src/plugin/Plugins.ts +++ b/packages/api/src/plugin/Plugins.ts @@ -12,12 +12,13 @@ import { PluginKind } from './PluginKind'; * Represents a StrykerPlugin */ export type Plugin = - FactoryPlugin[]> | ClassPlugin[]>; + | FactoryPlugin>> + | ClassPlugin>>; /** * Represents a plugin that is created with a factory method */ -export interface FactoryPlugin[]> { +export interface FactoryPlugin>> { readonly kind: TPluginKind; readonly name: string; /** @@ -29,7 +30,7 @@ export interface FactoryPlugin[]> { +export interface ClassPlugin>> { readonly kind: TPluginKind; readonly name: string; /** @@ -45,8 +46,11 @@ export interface ClassPlugin[]>(kind: TPluginKind, name: string, injectableClass: InjectableClass): - ClassPlugin { +export function declareClassPlugin>>( + kind: TPluginKind, + name: string, + injectableClass: InjectableClass +): ClassPlugin { return { injectableClass, kind, @@ -60,8 +64,11 @@ export function declareClassPlugin[]>(kind: TPluginKind, name: string, factory: InjectableFunction): - FactoryPlugin { +export function declareFactoryPlugin>>( + kind: TPluginKind, + name: string, + factory: InjectableFunction +): FactoryPlugin { return { factory, kind, @@ -93,5 +100,5 @@ export type Plugins = { */ export interface PluginResolver { resolve(kind: T, name: string): Plugins[T]; - resolveAll(kind: T): Plugins[T][]; + resolveAll(kind: T): Array; } diff --git a/packages/api/src/plugin/tokens.ts b/packages/api/src/plugin/tokens.ts index c936a8c005..ca67722860 100644 --- a/packages/api/src/plugin/tokens.ts +++ b/packages/api/src/plugin/tokens.ts @@ -1,4 +1,3 @@ - /** * Define a string literal. * @param value Token literal diff --git a/packages/api/src/report/MutantStatus.ts b/packages/api/src/report/MutantStatus.ts index a3eb3f6ddb..db09e79381 100644 --- a/packages/api/src/report/MutantStatus.ts +++ b/packages/api/src/report/MutantStatus.ts @@ -1,5 +1,4 @@ enum MutantStatus { - /** * The status of a survived mutant, because it was not covered by any test. */ diff --git a/packages/api/src/report/Reporter.ts b/packages/api/src/report/Reporter.ts index 8a2c97997d..ab6e3a8c25 100644 --- a/packages/api/src/report/Reporter.ts +++ b/packages/api/src/report/Reporter.ts @@ -8,7 +8,6 @@ import SourceFile from './SourceFile'; * Represents a reporter which can report during or after a Stryker run */ interface Reporter { - /** * Called when a source file was loaded * @param file The immutable source file @@ -25,7 +24,7 @@ interface Reporter { * Called when mutants are matched with tests * @param results The immutable array of mutants */ - onAllMutantsMatchedWithTests?(results: ReadonlyArray): void; + onAllMutantsMatchedWithTests?(results: readonly MatchedMutant[]): void; /** * Called when a mutant was tested diff --git a/packages/api/src/report/ScoreResult.ts b/packages/api/src/report/ScoreResult.ts index bc6f1b6e9e..929b63f84c 100644 --- a/packages/api/src/report/ScoreResult.ts +++ b/packages/api/src/report/ScoreResult.ts @@ -2,7 +2,6 @@ * Represents a score result of a file or directory */ interface ScoreResult { - /** * The file or directory name of this score result node */ @@ -22,7 +21,7 @@ interface ScoreResult { * Any child directory/file score result nodes * If this score result represents a file, the length will be 0 */ - readonly childResults: ReadonlyArray; + readonly childResults: readonly ScoreResult[]; /** * The total number of mutants that were killed diff --git a/packages/api/src/test_framework/TestFramework.ts b/packages/api/src/test_framework/TestFramework.ts index d07e76f8c5..e64b4e4560 100644 --- a/packages/api/src/test_framework/TestFramework.ts +++ b/packages/api/src/test_framework/TestFramework.ts @@ -4,7 +4,6 @@ import TestSelection from './TestSelection'; * Represents a TestFramework which can select one or more tests to be executed. */ interface TestFramework { - /** * Creates a code fragment which, if included in a test run, * is ran before a particular test is run. diff --git a/packages/api/src/test_runner/TestRunner.ts b/packages/api/src/test_runner/TestRunner.ts index 56ee450490..ddbdecca41 100644 --- a/packages/api/src/test_runner/TestRunner.ts +++ b/packages/api/src/test_runner/TestRunner.ts @@ -33,7 +33,6 @@ import RunResult from './RunResult'; * If it doesn't exists globally, you don't have to do anything. In that case it's not an initial test run and there was no code instrumented. */ interface TestRunner { - /** * Optional. When implemented, will be called before runs are done on this test runner. * @returns A promise if stuff is initialized asynchronously, runs will not start until the promise is resolved. diff --git a/packages/api/src/transpile/Transpiler.ts b/packages/api/src/transpile/Transpiler.ts index ee51cca69b..231e66c3b4 100644 --- a/packages/api/src/transpile/Transpiler.ts +++ b/packages/api/src/transpile/Transpiler.ts @@ -12,7 +12,6 @@ import { File } from '../../core'; * They are also expected to keep track of any source-maps if `keepSourceMaps` is set to true, in order to implement `getMappedLocation` */ export default interface Transpiler { - /** * Transpile each file and return the result. * Should also return any untouched files in the result. @@ -30,6 +29,5 @@ export default interface Transpiler { * * @returns an rejection (if transpiling failed) or the output files to be used as input for the next transpiler */ - transpile(files: ReadonlyArray): Promise>; - + transpile(files: readonly File[]): Promise; } diff --git a/packages/api/test/integration/install-module/install-module.ts b/packages/api/test/integration/install-module/install-module.ts index 9925071d0b..b03db1c34c 100644 --- a/packages/api/test/integration/install-module/install-module.ts +++ b/packages/api/test/integration/install-module/install-module.ts @@ -3,7 +3,6 @@ import { exec } from 'child_process'; import * as path from 'path'; describe('we have a module using stryker', () => { - const modulePath = path.resolve(__dirname, '../../../testResources/module'); function execInModule(command: string): Promise<[string, string]> { @@ -22,14 +21,11 @@ describe('we have a module using stryker', () => { } describe('after installing Stryker', () => { - before(() => { - return execInModule('npm install') - .then(() => execInModule('npm run tsc')); + return execInModule('npm install').then(() => execInModule('npm run tsc')); }); describe('when typescript is compiled', () => { - const arrangeActAndAssertModule = (moduleToRun: string, partsToBeAsserted: string[]) => { it(`should output "${partsToBeAsserted}" when using the "${moduleToRun}" module`, () => { return execInModule(`npm run use:${moduleToRun}`).then(([stdout]) => { @@ -38,10 +34,17 @@ describe('we have a module using stryker', () => { }); }; arrangeActAndAssertModule('core', ['files', 'file']); - arrangeActAndAssertModule('config', ['plugins: [ \'stryker-*\' ]']); + arrangeActAndAssertModule('config', ["plugins: [ 'stryker-*' ]"]); arrangeActAndAssertModule('test_framework', ['framework-1']); - arrangeActAndAssertModule('mutant', ['mutatorName: \'foo\'']); - arrangeActAndAssertModule('report', ['empty', 'all', 'status: 3', 'originalLines: \'string\'', 'Mutant status runtime error: RuntimeError', 'transpile error: TranspileError']); + arrangeActAndAssertModule('mutant', ["mutatorName: 'foo'"]); + arrangeActAndAssertModule('report', [ + 'empty', + 'all', + 'status: 3', + "originalLines: 'string'", + 'Mutant status runtime error: RuntimeError', + 'transpile error: TranspileError' + ]); arrangeActAndAssertModule('test_runner', ['MyTestRunner']); arrangeActAndAssertModule('transpile', ['foo', 'bar']); }); diff --git a/packages/api/test/unit/config/Config.spec.ts b/packages/api/test/unit/config/Config.spec.ts index d36509415b..5541748888 100644 --- a/packages/api/test/unit/config/Config.spec.ts +++ b/packages/api/test/unit/config/Config.spec.ts @@ -3,11 +3,10 @@ import * as minimatch from 'minimatch'; import { Config } from '../../../config'; describe('Config', () => { - let sut: Config; const defaultThresholds = Object.freeze({ high: 80, low: 60, break: null }); - beforeEach(() => sut = new Config()); + beforeEach(() => (sut = new Config())); describe('defaults', () => { it('should set default thresholds as expected', () => { @@ -16,7 +15,6 @@ describe('Config', () => { }); describe('set', () => { - it('should override values', () => { const expectedFiles = ['a file']; sut.set({ files: expectedFiles }); @@ -36,7 +34,6 @@ describe('Config', () => { }); describe('default value for `mutate` property', () => { - let defaultMutatePatterns: string[]; beforeEach(() => { diff --git a/packages/api/test/unit/core/File.spec.ts b/packages/api/test/unit/core/File.spec.ts index 8a0045b028..c4c95d6c81 100644 --- a/packages/api/test/unit/core/File.spec.ts +++ b/packages/api/test/unit/core/File.spec.ts @@ -3,7 +3,6 @@ import { deserialize, serialize } from 'surrial'; import { File } from '../../../core'; describe('File', () => { - it('should allow utf8 encoded string content in the constructor', () => { const actual = new File('foobar.js', 'string-content'); expect(actual.content).deep.eq(Buffer.from('string-content')); diff --git a/packages/api/test/unit/core/LogLevel.spec.ts b/packages/api/test/unit/core/LogLevel.spec.ts index a9052eb950..7afd9c79c7 100644 --- a/packages/api/test/unit/core/LogLevel.spec.ts +++ b/packages/api/test/unit/core/LogLevel.spec.ts @@ -2,7 +2,6 @@ import { expect } from 'chai'; import LogLevel from '../../../src/core/LogLevel'; describe('LogLevel', () => { - function arrangeActAssertLogLevel(actual: LogLevel, expected: string) { it(`should provide "${expected}" for log level "${actual}"`, () => { expect(actual).eq(expected); diff --git a/packages/babel-transpiler/.eslintrc b/packages/babel-transpiler/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/babel-transpiler/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/babel-transpiler/src/BabelConfigReader.ts b/packages/babel-transpiler/src/BabelConfigReader.ts index 23ad0857a6..9e5a35ac68 100644 --- a/packages/babel-transpiler/src/BabelConfigReader.ts +++ b/packages/babel-transpiler/src/BabelConfigReader.ts @@ -6,7 +6,7 @@ import * as path from 'path'; import * as babel from './helpers/babelWrapper'; export interface StrykerBabelConfig { - extensions: ReadonlyArray; + extensions: readonly string[]; options: babel.TransformOptions; optionsFile: string | null; optionsApi?: Partial; @@ -24,10 +24,8 @@ const DEFAULT_BABEL_CONFIG: Readonly = Object.freeze({ }); export class BabelConfigReader { - public static inject = tokens(commonTokens.logger); - constructor(private readonly log: Logger) { - } + constructor(private readonly log: Logger) {} public readConfig(strykerOptions: StrykerOptions): StrykerBabelConfig { const babelConfig: StrykerBabelConfig = { diff --git a/packages/babel-transpiler/src/BabelTranspiler.ts b/packages/babel-transpiler/src/BabelTranspiler.ts index 7f23591514..a795d1b323 100644 --- a/packages/babel-transpiler/src/BabelTranspiler.ts +++ b/packages/babel-transpiler/src/BabelTranspiler.ts @@ -7,31 +7,31 @@ import { BabelConfigReader, StrykerBabelConfig } from './BabelConfigReader'; import * as babel from './helpers/babelWrapper'; import { toJSFileName } from './helpers/helpers'; -const DEFAULT_EXTENSIONS: ReadonlyArray = (babel as any).DEFAULT_EXTENSIONS; +const DEFAULT_EXTENSIONS: readonly string[] = (babel as any).DEFAULT_EXTENSIONS; export function babelTranspilerFactory(injector: Injector) { - return injector - .provideClass('babelConfigReader', BabelConfigReader) - .injectClass(BabelTranspiler); + return injector.provideClass('babelConfigReader', BabelConfigReader).injectClass(BabelTranspiler); } babelTranspilerFactory.inject = tokens(commonTokens.injector); export class BabelTranspiler implements Transpiler { private readonly babelConfig: StrykerBabelConfig; private readonly projectRoot: string; - private readonly extensions: ReadonlyArray; + private readonly extensions: readonly string[]; public static inject = tokens(commonTokens.options, commonTokens.produceSourceMaps, 'babelConfigReader'); - public constructor(options: StrykerOptions, produceSourceMaps: boolean, babelConfigReader: BabelConfigReader) { + constructor(options: StrykerOptions, produceSourceMaps: boolean, babelConfigReader: BabelConfigReader) { if (produceSourceMaps) { - throw new Error(`Invalid \`coverageAnalysis\` "${options.coverageAnalysis}" is not supported by the stryker-babel-transpiler. Not able to produce source maps yet. Please set it to "off".`); + throw new Error( + `Invalid \`coverageAnalysis\` "${options.coverageAnalysis}" is not supported by the stryker-babel-transpiler. Not able to produce source maps yet. Please set it to "off".` + ); } this.babelConfig = babelConfigReader.readConfig(options); this.projectRoot = this.determineProjectRoot(); this.extensions = [...DEFAULT_EXTENSIONS, ...this.babelConfig.extensions]; } - public async transpile(files: ReadonlyArray): Promise> { + public async transpile(files: readonly File[]): Promise { return files.map(file => this.transpileFileIfNeeded(file)); } diff --git a/packages/babel-transpiler/src/index.ts b/packages/babel-transpiler/src/index.ts index e92c5f6406..faccdf8ea6 100644 --- a/packages/babel-transpiler/src/index.ts +++ b/packages/babel-transpiler/src/index.ts @@ -1,6 +1,4 @@ import { declareFactoryPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import { babelTranspilerFactory } from './BabelTranspiler'; -export const strykerPlugins = [ - declareFactoryPlugin(PluginKind.Transpiler, 'babel', babelTranspilerFactory) -]; +export const strykerPlugins = [declareFactoryPlugin(PluginKind.Transpiler, 'babel', babelTranspilerFactory)]; diff --git a/packages/babel-transpiler/test/helpers/projectLoader.ts b/packages/babel-transpiler/test/helpers/projectLoader.ts index d1a7df2b15..7d484e2f7f 100644 --- a/packages/babel-transpiler/test/helpers/projectLoader.ts +++ b/packages/babel-transpiler/test/helpers/projectLoader.ts @@ -18,17 +18,14 @@ function readFile(fileName: string) { } export class ProjectLoader { - public static getFiles(basePath: string) { - return this.load(basePath) - .then(files => files.sort((a, b) => a.name.localeCompare(b.name))); + return this.load(basePath).then(files => files.sort((a, b) => a.name.localeCompare(b.name))); } private static load(basePath: string): Promise { return this.glob(basePath) .then(fileNames => fileNames.map(fileName => path.join(basePath, fileName))) - .then(fileNames => Promise.all(fileNames.map(fileName => - readFile(fileName).then(content => new File(fileName, this.normalize(content)))))); + .then(fileNames => Promise.all(fileNames.map(fileName => readFile(fileName).then(content => new File(fileName, this.normalize(content)))))); } private static normalize(content: Buffer) { @@ -37,12 +34,14 @@ export class ProjectLoader { } private static glob(basePath: string): Promise { - return new Promise((res, rej) => glob('**/*.*', { cwd: basePath }, (err, matches) => { - if (err) { - rej(err); - } else { - res(matches); - } - })); + return new Promise((res, rej) => + glob('**/*.*', { cwd: basePath }, (err, matches) => { + if (err) { + rej(err); + } else { + res(matches); + } + }) + ); } } diff --git a/packages/babel-transpiler/test/integration/BabelProjects.it.spec.ts b/packages/babel-transpiler/test/integration/BabelProjects.it.spec.ts index 0e167ffe34..2af9d9356f 100644 --- a/packages/babel-transpiler/test/integration/BabelProjects.it.spec.ts +++ b/packages/babel-transpiler/test/integration/BabelProjects.it.spec.ts @@ -9,7 +9,6 @@ import { BabelTranspiler, babelTranspilerFactory } from '../../src/BabelTranspil import { ProjectLoader } from '../helpers/projectLoader'; function describeIntegrationTest(projectName: string, babelConfig: Partial = {}) { - const projectDir = path.resolve(__dirname, '..', '..', 'testResources', projectName); babelConfig.optionsFile = path.join(projectDir, babelConfig.optionsFile || '.babelrc'); let projectFiles: File[] = []; @@ -20,9 +19,7 @@ function describeIntegrationTest(projectName: string, babelConfig: Partial { @@ -35,7 +32,7 @@ function describeIntegrationTest(projectName: string, babelConfig: Partial, expected: ReadonlyArray) { + function expectFilesEqual(actual: readonly File[], expected: readonly File[]) { expect(actual).lengthOf(expected.length); for (const i in expected) { expect(actual[i].name).deep.eq(expected[i].name); @@ -70,8 +67,8 @@ describe('A Babel project with babel.config.js config file', () => { const noop = () => {}; describeIntegrationTest('babelProjectWithBabelConfigJs', { extensions: ['.ts'], - optionsApi: { cache: { forever: noop }} as ConfigAPI, - optionsFile: 'babel.config.js', + optionsApi: { cache: { forever: noop } } as ConfigAPI, + optionsFile: 'babel.config.js' }); }); describe('A Babel project with .babelrc.js config file', () => { diff --git a/packages/babel-transpiler/test/unit/BabelConfigReader.spec.ts b/packages/babel-transpiler/test/unit/BabelConfigReader.spec.ts index 41ac6d5635..899c39ad73 100644 --- a/packages/babel-transpiler/test/unit/BabelConfigReader.spec.ts +++ b/packages/babel-transpiler/test/unit/BabelConfigReader.spec.ts @@ -44,7 +44,7 @@ describe(BabelConfigReader.name, () => { const expectedConfig: StrykerBabelConfig = { extensions: ['.ts'], options: { presets: ['env'] }, - optionsFile: null, + optionsFile: null }; sut.readConfig(factory.strykerOptions({ babel: expectedConfig })); expect(testInjector.logger.debug).calledWith(`Babel config is: ${JSON.stringify(expectedConfig, null, 2)}`); @@ -60,12 +60,17 @@ describe(BabelConfigReader.name, () => { it('should log a warning if the babelrc file cannot be read', () => { sinon.stub(fs, 'existsSync').returns(true); - sinon.stub(fs, 'readFileSync').withArgs(path.resolve('.babelrc'), 'utf8').returns('something, not json'); + sinon + .stub(fs, 'readFileSync') + .withArgs(path.resolve('.babelrc'), 'utf8') + .returns('something, not json'); sut.readConfig(factory.strykerOptions()); - expect(testInjector.logger.error).calledWith(`Error while reading "${path.resolve('.babelrc')}" file: SyntaxError: Unexpected token s in JSON at position 0`); + expect(testInjector.logger.error).calledWith( + `Error while reading "${path.resolve('.babelrc')}" file: SyntaxError: Unexpected token s in JSON at position 0` + ); }); - it('should set the babelConfig to an empty object if nothing is configured and .babelrc file didn\'t exits', () => { + it("should set the babelConfig to an empty object if nothing is configured and .babelrc file didn't exits", () => { const expected: StrykerBabelConfig = { extensions: [], options: {}, @@ -77,6 +82,9 @@ describe(BabelConfigReader.name, () => { function arrangeBabelOptionsFile(babelOptions: babel.TransformOptions, fileName = '.babelrc') { sinon.stub(fs, 'existsSync').returns(true); - sinon.stub(fs, 'readFileSync').withArgs(path.resolve(fileName), 'utf8').returns(JSON.stringify(babelOptions)); + sinon + .stub(fs, 'readFileSync') + .withArgs(path.resolve(fileName), 'utf8') + .returns(JSON.stringify(babelOptions)); } }); diff --git a/packages/babel-transpiler/test/unit/BabelTranspiler.spec.ts b/packages/babel-transpiler/test/unit/BabelTranspiler.spec.ts index 0bd9d5e44e..2567c56665 100644 --- a/packages/babel-transpiler/test/unit/BabelTranspiler.spec.ts +++ b/packages/babel-transpiler/test/unit/BabelTranspiler.spec.ts @@ -38,12 +38,11 @@ describe(BabelTranspiler.name, () => { function createSut(produceSourceMaps = false) { return testInjector.injector .provideValue(commonTokens.produceSourceMaps, produceSourceMaps) - .provideValue('babelConfigReader', babelConfigReaderMock as unknown as BabelConfigReader) + .provideValue('babelConfigReader', (babelConfigReaderMock as unknown) as BabelConfigReader) .injectClass(BabelTranspiler); } describe('constructor', () => { - function arrangeHappyFlow() { babelConfigReaderMock.readConfig.returns(babelConfig); sut = createSut(); @@ -56,14 +55,15 @@ describe(BabelTranspiler.name, () => { it('should throw if `produceSourceMaps` was true and coverage analysis is "perTest"', () => { options.coverageAnalysis = 'perTest'; - expect(() => new BabelTranspiler(options, /*produceSourceMaps:*/ true, babelConfigReaderMock as unknown as BabelConfigReader)).throws('Invalid `coverageAnalysis` "perTest" is not supported by the stryker-babel-transpiler. Not able to produce source maps yet. Please set it to "off".'); + expect(() => new BabelTranspiler(options, /*produceSourceMaps:*/ true, (babelConfigReaderMock as unknown) as BabelConfigReader)).throws( + 'Invalid `coverageAnalysis` "perTest" is not supported by the stryker-babel-transpiler. Not able to produce source maps yet. Please set it to "off".' + ); }); }); describe('transpile', () => { - function arrangeHappyFlow(transformResult: babel.BabelFileResult | null = { code: 'code' }) { - sut = new BabelTranspiler(options, /*produceSourceMaps:*/ false, babelConfigReaderMock as unknown as BabelConfigReader); + sut = new BabelTranspiler(options, /*produceSourceMaps:*/ false, (babelConfigReaderMock as unknown) as BabelConfigReader); transformStub.returns(transformResult); } @@ -82,9 +82,7 @@ describe(BabelTranspiler.name, () => { }); it('should allow users to define babel options', async () => { - const plugins = [ - 'fooPlugin', 'barPlugin' - ]; + const plugins = ['fooPlugin', 'barPlugin']; babelConfig.options.plugins = plugins.slice(); arrangeHappyFlow(); await sut.transpile(files); @@ -102,7 +100,7 @@ describe(BabelTranspiler.name, () => { babelConfig.options.filename = 'override'; babelConfig.options.filenameRelative = 'override'; arrangeHappyFlow(); - sut = new BabelTranspiler(options, /*produceSourceMaps:*/ false, babelConfigReaderMock as unknown as BabelConfigReader); + sut = new BabelTranspiler(options, /*produceSourceMaps:*/ false, (babelConfigReaderMock as unknown) as BabelConfigReader); await sut.transpile([files[0]]); expect(transformStub).calledWith(files[0].textContent, { cwd: process.cwd(), @@ -113,6 +111,7 @@ describe(BabelTranspiler.name, () => { it('should not transpile binary files', async () => { arrangeHappyFlow(); + // eslint-disable-next-line const inputFile = new File('myBinaryFile.png', 'S�L!##���XLDDDDDDDD\K�'); const actualResultFiles = await sut.transpile([inputFile]); expect(actualResultFiles[0]).eq(inputFile); @@ -128,17 +127,19 @@ describe(BabelTranspiler.name, () => { it('should report an error if transpiled code was undefined', async () => { arrangeHappyFlow({ code: undefined }); - return expect(sut.transpile([new File('f.js', '')])).rejectedWith('Could not transpile file "f.js". Babel transform function delivered \`undefined\`.'); + return expect(sut.transpile([new File('f.js', '')])).rejectedWith( + 'Could not transpile file "f.js". Babel transform function delivered `undefined`.' + ); }); it('should only call the transform function when the file extension is a known file extension', async () => { arrangeHappyFlow(); const inputFiles: File[] = [ - new File(`es6.es6`, 'es6 = true'), - new File(`js.js`, 'js = true'), - new File(`es.es`, 'es = true'), - new File(`jsx.jsx`, 'jsx = true'), - new File(`ignored.njs`, 'ignored') + new File('es6.es6', 'es6 = true'), + new File('js.js', 'js = true'), + new File('es.es', 'es = true'), + new File('jsx.jsx', 'jsx = true'), + new File('ignored.njs', 'ignored') ]; const actualResultFiles = await sut.transpile(inputFiles); expect(transformStub).callCount(inputFiles.length - 1); @@ -146,21 +147,17 @@ describe(BabelTranspiler.name, () => { expect(transformStub).calledWith('js = true'); expect(transformStub).calledWith('es = true'); expect(transformStub).calledWith('jsx = true'); - expect(actualResultFiles.map(file => file.name)).deep.eq([ - 'es6.js', - 'js.js', - 'es.js', - 'jsx.js', - 'ignored.njs' - ]); + expect(actualResultFiles.map(file => file.name)).deep.eq(['es6.js', 'js.js', 'es.js', 'jsx.js', 'ignored.njs']); }); it('should return with an error when the babel transform fails', async () => { const error = new Error('Syntax error'); transformStub.throws(error); sut = createSut(); - return expect(sut.transpile([new File('picture.js', 'S�L!##���XLDDDDDDDD\K�')])) - .rejectedWith(`Error while transpiling "picture.js". Inner error: Error: Syntax error`); + // eslint-disable-next-line + return expect(sut.transpile([new File('picture.js', 'S�L!##���XLDDDDDDDD\K�')])).rejectedWith( + 'Error while transpiling "picture.js". Inner error: Error: Syntax error' + ); }); }); }); diff --git a/packages/core/.eslintrc b/packages/core/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/core/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/core/src/Sandbox.ts b/packages/core/src/Sandbox.ts index e22a66a4b7..137a273126 100644 --- a/packages/core/src/Sandbox.ts +++ b/packages/core/src/Sandbox.ts @@ -20,7 +20,6 @@ interface FileMap { } export default class Sandbox { - private readonly log = getLogger(Sandbox.name); private testRunner: Required; private fileMap: FileMap; @@ -29,11 +28,12 @@ export default class Sandbox { private constructor( private readonly options: StrykerOptions, private readonly index: number, - private readonly files: ReadonlyArray, + private readonly files: readonly File[], private readonly testFramework: TestFramework | null, private readonly timeOverheadMS: number, private readonly loggingContext: LoggingClientContext, - temporaryDirectory: TemporaryDirectory) { + temporaryDirectory: TemporaryDirectory + ) { this.workingDirectory = temporaryDirectory.createRandomDirectory('sandbox'); this.log.debug('Creating a sandbox for files in %s', this.workingDirectory); } @@ -47,12 +47,12 @@ export default class Sandbox { public static create( options: StrykerOptions, index: number, - files: ReadonlyArray, + files: readonly File[], testFramework: TestFramework | null, timeoutOverheadMS: number, loggingContext: LoggingClientContext, - temporaryDirectory: TemporaryDirectory) - : Promise { + temporaryDirectory: TemporaryDirectory + ): Promise { const sandbox = new Sandbox(options, index, files, testFramework, timeoutOverheadMS, loggingContext, temporaryDirectory); return sandbox.initialize().then(() => sandbox); } @@ -72,10 +72,16 @@ export default class Sandbox { } else { const mutantFiles = transpiledMutant.transpileResult.outputFiles; if (transpiledMutant.mutant.testSelectionResult === TestSelectionResult.Failed) { - this.log.warn(`Failed find coverage data for this mutant, running all tests. This might have an impact on performance: ${transpiledMutant.mutant.toString()}`); + this.log.warn( + `Failed find coverage data for this mutant, running all tests. This might have an impact on performance: ${transpiledMutant.mutant.toString()}` + ); } await Promise.all(mutantFiles.map(mutatedFile => this.writeFileInSandbox(mutatedFile))); - const runResult = await this.run(this.calculateTimeout(transpiledMutant.mutant), this.getFilterTestsHooks(transpiledMutant.mutant), this.fileMap[transpiledMutant.mutant.fileName]); + const runResult = await this.run( + this.calculateTimeout(transpiledMutant.mutant), + this.getFilterTestsHooks(transpiledMutant.mutant), + this.fileMap[transpiledMutant.mutant.fileName] + ); await this.reset(mutantFiles); return this.collectMutantResult(transpiledMutant.mutant, runResult); } @@ -84,7 +90,9 @@ export default class Sandbox { private readonly retrieveEarlyResult = (transpiledMutant: TranspiledMutant): MutantResult | null => { if (transpiledMutant.transpileResult.error) { if (this.log.isDebugEnabled()) { - this.log.debug(`Transpile error occurred: "${transpiledMutant.transpileResult.error}" during transpiling of mutant ${transpiledMutant.mutant.toString()}`); + this.log.debug( + `Transpile error occurred: "${transpiledMutant.transpileResult.error}" during transpiling of mutant ${transpiledMutant.mutant.toString()}` + ); } const result = transpiledMutant.mutant.result(MutantStatus.TranspileError, []); return result; @@ -98,13 +106,11 @@ export default class Sandbox { // No early result possible, need to run in the sandbox later return null; } - } + }; private collectMutantResult(mutant: TestableMutant, runResult: RunResult): MutantResult { const status: MutantStatus = this.determineMutantState(runResult); - const testNames = runResult.tests - .filter(t => t.status !== TestStatus.Skipped) - .map(t => t.name); + const testNames = runResult.tests.filter(t => t.status !== TestStatus.Skipped).map(t => t.name); if (this.log.isDebugEnabled() && status === MutantStatus.RuntimeError) { const error = runResult.errorMessages ? runResult.errorMessages.toString() : '(undefined)'; this.log.debug('A runtime error occurred: %s during execution of mutant: %s', error, mutant.toString()); @@ -127,7 +133,7 @@ export default class Sandbox { } } - private reset(mutatedFiles: ReadonlyArray) { + private reset(mutatedFiles: readonly File[]) { const originalFiles = this.files.filter(originalFile => mutatedFiles.some(mutatedFile => mutatedFile.name === originalFile.name)); return Promise.all(originalFiles.map(file => writeFile(this.fileMap[file.name], file.content))); @@ -140,8 +146,7 @@ export default class Sandbox { private fillSandbox(): Promise { this.fileMap = Object.create(null); - const copyPromises = this.files - .map(file => this.fillFile(file)); + const copyPromises = this.files.map(file => this.fillFile(file)); return Promise.all(copyPromises); } @@ -151,16 +156,17 @@ export default class Sandbox { const basePath = process.cwd(); const nodeModules = await findNodeModules(basePath); if (nodeModules) { - await symlinkJunction(nodeModules, path.join(this.workingDirectory, 'node_modules')) - .catch((error: NodeJS.ErrnoException) => { - if (error.code === 'EEXIST') { - this.log.warn(normalizeWhitespaces(`Could not symlink "${nodeModules}" in sandbox directory, + await symlinkJunction(nodeModules, path.join(this.workingDirectory, 'node_modules')).catch((error: NodeJS.ErrnoException) => { + if (error.code === 'EEXIST') { + this.log.warn( + normalizeWhitespaces(`Could not symlink "${nodeModules}" in sandbox directory, it is already created in the sandbox. Please remove the node_modules from your sandbox files. - Alternatively, set \`symlinkNodeModules\` to \`false\` to disable this warning.`)); - } else { - this.log.warn(`Unexpected error while trying to symlink "${nodeModules}" in sandbox directory.`, error); - } - }); + Alternatively, set \`symlinkNodeModules\` to \`false\` to disable this warning.`) + ); + } else { + this.log.warn(`Unexpected error while trying to symlink "${nodeModules}" in sandbox directory.`, error); + } + }); } else { this.log.warn(`Could not find a node_modules folder to symlink into the sandbox directory. Search "${basePath}" and its parent directories`); } @@ -178,14 +184,14 @@ export default class Sandbox { private async initializeTestRunner(): Promise { const fileNames = Object.keys(this.fileMap).map(sourceFileName => this.fileMap[sourceFileName]); - this.log.debug(`Creating test runner %s`, this.index); + this.log.debug('Creating test runner %s', this.index); this.testRunner = ResilientTestRunnerFactory.create(this.options, fileNames, this.workingDirectory, this.loggingContext); await this.testRunner.init(); } private calculateTimeout(mutant: TestableMutant) { const baseTimeout = mutant.timeSpentScopedTests; - return (this.options.timeoutFactor * baseTimeout) + this.options.timeoutMS + this.timeOverheadMS; + return this.options.timeoutFactor * baseTimeout + this.options.timeoutMS + this.timeOverheadMS; } private getFilterTestsHooks(mutant: TestableMutant): string | undefined { diff --git a/packages/core/src/SandboxPool.ts b/packages/core/src/SandboxPool.ts index 0febc550f0..2486bb03f9 100644 --- a/packages/core/src/SandboxPool.ts +++ b/packages/core/src/SandboxPool.ts @@ -17,8 +17,7 @@ import { TemporaryDirectory } from './utils/TemporaryDirectory'; const MAX_CONCURRENT_INITIALIZING_SANDBOXES = 2; export class SandboxPool implements Disposable { - - private readonly allSandboxes: Promise[] = []; + private readonly allSandboxes: Array> = []; private readonly overheadTimeMS: number; public static inject = tokens( @@ -28,15 +27,17 @@ export class SandboxPool implements Disposable { coreTokens.initialRunResult, coreTokens.transpiledFiles, coreTokens.loggingContext, - coreTokens.temporaryDirectory); + coreTokens.temporaryDirectory + ); constructor( private readonly log: Logger, private readonly options: StrykerOptions, private readonly testFramework: TestFramework | null, initialRunResult: InitialTestRunResult, - private readonly initialFiles: ReadonlyArray, + private readonly initialFiles: readonly File[], private readonly loggingContext: LoggingClientContext, - private readonly tempDir: TemporaryDirectory) { + private readonly tempDir: TemporaryDirectory + ) { this.overheadTimeMS = initialRunResult.overheadTimeMS; } @@ -57,7 +58,7 @@ export class SandboxPool implements Disposable { private readonly runInSandbox = async ([mutant, sandbox]: [TranspiledMutant, Sandbox]) => { const result = await sandbox.runMutant(mutant); return { result, sandbox }; - } + }; private startSandboxes(): Observable { const concurrency = this.determineConcurrency(); @@ -67,7 +68,9 @@ export class SandboxPool implements Disposable { if (this.isDisposed) { return null; } else { - return this.registerSandbox(Sandbox.create(this.options, n, this.initialFiles, this.testFramework, this.overheadTimeMS, this.loggingContext, this.tempDir)); + return this.registerSandbox( + Sandbox.create(this.options, n, this.initialFiles, this.testFramework, this.overheadTimeMS, this.loggingContext, this.tempDir) + ); } }, MAX_CONCURRENT_INITIALIZING_SANDBOXES), filter(sandboxOrNull => !!sandboxOrNull), @@ -96,7 +99,7 @@ export class SandboxPool implements Disposable { private readonly registerSandbox = async (promisedSandbox: Promise): Promise => { this.allSandboxes.push(promisedSandbox); return promisedSandbox; - } + }; private isDisposed = false; public async dispose() { diff --git a/packages/core/src/ScoreResultCalculator.ts b/packages/core/src/ScoreResultCalculator.ts index a90d11205d..5921f3eb6d 100644 --- a/packages/core/src/ScoreResultCalculator.ts +++ b/packages/core/src/ScoreResultCalculator.ts @@ -9,9 +9,8 @@ import { freezeRecursively, setExitCode } from './utils/objectUtils'; const defaultScoreIfNoValidMutants = 100; export default class ScoreResultCalculator { - public static inject = tokens(commonTokens.logger); - constructor(private readonly log: Logger) { } + constructor(private readonly log: Logger) {} public calculate(results: MutantResult[]): ScoreResult { const scoreResult = this.calculateScoreResult(results, ''); @@ -30,16 +29,16 @@ export default class ScoreResultCalculator { this.log.info(`Final mutation score of ${formattedScore} is greater than or equal to break threshold ${breaking}`); } } else { - this.log.debug('No breaking threshold configured. Won\'t fail the build no matter how low your mutation score is. Set `thresholds.break` to change this behavior.'); + this.log.debug( + "No breaking threshold configured. Won't fail the build no matter how low your mutation score is. Set `thresholds.break` to change this behavior." + ); } } private wrapIfSingleFileScoreResult(scoreResult: ScoreResult): ScoreResult { if (scoreResult.representsFile) { return this.copy(scoreResult, { - childResults: [ - this.copy(scoreResult, { name: path.basename(scoreResult.name) }) - ], + childResults: [this.copy(scoreResult, { name: path.basename(scoreResult.name) })], name: path.dirname(scoreResult.name) }); } else { @@ -89,7 +88,9 @@ export default class ScoreResultCalculator { const filesGroupedByDirectory = _.groupBy(uniqueFiles, file => file.split(path.sep)[0]); return Object.keys(filesGroupedByDirectory) - .map(directory => this.calculateScoreResult(_.flatMap(filesGroupedByDirectory[directory], file => resultsGroupedByFiles[file]), childrenBasePath)) + .map(directory => + this.calculateScoreResult(_.flatMap(filesGroupedByDirectory[directory], file => resultsGroupedByFiles[file]), childrenBasePath) + ) .sort(this.compareScoreResults); } else { return []; @@ -132,8 +133,8 @@ export default class ScoreResultCalculator { const totalValid = totalUndetected + totalDetected; const totalInvalid = runtimeErrors + transpileErrors; const totalMutants = totalValid + totalInvalid; - const mutationScore = totalValid > 0 ? totalDetected / totalValid * 100 : defaultScoreIfNoValidMutants; - const mutationScoreBasedOnCoveredCode = totalValid > 0 ? totalDetected / totalCovered * 100 || 0 : defaultScoreIfNoValidMutants; + const mutationScore = totalValid > 0 ? (totalDetected / totalValid) * 100 : defaultScoreIfNoValidMutants; + const mutationScoreBasedOnCoveredCode = totalValid > 0 ? (totalDetected / totalCovered) * 100 || 0 : defaultScoreIfNoValidMutants; return { killed, mutationScore, diff --git a/packages/core/src/SourceFile.ts b/packages/core/src/SourceFile.ts index f03eb142b0..81c826d89f 100644 --- a/packages/core/src/SourceFile.ts +++ b/packages/core/src/SourceFile.ts @@ -1,9 +1,9 @@ import { File, Location, Position, Range } from '@stryker-mutator/api/core'; const enum CharacterCodes { - MaxAsciiCharacter = 0x7F, - LineFeed = 0x0A, // \n - CarriageReturn = 0x0D, // \r + MaxAsciiCharacter = 0x7f, + LineFeed = 0x0a, // \n + CarriageReturn = 0x0d, // \r LineSeparator = 0x2028, ParagraphSeparator = 0x2029 } @@ -20,25 +20,26 @@ export function isLineBreak(ch: number): boolean { // Only the characters in Table 3 are treated as line terminators. Other new line or line // breaking characters are treated as white space but not as line terminators. - return ch === CharacterCodes.LineFeed || + return ( + ch === CharacterCodes.LineFeed || ch === CharacterCodes.CarriageReturn || ch === CharacterCodes.LineSeparator || - ch === CharacterCodes.ParagraphSeparator; + ch === CharacterCodes.ParagraphSeparator + ); } export default class SourceFile { - private readonly lineStarts: number[]; constructor(public file: File) { this.lineStarts = this.computeLineStarts(); } - get name() { + public get name() { return this.file.name; } - get content(): string { + public get content(): string { return this.file.textContent; } @@ -84,11 +85,9 @@ export default class SourceFile { if (midValue === position) { return middle; - } - else if (midValue > position) { + } else if (midValue > position) { high = middle - 1; - } - else { + } else { low = middle + 1; } } diff --git a/packages/core/src/Stryker.ts b/packages/core/src/Stryker.ts index 16d1a3f61a..4df8e437e1 100644 --- a/packages/core/src/Stryker.ts +++ b/packages/core/src/Stryker.ts @@ -19,7 +19,6 @@ import { MutantTranspileScheduler } from './transpiler/MutantTranspileScheduler' import { TranspilerFacade } from './transpiler/TranspilerFacade'; export default class Stryker { - private readonly log: Logger; private readonly injector: Injector; @@ -53,14 +52,16 @@ export default class Stryker { } public async runMutationTest(): Promise { - const loggingContext = await LogConfigurator.configureLoggingServer(this.options.logLevel, this.options.fileLogLevel, this.options.allowConsoleColors); + const loggingContext = await LogConfigurator.configureLoggingServer( + this.options.logLevel, + this.options.fileLogLevel, + this.options.allowConsoleColors + ); this.timer.reset(); const inputFiles = await this.injector.injectClass(InputFileResolver).resolve(); if (inputFiles.files.length) { this.temporaryDirectory.initialize(); - const inputFileInjector = this.injector - .provideValue(coreTokens.loggingContext, loggingContext) - .provideValue(coreTokens.inputFiles, inputFiles); + const inputFileInjector = this.injector.provideValue(coreTokens.loggingContext, loggingContext).provideValue(coreTokens.inputFiles, inputFiles); const initialTestRunProcess = inputFileInjector .provideValue(commonTokens.produceSourceMaps, this.options.coverageAnalysis !== 'off') .provideFactory(coreTokens.pluginCreatorTranspiler, PluginCreator.createFactory(PluginKind.Transpiler)) @@ -83,8 +84,7 @@ export default class Stryker { .matchWithMutants(mutator.mutate(inputFiles.filesToMutate)); try { if (initialRunResult.runResult.tests.length && testableMutants.length) { - const mutationTestExecutor = mutationTestProcessInjector - .injectClass(MutationTestExecutor); + const mutationTestExecutor = mutationTestProcessInjector.injectClass(MutationTestExecutor); const mutantResults = await mutationTestExecutor.run(testableMutants); await this.reportScore(mutantResults, inputFileInjector); await this.logDone(); diff --git a/packages/core/src/StrykerCli.ts b/packages/core/src/StrykerCli.ts index 68dfd11917..08580eaeec 100644 --- a/packages/core/src/StrykerCli.ts +++ b/packages/core/src/StrykerCli.ts @@ -6,11 +6,10 @@ import LogConfigurator from './logging/LogConfigurator'; import Stryker from './Stryker'; export default class StrykerCli { - private command: string = ''; private strykerConfig: string | null = null; - constructor(private readonly argv: string[]) { } + constructor(private readonly argv: string[]) {} private list(val: string) { return val.split(','); @@ -20,34 +19,53 @@ export default class StrykerCli { program .version(require('../package.json').version) .usage(' [options] [stryker.conf.js]') - .description(`Possible commands: + .description( + `Possible commands: run: Run mutation testing init: Initialize Stryker for your project - Optional location to the stryker.conf.js file as last argument. That file should export a function which accepts a "config" object\n${CONFIG_SYNTAX_HELP}`) + Optional location to the stryker.conf.js file as last argument. That file should export a function which accepts a "config" object\n${CONFIG_SYNTAX_HELP}` + ) .arguments(' [stryker.conf.js]') .action((cmd: string, config: string) => { this.command = cmd; this.strykerConfig = config; }) - .option('-f, --files ', `A comma separated list of globbing expression used for selecting all files needed to run the tests. For a more detailed way of selecting inputfiles, please use a configFile. - Example: node_modules/a-lib/**/*.js,src/**/*.js,!src/index.js,a.js,test/**/*.js`, this.list) - .option('-m, --mutate ', `A comma separated list of globbing expression used for selecting the files that should be mutated. - Example: src/**/*.js,a.js`, this.list) - .option('--coverageAnalysis ', `The coverage analysis strategy you want to use. Default value: "perTest"`) - .option('--testFramework ', `The name of the test framework you want to use.`) - .option('--testRunner ', `The name of the test runner you want to use`) - .option('--mutator ', `The name of the mutant generator you want to use`) + .option( + '-f, --files ', + `A comma separated list of globbing expression used for selecting all files needed to run the tests. For a more detailed way of selecting inputfiles, please use a configFile. + Example: node_modules/a-lib/**/*.js,src/**/*.js,!src/index.js,a.js,test/**/*.js`, + this.list + ) + .option( + '-m, --mutate ', + `A comma separated list of globbing expression used for selecting the files that should be mutated. + Example: src/**/*.js,a.js`, + this.list + ) + .option('--coverageAnalysis ', 'The coverage analysis strategy you want to use. Default value: "perTest"') + .option('--testFramework ', 'The name of the test framework you want to use.') + .option('--testRunner ', 'The name of the test runner you want to use') + .option('--mutator ', 'The name of the mutant generator you want to use') .option('--transpilers ', 'A comma separated list of transpilers to use.', this.list) .option('--reporters ', 'A comma separated list of the names of the reporter(s) you want to use', this.list) .option('--plugins ', 'A list of plugins you want stryker to load (`require`).', this.list) .option('--timeoutMS ', 'Tweak the absolute timeout used to wait for a test runner to complete', parseInt) .option('--timeoutFactor ', 'Tweak the standard deviation relative to the normal test run of a mutated test', parseFloat) .option('--maxConcurrentTestRunners ', 'Set the number of max concurrent test runner to spawn (default: cpuCount)', parseInt) - .option('--logLevel ', 'Set the log level for the console. Possible values: fatal, error, warn, info, debug, trace, all and off. Default is "info"') - .option('--fileLogLevel ', 'Set the log4js log level for the "stryker.log" file. Possible values: fatal, error, warn, info, debug, trace, all and off. Default is "off"') + .option( + '--logLevel ', + 'Set the log level for the console. Possible values: fatal, error, warn, info, debug, trace, all and off. Default is "info"' + ) + .option( + '--fileLogLevel ', + 'Set the log4js log level for the "stryker.log" file. Possible values: fatal, error, warn, info, debug, trace, all and off. Default is "off"' + ) .option('--allowConsoleColors ', 'Indicates whether or not Stryker should use colors in console.', parseBoolean, true) - .option('--tempDirName ', 'Set the name of the directory that is used by Stryker as a working directory. This directory will be cleaned after a successful run') + .option( + '--tempDirName ', + 'Set the name of the directory that is used by Stryker as a working directory. This directory will be cleaned after a successful run' + ) .parse(this.argv); function parseBoolean(val: string) { @@ -66,7 +84,7 @@ export default class StrykerCli { delete program.Option; delete program.commands; for (const i in program) { - if (i.charAt(0) === '_') { + if (i.startsWith('_')) { delete program[i]; } } @@ -80,9 +98,9 @@ export default class StrykerCli { run: () => new Stryker(program).runMutationTest() }; - if (Object.keys(commands).indexOf(this.command) >= 0) { + if (Object.keys(commands).includes(this.command)) { commands[this.command]().catch(err => { - log.error(`an error occurred`, err); + log.error('an error occurred', err); if (!log.isTraceEnabled()) { log.info('Trouble figuring out what went wrong? Try `npx stryker run --fileLogLevel trace --logLevel debug` to get some more info.'); } diff --git a/packages/core/src/TestFrameworkOrchestrator.ts b/packages/core/src/TestFrameworkOrchestrator.ts index 8ebcb5d8bc..66c7c38ae9 100644 --- a/packages/core/src/TestFrameworkOrchestrator.ts +++ b/packages/core/src/TestFrameworkOrchestrator.ts @@ -6,19 +6,19 @@ import { coreTokens } from './di'; import { PluginCreator } from './di/PluginCreator'; export default class TestFrameworkOrchestrator { - - public static inject = tokens( - commonTokens.logger, - commonTokens.options, - coreTokens.pluginCreatorTestFramework); + public static inject = tokens(commonTokens.logger, commonTokens.options, coreTokens.pluginCreatorTestFramework); constructor( private readonly log: Logger, private readonly options: StrykerOptions, - private readonly pluginCreator: PluginCreator) { } + private readonly pluginCreator: PluginCreator + ) {} public determineTestFramework(): TestFramework | null { if (this.options.coverageAnalysis !== 'perTest') { - this.log.debug('The `coverageAnalysis` setting is "%s", not hooking into the test framework to achieve performance benefits.', this.options.coverageAnalysis); + this.log.debug( + 'The `coverageAnalysis` setting is "%s", not hooking into the test framework to achieve performance benefits.', + this.options.coverageAnalysis + ); return null; } else { return this.determineFrameworkWithCoverageAnalysis(); @@ -39,5 +39,4 @@ export default class TestFrameworkOrchestrator { } return testFramework; } - } diff --git a/packages/core/src/TestableMutant.ts b/packages/core/src/TestableMutant.ts index 094fef3f03..66b92e6490 100644 --- a/packages/core/src/TestableMutant.ts +++ b/packages/core/src/TestableMutant.ts @@ -13,51 +13,48 @@ export enum TestSelectionResult { } export default class TestableMutant { - private readonly _selectedTests: TestSelection[] = []; public specsRan: string[] = []; private _timeSpentScopedTests = 0; private _location: Location; public testSelectionResult = TestSelectionResult.Success; - get selectedTests(): TestSelection[] { + public get selectedTests(): TestSelection[] { return this._selectedTests; } - get timeSpentScopedTests() { + public get timeSpentScopedTests() { return this._timeSpentScopedTests; } - get fileName() { + public get fileName() { return this.mutant.fileName; } - get mutatorName() { + public get mutatorName() { return this.mutant.mutatorName; } - get range() { + public get range() { return this.mutant.range; } - get replacement() { + public get replacement() { return this.mutant.replacement; } - get location() { + public get location() { if (!this._location) { this._location = this.sourceFile.getLocation(this.range); } return this._location; } - get mutatedCode() { - return this.sourceFile.content.substr(0, this.range[0]) + - this.replacement + - this.sourceFile.content.substr(this.range[1]); + public get mutatedCode() { + return this.sourceFile.content.substr(0, this.range[0]) + this.replacement + this.sourceFile.content.substr(this.range[1]); } - get originalCode() { + public get originalCode() { return this.sourceFile.content; } @@ -71,8 +68,7 @@ export default class TestableMutant { this._timeSpentScopedTests += testResult.timeSpentMs; } - constructor(public readonly id: string, public mutant: Mutant, public sourceFile: SourceFile) { - } + constructor(public readonly id: string, public mutant: Mutant, public sourceFile: SourceFile) {} public get originalLines() { const [startIndex, endIndex] = this.getMutationLineIndexes(); @@ -81,7 +77,11 @@ export default class TestableMutant { public get mutatedLines() { const [startIndex, endIndex] = this.getMutationLineIndexes(); - return this.sourceFile.content.substring(startIndex, this.mutant.range[0]) + this.mutant.replacement + this.sourceFile.content.substring(this.mutant.range[1], endIndex); + return ( + this.sourceFile.content.substring(startIndex, this.mutant.range[0]) + + this.mutant.replacement + + this.sourceFile.content.substring(this.mutant.range[1], endIndex) + ); } private getMutationLineIndexes() { diff --git a/packages/core/src/TranspiledMutant.ts b/packages/core/src/TranspiledMutant.ts index 920dddd9ed..7e8a86a4cc 100644 --- a/packages/core/src/TranspiledMutant.ts +++ b/packages/core/src/TranspiledMutant.ts @@ -2,12 +2,11 @@ import TestableMutant from './TestableMutant'; import TranspileResult from './transpiler/TranspileResult'; export default class TranspiledMutant { - /** * Creates a transpiled mutant * @param mutant The mutant which is just transpiled * @param transpileResult The transpile result of the mutant * @param changedAnyTranspiledFiles Indicated whether or not this mutant changed the transpiled output files. This is not always the case, for example: mutating a TS interface */ - constructor(public mutant: TestableMutant, public transpileResult: TranspileResult, public changedAnyTranspiledFiles: boolean) { } + constructor(public mutant: TestableMutant, public transpileResult: TranspileResult, public changedAnyTranspiledFiles: boolean) {} } diff --git a/packages/core/src/child-proxy/ChildProcessCrashedError.ts b/packages/core/src/child-proxy/ChildProcessCrashedError.ts index 779f976d66..a0441e7175 100644 --- a/packages/core/src/child-proxy/ChildProcessCrashedError.ts +++ b/packages/core/src/child-proxy/ChildProcessCrashedError.ts @@ -1,12 +1,7 @@ import { StrykerError } from '@stryker-mutator/util'; export default class ChildProcessCrashedError extends StrykerError { - constructor( - public readonly pid: number, - message: string, - public readonly exitCode?: number, - public readonly signal?: string, - innerError?: Error) { + constructor(public readonly pid: number, message: string, public readonly exitCode?: number, public readonly signal?: string, innerError?: Error) { super(message, innerError); Error.captureStackTrace(this, ChildProcessCrashedError); // TS recommendation: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work diff --git a/packages/core/src/child-proxy/ChildProcessProxy.ts b/packages/core/src/child-proxy/ChildProcessProxy.ts index f3e84f2955..929d04149f 100644 --- a/packages/core/src/child-proxy/ChildProcessProxy.ts +++ b/packages/core/src/child-proxy/ChildProcessProxy.ts @@ -30,15 +30,22 @@ export default class ChildProcessProxy implements Disposable { private readonly worker: ChildProcess; private readonly initTask: Task; - private disposeTask: ExpirableTask | undefined; + private disposeTask: ExpirableTask | undefined; private currentError: ChildProcessCrashedError | undefined; - private readonly workerTasks: Task[] = []; + private readonly workerTasks: Task[] = []; private readonly log = getLogger(ChildProcessProxy.name); private readonly stdoutBuilder = new StringBuilder(); private readonly stderrBuilder = new StringBuilder(); private isDisposed = false; - private constructor(requirePath: string, requireName: string, loggingContext: LoggingClientContext, options: StrykerOptions, additionalInjectableValues: unknown, workingDirectory: string) { + private constructor( + requirePath: string, + requireName: string, + loggingContext: LoggingClientContext, + options: StrykerOptions, + additionalInjectableValues: unknown, + workingDirectory: string + ) { this.worker = fork(require.resolve('./ChildProcessProxyWorker'), [autoStart], { silent: true, execArgv: [] }); this.initTask = new Task(); this.log.debug('Starting %s in child process %s', requirePath, this.worker.pid); @@ -62,14 +69,14 @@ export default class ChildProcessProxy implements Disposable { /** * @description Creates a proxy where each function of the object created using the constructorFunction arg is ran inside of a child process */ - public static create[]>( + public static create>>( requirePath: string, loggingContext: LoggingClientContext, options: StrykerOptions, additionalInjectableValues: TAdditionalContext, workingDirectory: string, - InjectableClass: InjectableClass): - ChildProcessProxy { + InjectableClass: InjectableClass + ): ChildProcessProxy { return new ChildProcessProxy(requirePath, InjectableClass.name, loggingContext, options, additionalInjectableValues, workingDirectory); } @@ -158,9 +165,7 @@ export default class ChildProcessProxy implements Disposable { } private reportError(error: Error) { - this.workerTasks - .filter(task => !task.isCompleted) - .forEach(task => task.reject(error)); + this.workerTasks.filter(task => !task.isCompleted).forEach(task => task.reject(error)); } private readonly handleUnexpectedExit = (code: number, signal: string) => { @@ -172,14 +177,19 @@ export default class ChildProcessProxy implements Disposable { this.log.warn(`Child process [pid ${this.currentError.pid}] ran out of memory. Stdout and stderr are logged on debug level.`); this.log.debug(stdoutAndStderr()); } else { - this.currentError = new ChildProcessCrashedError(this.worker.pid, `Child process [pid ${this.worker.pid}] exited unexpectedly with exit code ${code} (${signal || 'without signal'}). ${stdoutAndStderr()}`, code, signal); + this.currentError = new ChildProcessCrashedError( + this.worker.pid, + `Child process [pid ${this.worker.pid}] exited unexpectedly with exit code ${code} (${signal || 'without signal'}). ${stdoutAndStderr()}`, + code, + signal + ); this.log.warn(this.currentError.message, this.currentError); } this.reportError(this.currentError); function processOutOfMemory() { - return output.indexOf('JavaScript heap out of memory') >= 0; + return output.includes('JavaScript heap out of memory'); } function stdoutAndStderr() { @@ -189,16 +199,18 @@ export default class ChildProcessProxy implements Disposable { return 'Stdout and stderr were empty.'; } } - } + }; private readonly handleError = (error: Error) => { if (this.innerProcessIsCrashed(error)) { this.log.warn(`Child process [pid ${this.worker.pid}] has crashed. See other warning messages for more info.`, error); - this.reportError(new ChildProcessCrashedError(this.worker.pid, `Child process [pid ${this.worker.pid}] has crashed`, undefined, undefined, error)); + this.reportError( + new ChildProcessCrashedError(this.worker.pid, `Child process [pid ${this.worker.pid}] has crashed`, undefined, undefined, error) + ); } else { this.reportError(error); } - } + }; private innerProcessIsCrashed(error: Error) { return isErrnoException(error) && (error.code === BROKEN_PIPE_ERROR_CODE || error.code === IPC_CHANNEL_CLOSED_ERROR_CODE); @@ -214,8 +226,8 @@ export default class ChildProcessProxy implements Disposable { try { await this.disposeTask.promise; } finally { - this.log.debug('Kill %s', this.worker.pid); - await kill(this.worker.pid); + this.log.debug('Kill %s', this.worker.pid); + await kill(this.worker.pid); } } } diff --git a/packages/core/src/child-proxy/ChildProcessProxyWorker.ts b/packages/core/src/child-proxy/ChildProcessProxyWorker.ts index 4177ca2880..30c195db04 100644 --- a/packages/core/src/child-proxy/ChildProcessProxyWorker.ts +++ b/packages/core/src/child-proxy/ChildProcessProxyWorker.ts @@ -9,7 +9,6 @@ import { deserialize, serialize } from '../utils/objectUtils'; import { autoStart, CallMessage, ParentMessage, ParentMessageKind, WorkerMessage, WorkerMessageKind } from './messageProtocol'; export default class ChildProcessProxyWorker { - private log: Logger; public realSubject: any; @@ -33,7 +32,7 @@ export default class ChildProcessProxyWorker { LogConfigurator.configureChildProcess(message.loggingContext); this.log = getLogger(ChildProcessProxyWorker.name); this.handlePromiseRejections(); - let injector = buildChildProcessInjector(message.options as unknown as Config); + let injector = buildChildProcessInjector((message.options as unknown) as Config); const locals = message.additionalInjectableValues as any; for (const token of Object.keys(locals)) { injector = injector.provideValue(token, locals[token]); @@ -56,7 +55,8 @@ export default class ChildProcessProxyWorker { kind: ParentMessageKind.Result, result }); - }).catch(error => { + }) + .catch(error => { this.send({ correlationId: message.correlationId, error: errorToString(error), @@ -93,7 +93,10 @@ export default class ChildProcessProxyWorker { private removeAnyAdditionalMessageListeners(exceptListener: NodeJS.MessageListener) { process.listeners('message').forEach(listener => { if (listener !== exceptListener) { - this.log.debug('Removing an additional message listener, we don\'t want eavesdropping on our inter-process communication: %s', listener.toString()); + this.log.debug( + "Removing an additional message listener, we don't want eavesdropping on our inter-process communication: %s", + listener.toString() + ); process.removeListener('message', listener); } }); @@ -105,7 +108,7 @@ export default class ChildProcessProxyWorker { * See issue 350: https://github.com/stryker-mutator/stryker/issues/350 */ private handlePromiseRejections() { - const unhandledRejections: Promise[] = []; + const unhandledRejections: Array> = []; process.on('unhandledRejection', (reason, promise) => { const unhandledPromiseId = unhandledRejections.push(promise); this.log.debug(`UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: ${unhandledPromiseId}): ${reason}`); @@ -119,6 +122,6 @@ export default class ChildProcessProxyWorker { // Prevent side effects for merely requiring the file // Only actually start the child worker when it is requested -if (process.argv.indexOf(autoStart) !== -1) { +if (process.argv.includes(autoStart)) { new ChildProcessProxyWorker(); } diff --git a/packages/core/src/child-proxy/OutOfMemoryError.ts b/packages/core/src/child-proxy/OutOfMemoryError.ts index 9b1929cc66..48bb07e3a6 100644 --- a/packages/core/src/child-proxy/OutOfMemoryError.ts +++ b/packages/core/src/child-proxy/OutOfMemoryError.ts @@ -1,11 +1,11 @@ import ChildProcessCrashedError from './ChildProcessCrashedError'; export default class OutOfMemoryError extends ChildProcessCrashedError { - constructor(pid: number, exitCode: number) { - super(pid, `Process ${pid} ran out of memory`, exitCode); - this.message = `Process `; - Error.captureStackTrace(this, OutOfMemoryError); - // TS recommendation: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work - Object.setPrototypeOf(this, OutOfMemoryError.prototype); - } + constructor(pid: number, exitCode: number) { + super(pid, `Process ${pid} ran out of memory`, exitCode); + this.message = 'Process '; + Error.captureStackTrace(this, OutOfMemoryError); + // TS recommendation: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work + Object.setPrototypeOf(this, OutOfMemoryError.prototype); + } } diff --git a/packages/core/src/child-proxy/messageProtocol.ts b/packages/core/src/child-proxy/messageProtocol.ts index a055d360d0..1a24e838d1 100644 --- a/packages/core/src/child-proxy/messageProtocol.ts +++ b/packages/core/src/child-proxy/messageProtocol.ts @@ -31,7 +31,9 @@ export interface InitMessage { additionalInjectableValues: unknown; } -export interface DisposeMessage { kind: WorkerMessageKind.Dispose; } +export interface DisposeMessage { + kind: WorkerMessageKind.Dispose; +} export interface WorkResult { kind: ParentMessageKind.Result; diff --git a/packages/core/src/config/ConfigEditorApplier.ts b/packages/core/src/config/ConfigEditorApplier.ts index 7283513707..9f78b7882f 100644 --- a/packages/core/src/config/ConfigEditorApplier.ts +++ b/packages/core/src/config/ConfigEditorApplier.ts @@ -8,11 +8,9 @@ import { PluginCreator } from '../di/PluginCreator'; * Class that applies all config editor plugins */ export class ConfigEditorApplier implements ConfigEditor { - public static inject = tokens(commonTokens.pluginResolver, coreTokens.pluginCreatorConfigEditor); - constructor(private readonly pluginResolver: PluginResolver, - private readonly pluginCreator: PluginCreator) { } + constructor(private readonly pluginResolver: PluginResolver, private readonly pluginCreator: PluginCreator) {} public edit(config: Config): void { this.pluginResolver.resolveAll(PluginKind.ConfigEditor).forEach(plugin => { diff --git a/packages/core/src/config/ConfigReader.ts b/packages/core/src/config/ConfigReader.ts index cc32dd27a3..d4f75fedf1 100644 --- a/packages/core/src/config/ConfigReader.ts +++ b/packages/core/src/config/ConfigReader.ts @@ -8,18 +8,13 @@ import * as _ from 'lodash'; import * as path from 'path'; import { coreTokens } from '../di'; -export const CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' + - ' config.set({\n' + - ' // your config\n' + - ' });\n' + - ' };'; +export const CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' + ' config.set({\n' + ' // your config\n' + ' });\n' + ' };'; const DEFAULT_CONFIG_FILE = 'stryker.conf.js'; export default class ConfigReader { - public static inject = tokens(coreTokens.cliOptions, commonTokens.logger); - constructor(private readonly cliOptions: Partial, private readonly log: Logger) { } + constructor(private readonly cliOptions: Partial, private readonly log: Logger) {} public readConfig() { const configModule = this.loadConfigModule(); @@ -38,7 +33,7 @@ export default class ConfigReader { private loadConfigModule(): Function { // Dummy module to be returned if no config file is loaded. - let configModule: Function = () => { }; + let configModule: Function = () => {}; if (!this.cliOptions.configFile) { try { @@ -61,7 +56,7 @@ export default class ConfigReader { throw new StrykerError(`File ${configFileName} does not exist!`, e); } else { this.log.info('Stryker can help you setup a `stryker.conf` file for your project.'); - this.log.info('Please execute `stryker init` in your project\'s root directory.'); + this.log.info("Please execute `stryker init` in your project's root directory."); throw new StrykerError('Invalid config file', e); } } diff --git a/packages/core/src/config/ConfigValidator.ts b/packages/core/src/config/ConfigValidator.ts index 32c1581e3e..f902f288dd 100644 --- a/packages/core/src/config/ConfigValidator.ts +++ b/packages/core/src/config/ConfigValidator.ts @@ -1,5 +1,5 @@ import { Config } from '@stryker-mutator/api/config'; -import { LogLevel, MutationScoreThresholds, MutatorDescriptor, StrykerOptions } from '@stryker-mutator/api/core'; +import { LogLevel, MutationScoreThresholds, StrykerOptions } from '@stryker-mutator/api/core'; import { Logger } from '@stryker-mutator/api/logging'; import { commonTokens, tokens } from '@stryker-mutator/api/plugin'; import { TestFramework } from '@stryker-mutator/api/test_framework'; @@ -7,13 +7,13 @@ import { StrykerError } from '@stryker-mutator/util'; import { coreTokens } from '../di'; export default class ConfigValidator { - private isValid = true; public static inject = tokens(commonTokens.logger, commonTokens.options, coreTokens.testFramework); constructor( private readonly log: Logger, private readonly options: Readonly, - private readonly testFramework: TestFramework | null) { } + private readonly testFramework: TestFramework | null + ) {} public validate() { this.validateTestFramework(); @@ -34,14 +34,16 @@ export default class ConfigValidator { private validateTestFramework() { if (this.options.coverageAnalysis === 'perTest' && !this.testFramework) { - this.invalidate('Configured coverage analysis "perTest" requires there to be a testFramework configured. Either configure a testFramework or set coverageAnalysis to "all" or "off".'); + this.invalidate( + 'Configured coverage analysis "perTest" requires there to be a testFramework configured. Either configure a testFramework or set coverageAnalysis to "all" or "off".' + ); } } private validateMutator() { const mutator = this.options.mutator; if (typeof mutator === 'object') { - const mutatorDescriptor = mutator as MutatorDescriptor; + const mutatorDescriptor = mutator; this.validateIsString('mutator.name', mutatorDescriptor.name); this.validateIsStringArray('mutator.excludedMutations', mutatorDescriptor.excludedMutations); } else if (typeof mutator !== 'string') { @@ -75,9 +77,19 @@ export default class ConfigValidator { private validateLogLevel(logProperty: 'logLevel' | 'fileLogLevel') { const logLevel = this.options[logProperty]; - const VALID_LOG_LEVEL_VALUES = [LogLevel.Fatal, LogLevel.Error, LogLevel.Warning, LogLevel.Information, LogLevel.Debug, LogLevel.Trace, LogLevel.Off]; - if (VALID_LOG_LEVEL_VALUES.indexOf(logLevel) < 0) { - this.invalidate(`Value "${logLevel}" is invalid for \`${logProperty}\`. Expected one of the following: ${this.joinQuotedList(VALID_LOG_LEVEL_VALUES)}`); + const VALID_LOG_LEVEL_VALUES = [ + LogLevel.Fatal, + LogLevel.Error, + LogLevel.Warning, + LogLevel.Information, + LogLevel.Debug, + LogLevel.Trace, + LogLevel.Off + ]; + if (!VALID_LOG_LEVEL_VALUES.includes(logLevel)) { + this.invalidate( + `Value "${logLevel}" is invalid for \`${logProperty}\`. Expected one of the following: ${this.joinQuotedList(VALID_LOG_LEVEL_VALUES)}` + ); } } @@ -89,18 +101,24 @@ export default class ConfigValidator { private validateCoverageAnalysis() { const VALID_COVERAGE_ANALYSIS_VALUES = ['perTest', 'all', 'off']; const coverageAnalysis = this.options.coverageAnalysis; - if (VALID_COVERAGE_ANALYSIS_VALUES.indexOf(coverageAnalysis) < 0) { - this.invalidate(`Value "${coverageAnalysis}" is invalid for \`coverageAnalysis\`. Expected one of the following: ${this.joinQuotedList(VALID_COVERAGE_ANALYSIS_VALUES)}`); + if (!VALID_COVERAGE_ANALYSIS_VALUES.includes(coverageAnalysis)) { + this.invalidate( + `Value "${coverageAnalysis}" is invalid for \`coverageAnalysis\`. Expected one of the following: ${this.joinQuotedList( + VALID_COVERAGE_ANALYSIS_VALUES + )}` + ); } } private validateCoverageAnalysisWithRespectToTranspilers() { - if (Array.isArray(this.options.transpilers) && - this.options.transpilers.length > 1 && - this.options.coverageAnalysis !== 'off') { - this.invalidate(`Value "${this.options.coverageAnalysis}" for \`coverageAnalysis\` is invalid with multiple transpilers (configured transpilers: ${ - this.options.transpilers.join(', ') - }). Please report this to the Stryker team if you whish this feature to be implemented`); + if (Array.isArray(this.options.transpilers) && this.options.transpilers.length > 1 && this.options.coverageAnalysis !== 'off') { + this.invalidate( + `Value "${ + this.options.coverageAnalysis + }" for \`coverageAnalysis\` is invalid with multiple transpilers (configured transpilers: ${this.options.transpilers.join( + ', ' + )}). Please report this to the Stryker team if you whish this feature to be implemented` + ); } } diff --git a/packages/core/src/di/PluginCreator.ts b/packages/core/src/di/PluginCreator.ts index 5739c58d0b..7da04055c1 100644 --- a/packages/core/src/di/PluginCreator.ts +++ b/packages/core/src/di/PluginCreator.ts @@ -1,13 +1,22 @@ -import { ClassPlugin, commonTokens, FactoryPlugin, Plugin, PluginContexts, PluginInterfaces, PluginKind, PluginResolver, tokens } from '@stryker-mutator/api/plugin'; +import { + ClassPlugin, + commonTokens, + FactoryPlugin, + Plugin, + PluginContexts, + PluginInterfaces, + PluginKind, + PluginResolver, + tokens +} from '@stryker-mutator/api/plugin'; import { InjectableFunctionWithInject, InjectionToken, Injector } from 'typed-inject'; export class PluginCreator { - private constructor( private readonly kind: TPluginKind, private readonly pluginResolver: PluginResolver, - private readonly injector: Injector) { - } + private readonly injector: Injector + ) {} public create(name: string): PluginInterfaces[TPluginKind] { const plugin = this.pluginResolver.resolve(this.kind, name); @@ -20,17 +29,16 @@ export class PluginCreator { } } - private isFactoryPlugin(plugin: Plugin): - plugin is FactoryPlugin[]> { - return !!(plugin as FactoryPlugin[]>).factory; + private isFactoryPlugin(plugin: Plugin): plugin is FactoryPlugin>> { + return !!(plugin as FactoryPlugin>>).factory; } - private isClassPlugin(plugin: Plugin): - plugin is ClassPlugin[]> { - return !!(plugin as ClassPlugin[]>).injectableClass; + private isClassPlugin(plugin: Plugin): plugin is ClassPlugin>> { + return !!(plugin as ClassPlugin>>).injectableClass; } - public static createFactory(kind: TPluginKind) - : InjectableFunctionWithInject, [typeof commonTokens.pluginResolver, typeof commonTokens.injector]> { + public static createFactory( + kind: TPluginKind + ): InjectableFunctionWithInject, [typeof commonTokens.pluginResolver, typeof commonTokens.injector]> { function factory(pluginResolver: PluginResolver, injector: Injector): PluginCreator { return new PluginCreator(kind, pluginResolver, injector); } diff --git a/packages/core/src/di/PluginLoader.ts b/packages/core/src/di/PluginLoader.ts index 0c12ff0edc..7daf83604f 100644 --- a/packages/core/src/di/PluginLoader.ts +++ b/packages/core/src/di/PluginLoader.ts @@ -10,14 +10,14 @@ import * as coreTokens from './coreTokens'; const IGNORED_PACKAGES = ['core', 'api', 'util']; interface PluginModule { - strykerPlugins: Plugin[]; + strykerPlugins: Array>; } export class PluginLoader implements PluginResolver { - private readonly pluginsByKind: Map[]> = new Map(); + private readonly pluginsByKind: Map>> = new Map(); public static inject = tokens(commonTokens.logger, coreTokens.pluginDescriptors); - constructor(private readonly log: Logger, private readonly pluginDescriptors: ReadonlyArray) { } + constructor(private readonly log: Logger, private readonly pluginDescriptors: readonly string[]) {} public load() { this.resolvePluginModules().forEach(moduleName => { @@ -32,33 +32,34 @@ export class PluginLoader implements PluginResolver { if (plugin) { return plugin as any; } else { - throw new Error(`Cannot load ${kind} plugin "${name}". Did you forget to install it? Loaded ${kind - } plugins were: ${plugins.map(p => p.name).join(', ')}`); + throw new Error( + `Cannot load ${kind} plugin "${name}". Did you forget to install it? Loaded ${kind} plugins were: ${plugins.map(p => p.name).join(', ')}` + ); } } else { throw new Error(`Cannot load ${kind} plugin "${name}". In fact, no ${kind} plugins were loaded. Did you forget to install it?`); } } - public resolveAll(kind: T): Plugins[T][] { + public resolveAll(kind: T): Array { const plugins = this.pluginsByKind.get(kind); - return plugins || [] as any; + return plugins || ([] as any); } private resolvePluginModules() { const modules: string[] = []; this.pluginDescriptors.forEach(pluginExpression => { if (_.isString(pluginExpression)) { - if (pluginExpression.indexOf('*') !== -1) { - + if (pluginExpression.includes('*')) { // Plugin directory is the node_modules folder of the module that installed stryker // So if current __dirname is './@stryker-mutator/core/src/di' so 4 directories above const pluginDirectory = path.dirname(path.resolve(__dirname, '..', '..', '..', '..', pluginExpression)); const regexp = new RegExp('^' + path.basename(pluginExpression).replace('*', '.*')); this.log.debug('Loading %s from %s', pluginExpression, pluginDirectory); - const plugins = fsAsPromised.readdirSync(pluginDirectory) - .filter(pluginName => IGNORED_PACKAGES.indexOf(pluginName) === -1 && regexp.test(pluginName)) + const plugins = fsAsPromised + .readdirSync(pluginDirectory) + .filter(pluginName => !IGNORED_PACKAGES.includes(pluginName) && regexp.test(pluginName)) .map(pluginName => path.resolve(pluginDirectory, pluginName)); if (plugins.length === 0) { this.log.debug('Expression %s not resulted in plugins to load', pluginExpression); @@ -89,8 +90,7 @@ export class PluginLoader implements PluginResolver { } } catch (e) { if (e.code === 'MODULE_NOT_FOUND' && e.message.indexOf(name) !== -1) { - this.log.warn('Cannot find plugin "%s".\n Did you forget to install it ?\n' + - ' npm install %s --save-dev', name, name); + this.log.warn('Cannot find plugin "%s".\n Did you forget to install it ?\n' + ' npm install %s --save-dev', name, name); } else { this.log.warn('Error during loading "%s" plugin:\n %s', name, e.message); } @@ -107,7 +107,7 @@ export class PluginLoader implements PluginResolver { } private isPluginModule(module: unknown): module is PluginModule { - const pluginModule = (module as PluginModule); + const pluginModule = module as PluginModule; return pluginModule && pluginModule.strykerPlugins && Array.isArray(pluginModule.strykerPlugins); } } diff --git a/packages/core/src/di/buildChildProcessInjector.ts b/packages/core/src/di/buildChildProcessInjector.ts index 0ed504e973..e35ba7befe 100644 --- a/packages/core/src/di/buildChildProcessInjector.ts +++ b/packages/core/src/di/buildChildProcessInjector.ts @@ -14,7 +14,7 @@ export function buildChildProcessInjector(options: StrykerOptions): Injector { +function pluginDescriptorsFactory(options: StrykerOptions): readonly string[] { return options.plugins; } pluginDescriptorsFactory.inject = tokens(commonTokens.options); diff --git a/packages/core/src/di/buildMainInjector.ts b/packages/core/src/di/buildMainInjector.ts index 115dd1eb6d..1d040c4a1b 100644 --- a/packages/core/src/di/buildMainInjector.ts +++ b/packages/core/src/di/buildMainInjector.ts @@ -1,5 +1,5 @@ -import { Config } from '@stryker-mutator/api/config'; import { StrykerOptions } from '@stryker-mutator/api/core'; +import { Config } from '@stryker-mutator/api/config'; import { commonTokens, Injector, OptionsContext, PluginKind, Scope, tokens } from '@stryker-mutator/api/plugin'; import { Reporter } from '@stryker-mutator/api/report'; import { TestFramework } from '@stryker-mutator/api/test_framework'; @@ -45,10 +45,8 @@ export function buildMainInjector(cliOptions: Partial): Injector .provideClass(coreTokens.timer, Timer); } -function pluginDescriptorsFactory(config: Config): ReadonlyArray { - config.plugins.push( - require.resolve('../reporters') - ); +function pluginDescriptorsFactory(config: Config): readonly string[] { + config.plugins.push(require.resolve('../reporters')); return config.plugins; } pluginDescriptorsFactory.inject = tokens(coreTokens.configReadFromConfigFile); diff --git a/packages/core/src/di/factoryMethods.ts b/packages/core/src/di/factoryMethods.ts index 7a347d8d71..fe022eccec 100644 --- a/packages/core/src/di/factoryMethods.ts +++ b/packages/core/src/di/factoryMethods.ts @@ -7,14 +7,18 @@ import { ConfigEditorApplier } from '../config'; import TestFrameworkOrchestrator from '../TestFrameworkOrchestrator'; import { freezeRecursively } from '../utils/objectUtils'; -export function pluginResolverFactory(injector: Injector<{ [commonTokens.logger]: Logger, [coreTokens.pluginDescriptors]: ReadonlyArray }>): PluginResolver { +export function pluginResolverFactory( + injector: Injector<{ [commonTokens.logger]: Logger; [coreTokens.pluginDescriptors]: readonly string[] }> +): PluginResolver { const pluginLoader = injector.injectClass(PluginLoader); pluginLoader.load(); return pluginLoader; } pluginResolverFactory.inject = tokens(commonTokens.injector); -export function testFrameworkFactory(injector: Injector }>) { +export function testFrameworkFactory( + injector: Injector }> +) { return injector.injectClass(TestFrameworkOrchestrator).determineTestFramework(); } testFrameworkFactory.inject = tokens(commonTokens.injector); @@ -28,4 +32,7 @@ export function optionsFactory(config: Config, configEditorApplier: ConfigEditor configEditorApplier.edit(config); return freezeRecursively(config); } -optionsFactory.inject = tokens<[typeof coreTokens.configReadFromConfigFile, typeof coreTokens.configEditorApplier]>(coreTokens.configReadFromConfigFile, coreTokens.configEditorApplier); +optionsFactory.inject = tokens<[typeof coreTokens.configReadFromConfigFile, typeof coreTokens.configEditorApplier]>( + coreTokens.configReadFromConfigFile, + coreTokens.configEditorApplier +); diff --git a/packages/core/src/globals.ts b/packages/core/src/globals.ts index f8a2e714dd..a1f763860b 100644 --- a/packages/core/src/globals.ts +++ b/packages/core/src/globals.ts @@ -1,11 +1,11 @@ /** * These are fake dom type definitions that rxjs depends on */ -interface EventTarget { } -interface NodeList { } -interface HTMLCollection { } -interface XMLHttpRequest { } -interface Event { } -interface MessageEvent { } -interface CloseEvent { } -interface WebSocket { } +interface EventTarget {} +interface NodeList {} +interface HTMLCollection {} +interface XMLHttpRequest {} +interface Event {} +interface MessageEvent {} +interface CloseEvent {} +interface WebSocket {} diff --git a/packages/core/src/initializer/NpmClient.ts b/packages/core/src/initializer/NpmClient.ts index 43e57fb121..c7863053d3 100644 --- a/packages/core/src/initializer/NpmClient.ts +++ b/packages/core/src/initializer/NpmClient.ts @@ -8,7 +8,7 @@ import PromptOption from './PromptOption'; interface NpmSearchResult { total: number; - results: { package: PackageInfo }[]; + results: Array<{ package: PackageInfo }>; } interface NpmPackage { @@ -17,15 +17,17 @@ interface NpmPackage { } const getName = (packageName: string) => { - return packageName.replace('@stryker-mutator/', '') + return packageName + .replace('@stryker-mutator/', '') .replace('stryker-', '') .split('-')[0]; }; -const mapSearchResultToPromptOption = (searchResults: NpmSearchResult): PromptOption[] => searchResults.results.map(result => ({ - name: getName(result.package.name), - pkg: result.package -})); +const mapSearchResultToPromptOption = (searchResults: NpmSearchResult): PromptOption[] => + searchResults.results.map(result => ({ + name: getName(result.package.name), + pkg: result.package + })); const handleResult = (from: string) => (response: IRestResponse): T => { if (response.statusCode === 200 && response.result) { @@ -36,24 +38,18 @@ const handleResult = (from: string) => (response: IRestResponse): T => { }; export default class NpmClient { - public static inject = tokens(commonTokens.logger, initializerTokens.restClientNpmSearch, initializerTokens.restClientNpm); - constructor( - private readonly log: Logger, - private readonly searchClient: RestClient, - private readonly packageClient: RestClient) { - } + constructor(private readonly log: Logger, private readonly searchClient: RestClient, private readonly packageClient: RestClient) {} public getTestRunnerOptions(): Promise { - return this.search('/v2/search?q=keywords:@stryker-mutator/test-runner-plugin') - .then(mapSearchResultToPromptOption); + return this.search('/v2/search?q=keywords:@stryker-mutator/test-runner-plugin').then(mapSearchResultToPromptOption); } public getTestFrameworkOptions(testRunnerFilter: string | null): Promise { return this.search('/v2/search?q=keywords:@stryker-mutator/test-framework-plugin') .then(searchResult => { if (testRunnerFilter) { - searchResult.results = searchResult.results.filter(framework => framework.package.keywords.indexOf(testRunnerFilter) >= 0); + searchResult.results = searchResult.results.filter(framework => framework.package.keywords.includes(testRunnerFilter)); } return searchResult; }) @@ -61,23 +57,21 @@ export default class NpmClient { } public getMutatorOptions(): Promise { - return this.search('/v2/search?q=keywords:@stryker-mutator/mutator-plugin') - .then(mapSearchResultToPromptOption); + return this.search('/v2/search?q=keywords:@stryker-mutator/mutator-plugin').then(mapSearchResultToPromptOption); } public getTranspilerOptions(): Promise { - return this.search('/v2/search?q=keywords:@stryker-mutator/transpiler-plugin') - .then(mapSearchResultToPromptOption); + return this.search('/v2/search?q=keywords:@stryker-mutator/transpiler-plugin').then(mapSearchResultToPromptOption); } public getTestReporterOptions(): Promise { - return this.search(`/v2/search?q=keywords:@stryker-mutator/reporter-plugin`) - .then(mapSearchResultToPromptOption); + return this.search('/v2/search?q=keywords:@stryker-mutator/reporter-plugin').then(mapSearchResultToPromptOption); } public getAdditionalConfig(pkg: PackageInfo): Promise { const path = `/${pkg.name}@${pkg.version}/package.json`; - return this.packageClient.get(path) + return this.packageClient + .get(path) .then(handleResult(path)) .then(pkg => pkg.initStrykerConfig || {}) .catch(err => { @@ -88,7 +82,8 @@ export default class NpmClient { private search(path: string): Promise { this.log.debug(`Searching: ${path}`); - return this.searchClient.get(path) + return this.searchClient + .get(path) .then(handleResult(path)) .catch(err => { this.log.error(`Unable to reach npms.io (for query ${path}). Please check your internet connection.`, errorToString(err)); diff --git a/packages/core/src/initializer/StrykerConfigWriter.ts b/packages/core/src/initializer/StrykerConfigWriter.ts index aab514741c..a39b1404c1 100644 --- a/packages/core/src/initializer/StrykerConfigWriter.ts +++ b/packages/core/src/initializer/StrykerConfigWriter.ts @@ -10,17 +10,12 @@ import PromptOption from './PromptOption'; const STRYKER_CONFIG_FILE = 'stryker.conf.js'; export default class StrykerConfigWriter { - public static inject = tokens(commonTokens.logger, initializerTokens.out); - constructor( - private readonly log: Logger, - private readonly out: typeof console.log) { - } + constructor(private readonly log: Logger, private readonly out: typeof console.log) {} public guardForExistingConfig() { if (fsAsPromised.existsSync(STRYKER_CONFIG_FILE)) { - const msg = - 'Stryker config file "stryker.conf.js" already exists in the current directory. Please remove it and try again.'; + const msg = 'Stryker config file "stryker.conf.js" already exists in the current directory. Please remove it and try again.'; this.log.error(msg); throw new Error(msg); } @@ -37,7 +32,8 @@ export default class StrykerConfigWriter { selectedTranspilers: null | PromptOption[], selectedReporters: PromptOption[], selectedPackageManager: PromptOption, - additionalPiecesOfConfig: Partial[]): Promise { + additionalPiecesOfConfig: Array> + ): Promise { const configObject: Partial = { mutator: selectedMutator ? selectedMutator.name : '', packageManager: selectedPackageManager.name, @@ -56,8 +52,11 @@ export default class StrykerConfigWriter { * @function */ public async writePreset(presetConfig: PresetConfiguration) { - return this.writeStrykerConfigRaw(presetConfig.config, `// This config was generated using a preset. - // Please see the handbook for more information: ${presetConfig.handbookUrl}`); + return this.writeStrykerConfigRaw( + presetConfig.config, + `// This config was generated using a preset. + // Please see the handbook for more information: ${presetConfig.handbookUrl}` + ); } private configureTestFramework(configObject: Partial, selectedTestFramework: null | PromptOption) { diff --git a/packages/core/src/initializer/StrykerInitializer.ts b/packages/core/src/initializer/StrykerInitializer.ts index 670c20db89..662320e32f 100644 --- a/packages/core/src/initializer/StrykerInitializer.ts +++ b/packages/core/src/initializer/StrykerInitializer.ts @@ -13,24 +13,26 @@ import { StrykerInquirer } from './StrykerInquirer'; const enum PackageManager { Npm = 'npm', - Yarn = 'yarn', + Yarn = 'yarn' } export default class StrykerInitializer { - public static inject = tokens( commonTokens.logger, initializerTokens.out, initializerTokens.npmClient, initializerTokens.strykerPresets, initializerTokens.configWriter, - initializerTokens.inquirer); - constructor(private readonly log: Logger, - private readonly out: typeof console.log, - private readonly client: NpmClient, - private readonly strykerPresets: Preset[], - private readonly configWriter: StrykerConfigWriter, - private readonly inquirer: StrykerInquirer) { } + initializerTokens.inquirer + ); + constructor( + private readonly log: Logger, + private readonly out: typeof console.log, + private readonly client: NpmClient, + private readonly strykerPresets: Preset[], + private readonly configWriter: StrykerConfigWriter, + private readonly inquirer: StrykerInquirer + ) {} /** * Runs the initializer will prompt the user for questions about his setup. After that, install plugins and configure Stryker. @@ -42,12 +44,11 @@ export default class StrykerInitializer { const selectedPreset = await this.selectPreset(); if (selectedPreset) { await this.initiatePreset(this.configWriter, selectedPreset); - } - else { + } else { await this.initiateCustom(this.configWriter); } this.out('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); - this.out('Let\'s kill some mutants with this command: `stryker run`'); + this.out("Let's kill some mutants with this command: `stryker run`"); this.out('Note: Stryker will use `.stryker-temp` as location for temporary files. Be sure to add it to your ignored files in source control.'); } @@ -85,24 +86,24 @@ export default class StrykerInitializer { private async initiateCustom(configWriter: StrykerConfigWriter) { const selectedTestRunner = await this.selectTestRunner(); - const selectedTestFramework = selectedTestRunner && !CommandTestRunner.is(selectedTestRunner.name) - ? await this.selectTestFramework(selectedTestRunner) : null; + const selectedTestFramework = + selectedTestRunner && !CommandTestRunner.is(selectedTestRunner.name) ? await this.selectTestFramework(selectedTestRunner) : null; const selectedMutator = await this.selectMutator(); const selectedTranspilers = await this.selectTranspilers(); const selectedReporters = await this.selectReporters(); const selectedPackageManager = await this.selectPackageManager(); const npmDependencies = this.getSelectedNpmDependencies( - [selectedTestRunner, selectedTestFramework, selectedMutator] - .concat(selectedTranspilers) - .concat(selectedReporters) + [selectedTestRunner, selectedTestFramework, selectedMutator].concat(selectedTranspilers).concat(selectedReporters) ); - await configWriter.write(selectedTestRunner, + await configWriter.write( + selectedTestRunner, selectedTestFramework, selectedMutator, selectedTranspilers, selectedReporters, selectedPackageManager, - await this.fetchAdditionalConfig(npmDependencies)); + await this.fetchAdditionalConfig(npmDependencies) + ); this.installNpmDependencies(npmDependencies.map(pkg => pkg.name), selectedPackageManager); } @@ -120,13 +121,16 @@ export default class StrykerInitializer { private async selectReporters(): Promise { let reporterOptions: PromptOption[]; reporterOptions = await this.client.getTestReporterOptions(); - reporterOptions.push({ - name: 'clear-text', - pkg: null - }, { + reporterOptions.push( + { + name: 'clear-text', + pkg: null + }, + { name: 'progress', pkg: null - }, { + }, + { name: 'dashboard', pkg: null } @@ -190,9 +194,8 @@ export default class StrykerInitializer { ]); } - private getSelectedNpmDependencies(selectedOptions: (PromptOption | null)[]): PackageInfo[] { - return filterEmpty(filterEmpty(selectedOptions) - .map(option => option.pkg)); + private getSelectedNpmDependencies(selectedOptions: Array): PackageInfo[] { + return filterEmpty(filterEmpty(selectedOptions).map(option => option.pkg)); } /** @@ -206,9 +209,7 @@ export default class StrykerInitializer { const dependencyArg = dependencies.join(' '); this.out('Installing NPM dependencies...'); - const cmd = selectedOption.name === PackageManager.Npm - ? `npm i --save-dev ${dependencyArg}` - : `yarn add ${dependencyArg} --dev`; + const cmd = selectedOption.name === PackageManager.Npm ? `npm i --save-dev ${dependencyArg}` : `yarn add ${dependencyArg} --dev`; this.out(cmd); try { child.execSync(cmd, { stdio: [0, 1, 2] }); @@ -218,7 +219,6 @@ export default class StrykerInitializer { } private async fetchAdditionalConfig(dependencies: PackageInfo[]): Promise { - return filterEmpty(await Promise.all(dependencies - .map(dep => this.client.getAdditionalConfig(dep)))); + return filterEmpty(await Promise.all(dependencies.map(dep => this.client.getAdditionalConfig(dep)))); } } diff --git a/packages/core/src/initializer/StrykerInquirer.ts b/packages/core/src/initializer/StrykerInquirer.ts index a2dacb8369..78f2e0afc6 100644 --- a/packages/core/src/initializer/StrykerInquirer.ts +++ b/packages/core/src/initializer/StrykerInquirer.ts @@ -9,9 +9,8 @@ export interface PromptResult { } export class StrykerInquirer { - public async promptPresets(options: Preset[]): Promise { - const choices: inquirer.ChoiceType[] = options.map(_ => _.name); + const choices: Array> = options.map(_ => _.name); choices.push(new inquirer.Separator()); choices.push('None/other'); const answers = await inquirer.prompt<{ preset: string }>({ @@ -24,13 +23,14 @@ export class StrykerInquirer { } public async promptTestRunners(options: PromptOption[]): Promise { - const choices: inquirer.ChoiceType[] = options.map(_ => _.name); + const choices: Array> = options.map(_ => _.name); choices.push(new inquirer.Separator()); choices.push(CommandTestRunner.runnerName); const answers = await inquirer.prompt<{ testRunner: string }>({ choices, default: 'Mocha', - message: 'Which test runner do you want to use? If your test runner isn\'t listed here, you can choose "command" (it uses your `npm test` command, but will come with a big performance penalty)', + message: + 'Which test runner do you want to use? If your test runner isn\'t listed here, you can choose "command" (it uses your `npm test` command, but will come with a big performance penalty)', name: 'testRunner', type: 'list' }); diff --git a/packages/core/src/initializer/StrykerPresets.ts b/packages/core/src/initializer/StrykerPresets.ts index bafdcf8809..2beb7ba8fd 100644 --- a/packages/core/src/initializer/StrykerPresets.ts +++ b/packages/core/src/initializer/StrykerPresets.ts @@ -4,5 +4,5 @@ import { ReactPreset } from './presets/ReactPreset'; import { VueJsPreset } from './presets/VueJsPreset'; // Add new presets here -const strykerPresets: Preset[] = [ new AngularPreset(), new ReactPreset(), new VueJsPreset() ]; +const strykerPresets: Preset[] = [new AngularPreset(), new ReactPreset(), new VueJsPreset()]; export default strykerPresets; diff --git a/packages/core/src/initializer/index.ts b/packages/core/src/initializer/index.ts index 2a347ce58c..59a7eb8897 100644 --- a/packages/core/src/initializer/index.ts +++ b/packages/core/src/initializer/index.ts @@ -27,6 +27,4 @@ export function initializerFactory(): StrykerInitializer { .injectClass(StrykerInitializer); } -export { - initializerTokens -}; +export { initializerTokens }; diff --git a/packages/core/src/initializer/initializerTokens.ts b/packages/core/src/initializer/initializerTokens.ts index bf66569e40..e6efd7a714 100644 --- a/packages/core/src/initializer/initializerTokens.ts +++ b/packages/core/src/initializer/initializerTokens.ts @@ -1,4 +1,3 @@ - export const restClientNpmSearch = 'restClientNpmSearch'; export const restClientNpm = 'restClientNpm'; export const npmClient = 'npmClient'; diff --git a/packages/core/src/initializer/presets/AngularPreset.ts b/packages/core/src/initializer/presets/AngularPreset.ts index befe099202..bc7c006c04 100644 --- a/packages/core/src/initializer/presets/AngularPreset.ts +++ b/packages/core/src/initializer/presets/AngularPreset.ts @@ -30,7 +30,9 @@ export class AngularPreset implements Preset { } }, reporters: ['progress', 'clear-text', 'html'], - maxConcurrentTestRunners: ${Math.floor(os.cpus().length / 2)}, // Recommended to use about half of your available cores when running stryker with angular. + maxConcurrentTestRunners: ${Math.floor( + os.cpus().length / 2 + )}, // Recommended to use about half of your available cores when running stryker with angular. coverageAnalysis: 'off' }`; public async createConfig(): Promise { diff --git a/packages/core/src/initializer/presets/ReactPreset.ts b/packages/core/src/initializer/presets/ReactPreset.ts index f66ce6ce29..9dafd03ab9 100644 --- a/packages/core/src/initializer/presets/ReactPreset.ts +++ b/packages/core/src/initializer/presets/ReactPreset.ts @@ -10,11 +10,7 @@ const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/ma */ export class ReactPreset implements Preset { public readonly name = 'react'; - private readonly generalDependencies = [ - '@stryker-mutator/core', - '@stryker-mutator/jest-runner', - '@stryker-mutator/html-reporter' - ]; + private readonly generalDependencies = ['@stryker-mutator/core', '@stryker-mutator/jest-runner', '@stryker-mutator/html-reporter']; private readonly sharedConfig = `testRunner: 'jest', reporters: ['progress', 'clear-text', 'html'], @@ -39,7 +35,7 @@ export class ReactPreset implements Preset { }`; public async createConfig(): Promise { - const choices: inquirer.ChoiceType[] = ['JSX', 'TSX']; + const choices: Array> = ['JSX', 'TSX']; const answers = await inquirer.prompt<{ choice: string }>({ choices, message: 'Is your project a JSX project or a TSX project?', diff --git a/packages/core/src/initializer/presets/VueJsPreset.ts b/packages/core/src/initializer/presets/VueJsPreset.ts index 316228f126..06b083cf31 100644 --- a/packages/core/src/initializer/presets/VueJsPreset.ts +++ b/packages/core/src/initializer/presets/VueJsPreset.ts @@ -10,11 +10,7 @@ const handbookUrl = 'https://github.com/stryker-mutator/stryker-handbook/blob/ma */ export class VueJsPreset implements Preset { public readonly name = 'vueJs'; - private readonly generalDependencies = [ - '@stryker-mutator/core', - '@stryker-mutator/vue-mutator', - '@stryker-mutator/html-reporter' - ]; + private readonly generalDependencies = ['@stryker-mutator/core', '@stryker-mutator/vue-mutator', '@stryker-mutator/html-reporter']; private readonly jestDependency = '@stryker-mutator/jest-runner'; private readonly jestConf = `{ @@ -44,14 +40,14 @@ export class VueJsPreset implements Preset { }`; public async createConfig(): Promise { - const testRunnerChoices: inquirer.ChoiceType[] = ['karma', 'jest']; + const testRunnerChoices: Array> = ['karma', 'jest']; const testRunnerAnswers = await inquirer.prompt<{ testRunner: string }>({ choices: testRunnerChoices, message: 'Which test runner do you want to use?', name: 'testRunner', type: 'list' }); - const scriptChoices: inquirer.ChoiceType[] = ['typescript', 'javascript']; + const scriptChoices: Array> = ['typescript', 'javascript']; const scriptAnswers = await inquirer.prompt<{ script: string }>({ choices: scriptChoices, message: 'Which language does your project use?', diff --git a/packages/core/src/input/InputFileCollection.ts b/packages/core/src/input/InputFileCollection.ts index 05299a5f88..0d71669d44 100644 --- a/packages/core/src/input/InputFileCollection.ts +++ b/packages/core/src/input/InputFileCollection.ts @@ -4,25 +4,29 @@ import { normalizeWhitespaces } from '@stryker-mutator/util'; import os = require('os'); export default class InputFileCollection { - public readonly files: ReadonlyArray; - public readonly filesToMutate: ReadonlyArray; + public readonly files: readonly File[]; + public readonly filesToMutate: readonly File[]; - constructor(files: ReadonlyArray, mutateGlobResult: ReadonlyArray) { + constructor(files: readonly File[], mutateGlobResult: readonly string[]) { this.files = files; this.filesToMutate = files.filter(file => mutateGlobResult.some(name => name === file.name)); } public logFiles(log: Logger) { if (!this.files.length) { - 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\`).`); + 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(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).`)); + 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/core/src/input/InputFileResolver.ts b/packages/core/src/input/InputFileResolver.ts index ab615363c9..ed5fc5557f 100644 --- a/packages/core/src/input/InputFileResolver.ts +++ b/packages/core/src/input/InputFileResolver.ts @@ -3,10 +3,7 @@ import { File, StrykerOptions } from '@stryker-mutator/api/core'; import { Logger } from '@stryker-mutator/api/logging'; import { commonTokens, tokens } from '@stryker-mutator/api/plugin'; import { SourceFile } from '@stryker-mutator/api/report'; -import { childProcessAsPromised } from '@stryker-mutator/util'; -import { StrykerError } from '@stryker-mutator/util'; -import { fsAsPromised, isErrnoException } from '@stryker-mutator/util'; -import { normalizeWhitespaces } from '@stryker-mutator/util'; +import { childProcessAsPromised, fsAsPromised, isErrnoException, normalizeWhitespaces, StrykerError } from '@stryker-mutator/util'; import * as path from 'path'; import { coreTokens } from '../di'; import StrictReporter from '../reporters/StrictReporter'; @@ -24,16 +21,12 @@ function toReportSourceFile(file: File): SourceFile { const IGNORE_PATTERN_CHARACTER = '!'; export default class InputFileResolver { - private readonly mutatePatterns: ReadonlyArray; - private readonly filePatterns: ReadonlyArray | undefined; + private readonly mutatePatterns: readonly string[]; + private readonly filePatterns: readonly string[] | undefined; private readonly tempDirName: string; public static inject = tokens(commonTokens.logger, commonTokens.options, coreTokens.reporter); - constructor( - private readonly log: Logger, - { mutate, files, tempDirName }: StrykerOptions, - private readonly reporter: StrictReporter - ) { + constructor(private readonly log: Logger, { mutate, files, tempDirName }: StrykerOptions, private readonly reporter: StrictReporter) { this.tempDirName = tempDirName; this.mutatePatterns = mutate || []; if (files) { @@ -42,10 +35,7 @@ export default class InputFileResolver { } public async resolve(): Promise { - const [inputFileNames, mutateFiles] = await Promise.all([ - this.resolveInputFiles(), - this.resolveMutateFiles() - ]); + const [inputFileNames, mutateFiles] = await Promise.all([this.resolveInputFiles(), this.resolveMutateFiles()]); const files: File[] = await this.readFiles(inputFileNames); const inputFileCollection = new InputFileCollection(files, mutateFiles); this.reportAllSourceFilesRead(files); @@ -64,7 +54,7 @@ export default class InputFileResolver { private resolveMutateFiles() { return this.expand(this.mutatePatterns, !shallowEquals(this.mutatePatterns, new Config().mutate)); - function shallowEquals(arr1: ReadonlyArray, arr2: ReadonlyArray): boolean { + function shallowEquals(arr1: readonly string[], arr2: readonly string[]): boolean { if (arr1.length !== arr2.length) { return false; } else { @@ -83,7 +73,7 @@ export default class InputFileResolver { * 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 { + private async expand(patterns: readonly string[], logAboutUselessPatterns = true): Promise { const fileSet = new Set(); for (const pattern of patterns) { if (pattern.startsWith(IGNORE_PATTERN_CHARACTER)) { @@ -100,30 +90,30 @@ export default class InputFileResolver { 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.` - ); + 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 /${this.tempDirName}/*`, - { maxBuffer: 10 * 1000 * 1024 } - ); - const fileNames = stdout.toString() + const { stdout } = await childProcessAsPromised.exec(`git ls-files --others --exclude-standard --cached --exclude /${this.tempDirName}/*`, { + maxBuffer: 10 * 1000 * 1024 + }); + 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\` + 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`), + is located inside a git repository` + ), error ); } @@ -138,9 +128,7 @@ export default class InputFileResolver { } private readFiles(files: string[]): Promise { - return Promise.all(files.map(fileName => this.readFile(fileName))).then( - filterEmpty - ); + return Promise.all(files.map(fileName => this.readFile(fileName))).then(filterEmpty); } private readFile(fileName: string): Promise { @@ -152,10 +140,7 @@ export default class InputFileResolver { return file; }) .catch(error => { - if ( - (isErrnoException(error) && error.code === 'ENOENT') || - error.code === 'EISDIR' - ) { + 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 diff --git a/packages/core/src/logging/LogConfigurator.ts b/packages/core/src/logging/LogConfigurator.ts index 2cd697eff7..ab71aab648 100644 --- a/packages/core/src/logging/LogConfigurator.ts +++ b/packages/core/src/logging/LogConfigurator.ts @@ -14,7 +14,7 @@ const enum AppenderName { Server = 'server' } -const layouts: { color: log4js.PatternLayout, noColor: log4js.PatternLayout } = { +const layouts: { color: log4js.PatternLayout; noColor: log4js.PatternLayout } = { color: { pattern: '%[%r (%z) %p %c%] %m', type: 'pattern' @@ -31,9 +31,7 @@ interface AppendersConfiguration { const LOG_FILE_NAME = 'stryker.log'; export default class LogConfigurator { - private static createMainProcessAppenders(consoleLogLevel: LogLevel, fileLogLevel: LogLevel, allowConsoleColors: boolean): AppendersConfiguration { - // Add the custom "multiAppender": https://log4js-node.github.io/log4js-node/appenders.html#other-appenders const multiAppender = { type: require.resolve('./MultiAppender'), appenders: [AppenderName.FilteredConsoleLevel] }; @@ -42,9 +40,9 @@ export default class LogConfigurator { let allAppenders: AppendersConfiguration = { [AppenderName.Console]: { type: 'stdout', layout: consoleLayout }, // Exclude messages like: "ERROR log4js A worker log process hung up unexpectedly" #1245 - [AppenderName.FilteredConsoleCategory]: {type: 'categoryFilter', appender: AppenderName.Console, exclude: 'log4js' }, + [AppenderName.FilteredConsoleCategory]: { type: 'categoryFilter', appender: AppenderName.Console, exclude: 'log4js' }, [AppenderName.FilteredConsoleLevel]: { type: 'logLevelFilter', appender: AppenderName.FilteredConsoleCategory, level: consoleLogLevel }, - [AppenderName.All]: multiAppender, + [AppenderName.All]: multiAppender }; // only add file if it is needed. Otherwise log4js will create the file directly, pretty annoying. @@ -67,7 +65,8 @@ export default class LogConfigurator { appenders, categories: { default: { - appenders: [AppenderName.All], level: defaultLogLevel + appenders: [AppenderName.All], + level: defaultLogLevel } } }; @@ -78,7 +77,11 @@ export default class LogConfigurator { * @param consoleLogLevel The log level to configure for the console * @param fileLogLevel The log level to configure for the "stryker.log" file */ - public static configureMainProcess(consoleLogLevel: LogLevel = LogLevel.Information, fileLogLevel: LogLevel = LogLevel.Off, allowConsoleColors: boolean = true) { + public static configureMainProcess( + consoleLogLevel: LogLevel = LogLevel.Information, + fileLogLevel: LogLevel = LogLevel.Off, + allowConsoleColors: boolean = true + ) { const appenders = this.createMainProcessAppenders(consoleLogLevel, fileLogLevel, allowConsoleColors); log4js.configure(this.createLog4jsConfig(minLevel(consoleLogLevel, fileLogLevel), appenders)); } @@ -92,7 +95,11 @@ export default class LogConfigurator { * @param fileLogLevel the file log level * @returns the context */ - public static async configureLoggingServer(consoleLogLevel: LogLevel, fileLogLevel: LogLevel, allowConsoleColors: boolean): Promise { + public static async configureLoggingServer( + consoleLogLevel: LogLevel, + fileLogLevel: LogLevel, + allowConsoleColors: boolean + ): Promise { const loggerPort = await getFreePort(); // Include the appenders for the main Stryker process, as log4js has only one single `configure` method. diff --git a/packages/core/src/logging/MultiAppender.ts b/packages/core/src/logging/MultiAppender.ts index 45b61c9b3e..3bd5b7fd07 100644 --- a/packages/core/src/logging/MultiAppender.ts +++ b/packages/core/src/logging/MultiAppender.ts @@ -3,8 +3,7 @@ import { LoggingEvent } from 'log4js'; export type RuntimeAppender = (loggingEvent: LoggingEvent) => void; export class MultiAppender { - - constructor(private readonly appenders: RuntimeAppender[]) { } + constructor(private readonly appenders: RuntimeAppender[]) {} public append(loggingEvent: LoggingEvent) { this.appenders.forEach(appender => appender(loggingEvent)); @@ -19,7 +18,7 @@ export class MultiAppender { * @param _ The layouts provided by log4js * @param findAppender A method to locate other appenders */ -export function configure(config: { appenders: string[] }, _: any, findAppender: (name: string) => RuntimeAppender ): RuntimeAppender { +export function configure(config: { appenders: string[] }, _: any, findAppender: (name: string) => RuntimeAppender): RuntimeAppender { const multiAppender = new MultiAppender(config.appenders.map(name => findAppender(name))); return multiAppender.append.bind(multiAppender); } diff --git a/packages/core/src/mutants/MutantTestMatcher.ts b/packages/core/src/mutants/MutantTestMatcher.ts index cbbe30ca3a..f0564fa4be 100644 --- a/packages/core/src/mutants/MutantTestMatcher.ts +++ b/packages/core/src/mutants/MutantTestMatcher.ts @@ -30,15 +30,14 @@ interface StatementIndex { } export class MutantTestMatcher { - public static inject = tokens(commonTokens.logger, commonTokens.options, coreTokens.reporter, coreTokens.inputFiles, coreTokens.initialRunResult); constructor( private readonly log: Logger, private readonly options: StrykerOptions, private readonly reporter: StrictReporter, private readonly input: InputFileCollection, - private readonly initialRunResult: InitialTestRunResult) { - } + private readonly initialRunResult: InitialTestRunResult + ) {} private get baseline(): CoverageCollection | null { if (this.isCoveragePerTestResult(this.initialRunResult.runResult.coverage)) { @@ -48,14 +47,16 @@ export class MutantTestMatcher { } } - public async matchWithMutants(mutants: ReadonlyArray): Promise> { - + public async matchWithMutants(mutants: readonly Mutant[]): Promise { const testableMutants = this.createTestableMutants(mutants); if (this.options.coverageAnalysis === 'off') { testableMutants.forEach(mutant => mutant.selectAllTests(this.initialRunResult.runResult, TestSelectionResult.Success)); } else if (!this.initialRunResult.runResult.coverage) { - this.log.warn('No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', this.options.coverageAnalysis); + this.log.warn( + 'No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', + this.options.coverageAnalysis + ); testableMutants.forEach(mutant => mutant.selectAllTests(this.initialRunResult.runResult, TestSelectionResult.FailedButAlreadyReported)); } else { await Promise.all(testableMutants.map(testableMutant => this.enrichWithCoveredTests(testableMutant))); @@ -117,17 +118,23 @@ export class MutantTestMatcher { } } - private createTestableMutants(mutants: ReadonlyArray): ReadonlyArray { + private createTestableMutants(mutants: readonly Mutant[]): readonly TestableMutant[] { const sourceFiles = this.input.filesToMutate.map(file => new SourceFile(file)); - return filterEmpty(mutants.map((mutant, index) => { - const sourceFile = sourceFiles.find(file => file.name === mutant.fileName); - if (sourceFile) { - return new TestableMutant(index.toString(), mutant, sourceFile); - } else { - this.log.error(`Mutant "${mutant.mutatorName}${mutant.replacement}" is corrupt, because cannot find a text file with name ${mutant.fileName}. List of source files: \n\t${sourceFiles.map(s => s.name).join('\n\t')}`); - return null; - } - })); + return filterEmpty( + mutants.map((mutant, index) => { + const sourceFile = sourceFiles.find(file => file.name === mutant.fileName); + if (sourceFile) { + return new TestableMutant(index.toString(), mutant, sourceFile); + } else { + this.log.error( + `Mutant "${mutant.mutatorName}${mutant.replacement}" is corrupt, because cannot find a text file with name ${ + mutant.fileName + }. List of source files: \n\t${sourceFiles.map(s => s.name).join('\n\t')}` + ); + return null; + } + }) + ); } /** @@ -142,7 +149,7 @@ export class MutantTestMatcher { mutatorName: testableMutant.mutant.mutatorName, replacement: testableMutant.mutant.replacement, scopedTestIds: testableMutant.selectedTests.map(testSelection => testSelection.id), - timeSpentScopedTests: testableMutant.timeSpentScopedTests, + timeSpentScopedTests: testableMutant.timeSpentScopedTests }); return Object.freeze(matchedMutant); } @@ -174,7 +181,7 @@ export class MutantTestMatcher { * @returns The index of the smallest statement surrounding the location, or null if not found. */ private findMatchingStatementInMap(needle: LocationHelper, haystack: StatementMap): string | null { - let smallestStatement: { index: string | null, location: LocationHelper } = { + let smallestStatement: { index: string | null; location: LocationHelper } = { index: null, location: LocationHelper.MAX_VALUE }; diff --git a/packages/core/src/mutants/MutatorFacade.ts b/packages/core/src/mutants/MutatorFacade.ts index 92e2062eda..110648ba69 100644 --- a/packages/core/src/mutants/MutatorFacade.ts +++ b/packages/core/src/mutants/MutatorFacade.ts @@ -5,28 +5,26 @@ import { commonTokens, PluginKind, tokens } from '@stryker-mutator/api/plugin'; import { coreTokens, PluginCreator } from '../di'; export class MutatorFacade implements Mutator { - public static inject = tokens(commonTokens.options, coreTokens.pluginCreatorMutator, commonTokens.logger); constructor( private readonly options: StrykerOptions, private readonly pluginCreator: PluginCreator, - private readonly log: Logger) { - } + private readonly log: Logger + ) {} - public mutate(inputFiles: ReadonlyArray): ReadonlyArray { - const allMutants = this.pluginCreator.create(this.getMutatorName(this.options.mutator)) - .mutate(inputFiles); + public mutate(inputFiles: readonly File[]): readonly Mutant[] { + const allMutants = this.pluginCreator.create(this.getMutatorName(this.options.mutator)).mutate(inputFiles); const includedMutants = this.removeExcludedMutants(allMutants); this.logMutantCount(includedMutants.length, allMutants.length); return includedMutants; } - private removeExcludedMutants(mutants: ReadonlyArray): ReadonlyArray { + private removeExcludedMutants(mutants: readonly Mutant[]): readonly Mutant[] { if (typeof this.options.mutator === 'string') { return mutants; } else { - const mutatorDescriptor = this.options.mutator as MutatorDescriptor; - return mutants.filter(mutant => mutatorDescriptor.excludedMutations.indexOf(mutant.mutatorName) === -1); + const mutatorDescriptor = this.options.mutator; + return mutants.filter(mutant => !mutatorDescriptor.excludedMutations.includes(mutant.mutatorName)); } } @@ -43,7 +41,7 @@ export class MutatorFacade implements Mutator { if (includedMutantCount) { mutantCountMessage = `${includedMutantCount} Mutant(s) generated`; } else { - mutantCountMessage = `It\'s a mutant-free world, nothing to test.`; + mutantCountMessage = "It's a mutant-free world, nothing to test."; } const numberExcluded = totalMutantCount - includedMutantCount; if (numberExcluded) { diff --git a/packages/core/src/process/InitialTestExecutor.ts b/packages/core/src/process/InitialTestExecutor.ts index cb7e124248..6fd060ce72 100644 --- a/packages/core/src/process/InitialTestExecutor.ts +++ b/packages/core/src/process/InitialTestExecutor.ts @@ -44,7 +44,6 @@ interface Timing { } export default class InitialTestExecutor { - public static inject = tokens( commonTokens.options, commonTokens.logger, @@ -53,7 +52,8 @@ export default class InitialTestExecutor { coreTokens.timer, coreTokens.loggingContext, coreTokens.transpiler, - coreTokens.temporaryDirectory); + coreTokens.temporaryDirectory + ); constructor( private readonly options: StrykerOptions, @@ -63,10 +63,10 @@ export default class InitialTestExecutor { private readonly timer: Timer, private readonly loggingContext: LoggingClientContext, private readonly transpiler: Transpiler, - private readonly tempDir: TemporaryDirectory) { } + private readonly tempDir: TemporaryDirectory + ) {} public async run(): Promise { - this.log.info('Starting initial test run. This may take a while.'); // Before we can run the tests we transpile the input files. @@ -93,7 +93,7 @@ export default class InitialTestExecutor { }; } - private async runInSandbox(files: ReadonlyArray): Promise<{ runResult: RunResult, grossTimeMS: number }> { + private async runInSandbox(files: readonly File[]): Promise<{ runResult: RunResult; grossTimeMS: number }> { const sandbox = await Sandbox.create(this.options, 0, files, this.testFramework, 0, this.loggingContext, this.tempDir); this.timer.mark(INITIAL_TEST_RUN_MARKER); const runResult = await sandbox.run(INITIAL_RUN_TIMEOUT, this.getCollectCoverageHooksIfNeeded()); @@ -102,8 +102,10 @@ export default class InitialTestExecutor { return { runResult, grossTimeMS }; } - private async annotateForCodeCoverage(files: ReadonlyArray, sourceMapper: SourceMapper) - : Promise<{ instrumentedFiles: ReadonlyArray, coverageMaps: CoverageMapsByFile }> { + private async annotateForCodeCoverage( + files: readonly File[], + sourceMapper: SourceMapper + ): Promise<{ instrumentedFiles: readonly File[]; coverageMaps: CoverageMapsByFile }> { const filesToInstrument = this.inputFiles.filesToMutate.map(mutateFile => sourceMapper.transpiledFileNameFor(mutateFile.name)); const coverageInstrumenterTranspiler = new CoverageInstrumenterTranspiler(this.options, filesToInstrument); const instrumentedFiles = await coverageInstrumenterTranspiler.transpile(files); @@ -142,7 +144,7 @@ export default class InitialTestExecutor { * The overhead time is used to calculate exact timeout values during mutation testing. * See timeoutMS setting in README for more information on this calculation */ - private calculateTiming(grossTimeMS: number, tests: ReadonlyArray): Timing { + private calculateTiming(grossTimeMS: number, tests: readonly TestResult[]): Timing { const netTimeMS = tests.reduce((total, test) => total + test.timeSpentMs, 0); const overheadTimeMS = grossTimeMS - netTimeMS; return { @@ -155,16 +157,18 @@ export default class InitialTestExecutor { if (this.options.coverageAnalysis === 'perTest') { if (this.testFramework) { // Add piece of javascript to collect coverage per test results - this.log.debug(`Adding test hooks for coverageAnalysis "perTest".`); + this.log.debug('Adding test hooks for coverageAnalysis "perTest".'); return coveragePerTestHooks(this.testFramework); } else { - this.log.warn('Cannot measure coverage results per test, there is no testFramework and thus no way of executing code right before and after each test.'); + this.log.warn( + 'Cannot measure coverage results per test, there is no testFramework and thus no way of executing code right before and after each test.' + ); } } return undefined; } - private logTranspileResult(transpiledFiles: ReadonlyArray) { + private logTranspileResult(transpiledFiles: readonly File[]) { if (this.options.transpilers.length && this.log.isDebugEnabled()) { this.log.debug(`Transpiled files: ${JSON.stringify(transpiledFiles.map(f => `${f.name}`), null, 2)}`); } @@ -175,8 +179,13 @@ export default class InitialTestExecutor { } private logInitialTestRunSucceeded(tests: TestResult[], timing: Timing) { - this.log.info('Initial test run succeeded. Ran %s tests in %s (net %s ms, overhead %s ms).', - tests.length, this.timer.humanReadableElapsed(), timing.net, timing.overhead); + this.log.info( + 'Initial test run succeeded. Ran %s tests in %s (net %s ms, overhead %s ms).', + tests.length, + this.timer.humanReadableElapsed(), + timing.net, + timing.overhead + ); } private logFailedTestsInInitialRun(failedTests: TestResult[]): void { @@ -192,14 +201,14 @@ export default class InitialTestExecutor { private logErrorsInInitialRun(runResult: RunResult) { let message = 'One or more tests resulted in an error:'; if (runResult.errorMessages && runResult.errorMessages.length) { - runResult.errorMessages.forEach(error => message += `${EOL}\t${error}`); + runResult.errorMessages.forEach(error => (message += `${EOL}\t${error}`)); } this.log.error(message); } private logTimeoutInitialRun(runResult: RunResult) { let message = 'Initial test run timed out! Ran following tests before timeout:'; - runResult.tests.forEach(test => message += `${EOL}\t${test.name} (${TestStatus[test.status]})`); + runResult.tests.forEach(test => (message += `${EOL}\t${test.name} (${TestStatus[test.status]})`)); this.log.error(message); } } diff --git a/packages/core/src/process/MutationTestExecutor.ts b/packages/core/src/process/MutationTestExecutor.ts index 037eff85f8..696bfd2a88 100644 --- a/packages/core/src/process/MutationTestExecutor.ts +++ b/packages/core/src/process/MutationTestExecutor.ts @@ -8,34 +8,33 @@ import TestableMutant from '../TestableMutant'; import { MutantTranspileScheduler } from '../transpiler/MutantTranspileScheduler'; export class MutationTestExecutor { - public static inject = tokens( - coreTokens.reporter, - coreTokens.mutantTranspileScheduler, - coreTokens.sandboxPool); + public static inject = tokens(coreTokens.reporter, coreTokens.mutantTranspileScheduler, coreTokens.sandboxPool); constructor( private readonly reporter: StrictReporter, private readonly mutantTranspileScheduler: MutantTranspileScheduler, - private readonly sandboxPool: SandboxPool) { - } - - public async run(allMutants: ReadonlyArray): Promise { + private readonly sandboxPool: SandboxPool + ) {} - const results = await this.sandboxPool.runMutants(this.mutantTranspileScheduler.scheduleTranspileMutants(allMutants)).pipe( - tap(this.reportResult), - // Signal the mutant transpiler that there is another slot open for transpiling - tap(this.mutantTranspileScheduler.scheduleNext), - toArray(), - tap(this.reportAll) - ).toPromise(); + public async run(allMutants: readonly TestableMutant[]): Promise { + const results = await this.sandboxPool + .runMutants(this.mutantTranspileScheduler.scheduleTranspileMutants(allMutants)) + .pipe( + tap(this.reportResult), + // Signal the mutant transpiler that there is another slot open for transpiling + tap(this.mutantTranspileScheduler.scheduleNext), + toArray(), + tap(this.reportAll) + ) + .toPromise(); return results; } private readonly reportResult = (mutantResult: MutantResult): void => { this.reporter.onMutantTested(mutantResult); - } + }; private readonly reportAll = (mutantResults: MutantResult[]): void => { this.reporter.onAllMutantsTested(mutantResults); - } + }; } diff --git a/packages/core/src/reporters/BroadcastReporter.ts b/packages/core/src/reporters/BroadcastReporter.ts index c7759e14b4..6e80c7598a 100644 --- a/packages/core/src/reporters/BroadcastReporter.ts +++ b/packages/core/src/reporters/BroadcastReporter.ts @@ -8,11 +8,7 @@ import { PluginCreator } from '../di/PluginCreator'; import StrictReporter from './StrictReporter'; export default class BroadcastReporter implements StrictReporter { - - public static readonly inject = tokens( - commonTokens.options, - coreTokens.pluginCreatorReporter, - commonTokens.logger); + public static readonly inject = tokens(commonTokens.options, coreTokens.pluginCreatorReporter, commonTokens.logger); public readonly reporters: { [name: string]: Reporter; @@ -20,7 +16,8 @@ export default class BroadcastReporter implements StrictReporter { constructor( private readonly options: StrykerOptions, private readonly pluginCreator: PluginCreator, - private readonly log: Logger) { + private readonly log: Logger + ) { this.reporters = {}; this.options.reporters.forEach(reporterName => this.createReporter(reporterName)); this.logAboutReporters(); @@ -28,9 +25,7 @@ export default class BroadcastReporter implements StrictReporter { private createReporter(reporterName: string): void { if (reporterName === 'progress' && !process.stdout.isTTY) { - this.log.info( - 'Detected that current console does not support the "progress" reporter, downgrading to "progress-append-only" reporter' - ); + this.log.info('Detected that current console does not support the "progress" reporter, downgrading to "progress-append-only" reporter'); reporterName = 'progress-append-only'; } this.reporters[reporterName] = this.pluginCreator.create(reporterName); @@ -43,25 +38,29 @@ export default class BroadcastReporter implements StrictReporter { this.log.debug(`Broadcasting to reporters ${JSON.stringify(reporterNames)}`); } } else { - this.log.warn('No reporter configured. Please configure one or more reporters in the (for example: reporters: [\'progress\'])'); + this.log.warn("No reporter configured. Please configure one or more reporters in the (for example: reporters: ['progress'])"); } } private broadcast(methodName: keyof Reporter, eventArgs: any): Promise { - return Promise.all(Object.keys(this.reporters).map(async reporterName => { - const reporter = this.reporters[reporterName]; - if (typeof reporter[methodName] === 'function') { - const deprecatedMethodName = 'onScoreCalculated'; - if (methodName === deprecatedMethodName) { - this.log.warn(`DEPRECATED: The reporter '${reporterName}' uses '${deprecatedMethodName}' which is deprecated. Please use 'onMutationTestReportReady' and calculate the score as an alternative.`); - } - try { - await (reporter[methodName] as any)(eventArgs); - } catch (error) { - this.handleError(error, methodName, reporterName); + return Promise.all( + Object.keys(this.reporters).map(async reporterName => { + const reporter = this.reporters[reporterName]; + if (typeof reporter[methodName] === 'function') { + const deprecatedMethodName = 'onScoreCalculated'; + if (methodName === deprecatedMethodName) { + this.log.warn( + `DEPRECATED: The reporter '${reporterName}' uses '${deprecatedMethodName}' which is deprecated. Please use 'onMutationTestReportReady' and calculate the score as an alternative.` + ); + } + try { + await (reporter[methodName] as any)(eventArgs); + } catch (error) { + this.handleError(error, methodName, reporterName); + } } - } - })); + }) + ); } public onSourceFileRead(file: SourceFile): void { @@ -72,7 +71,7 @@ export default class BroadcastReporter implements StrictReporter { this.broadcast('onAllSourceFilesRead', files); } - public onAllMutantsMatchedWithTests(results: ReadonlyArray): void { + public onAllMutantsMatchedWithTests(results: readonly MatchedMutant[]): void { this.broadcast('onAllMutantsMatchedWithTests', results); } diff --git a/packages/core/src/reporters/ClearTextReporter.ts b/packages/core/src/reporters/ClearTextReporter.ts index 31c1f6526d..b2e3ef30a3 100644 --- a/packages/core/src/reporters/ClearTextReporter.ts +++ b/packages/core/src/reporters/ClearTextReporter.ts @@ -9,7 +9,6 @@ import { tokens } from 'typed-inject'; import ClearTextScoreTable from './ClearTextScoreTable'; export default class ClearTextReporter implements Reporter { - public static inject = tokens(commonTokens.logger, commonTokens.options); constructor(private readonly log: Logger, private readonly options: StrykerOptions) { this.configConsoleColor(); @@ -89,14 +88,10 @@ export default class ClearTextReporter implements Reporter { const clearTextReporterConfig = this.options.clearTextReporter; if (clearTextReporterConfig && clearTextReporterConfig.allowColor !== false) { - return sourceFilePath + ':' + position.line + ':' + position.column; + return `${sourceFilePath}:${position.line}:${position.column}`; } - return [ - chalk.cyan(sourceFilePath), - chalk.yellow(`${position.line}`), - chalk.yellow(`${position.column}`), - ].join(':'); + return [chalk.cyan(sourceFilePath), chalk.yellow(`${position.line}`), chalk.yellow(`${position.column}`)].join(':'); } private logExecutedTests(result: MutantResult, logImplementation: (input: string) => void) { diff --git a/packages/core/src/reporters/ClearTextScoreTable.ts b/packages/core/src/reporters/ClearTextScoreTable.ts index ad70e6f245..90e4b7eb43 100644 --- a/packages/core/src/reporters/ClearTextScoreTable.ts +++ b/packages/core/src/reporters/ClearTextScoreTable.ts @@ -83,7 +83,6 @@ class FileColumn extends Column { * Represents a clear text table for mutation score */ export default class ClearTextScoreTable { - private readonly columns: Column[]; constructor(private readonly metricsResult: MetricsResult, thresholds: MutationScoreThresholds) { @@ -111,20 +110,15 @@ export default class ClearTextScoreTable { } private drawValues(current = this.metricsResult, ancestorCount = 0): string[] { - return [this.drawRow(c => c.drawTableCell(current, ancestorCount))] - .concat(_.flatMap(current.childResults, child => this.drawValues(child, ancestorCount + 1))); + return [this.drawRow(c => c.drawTableCell(current, ancestorCount))].concat( + _.flatMap(current.childResults, child => this.drawValues(child, ancestorCount + 1)) + ); } /** * Returns a string with the score results drawn in a table. */ public draw() { - return [ - this.drawBorder(), - this.drawHeader(), - this.drawBorder(), - this.drawValues().join(os.EOL), - this.drawBorder() - ].join(os.EOL); + return [this.drawBorder(), this.drawHeader(), this.drawBorder(), this.drawValues().join(os.EOL), this.drawBorder()].join(os.EOL); } } diff --git a/packages/core/src/reporters/DotsReporter.ts b/packages/core/src/reporters/DotsReporter.ts index dadcb10b10..1d63079c13 100644 --- a/packages/core/src/reporters/DotsReporter.ts +++ b/packages/core/src/reporters/DotsReporter.ts @@ -3,7 +3,6 @@ import chalk from 'chalk'; import * as os from 'os'; export default class DotsReporter implements Reporter { - public onMutantTested(result: MutantResult) { let toLog: string; switch (result.status) { diff --git a/packages/core/src/reporters/EventRecorderReporter.ts b/packages/core/src/reporters/EventRecorderReporter.ts index 5fa9dd99e7..cb1241601d 100644 --- a/packages/core/src/reporters/EventRecorderReporter.ts +++ b/packages/core/src/reporters/EventRecorderReporter.ts @@ -12,7 +12,7 @@ const DEFAULT_BASE_FOLDER = 'reports/mutation/events'; export default class EventRecorderReporter implements StrictReporter { public static readonly inject = tokens(commonTokens.logger, commonTokens.options); - private readonly allWork: Promise[] = []; + private readonly allWork: Array> = []; private readonly createBaseFolderTask: Promise; private _baseFolder: string; private index = 0; @@ -27,7 +27,9 @@ export default class EventRecorderReporter implements StrictReporter { this._baseFolder = this.options.eventReporter.baseDir; this.log.debug(`Using configured output folder ${this._baseFolder}`); } else { - this.log.debug(`No base folder configuration found (using configuration: eventReporter: { baseDir: 'output/folder' }), using default ${DEFAULT_BASE_FOLDER}`); + this.log.debug( + `No base folder configuration found (using configuration: eventReporter: { baseDir: 'output/folder' }), using default ${DEFAULT_BASE_FOLDER}` + ); this._baseFolder = DEFAULT_BASE_FOLDER; } } @@ -62,7 +64,7 @@ export default class EventRecorderReporter implements StrictReporter { this.work('onAllSourceFilesRead', files); } - public onAllMutantsMatchedWithTests(results: ReadonlyArray): void { + public onAllMutantsMatchedWithTests(results: readonly MatchedMutant[]): void { this.work('onAllMutantsMatchedWithTests', results); } diff --git a/packages/core/src/reporters/MutationTestReportCalculator.ts b/packages/core/src/reporters/MutationTestReportCalculator.ts index abe5b5359f..f6c2637a74 100644 --- a/packages/core/src/reporters/MutationTestReportCalculator.ts +++ b/packages/core/src/reporters/MutationTestReportCalculator.ts @@ -8,7 +8,6 @@ import { coreTokens } from '../di'; import InputFileCollection from '../input/InputFileCollection'; export class MutationTestReportCalculator { - public static inject = tokens(coreTokens.reporter, commonTokens.options, coreTokens.inputFiles, commonTokens.logger); constructor( @@ -16,13 +15,13 @@ export class MutationTestReportCalculator { private readonly options: StrykerOptions, private readonly inputFiles: InputFileCollection, private readonly log: Logger - ) { } + ) {} - public report(results: ReadonlyArray) { + public report(results: readonly MutantResult[]) { this.reporter.onMutationTestReportReady(this.mutationTestReport(results)); } - private mutationTestReport(results: ReadonlyArray): mutationTestReportSchema.MutationTestResult { + private mutationTestReport(results: readonly MutantResult[]): mutationTestReportSchema.MutationTestResult { return { files: this.toFileResults(results), schemaVersion: '1.0', @@ -30,7 +29,7 @@ export class MutationTestReportCalculator { }; } - private toFileResults(results: ReadonlyArray): mutationTestReportSchema.FileResultDictionary { + private toFileResults(results: readonly MutantResult[]): mutationTestReportSchema.FileResultDictionary { const resultDictionary: mutationTestReportSchema.FileResultDictionary = Object.create(null); results.forEach(mutantResult => { const fileResult = resultDictionary[mutantResult.sourceFilePath]; @@ -45,8 +44,10 @@ export class MutationTestReportCalculator { source: sourceFile.textContent }; } else { - this.log.warn(normalizeWhitespaces(`File "${mutantResult.sourceFilePath}" not found - in input files, but did receive mutant result for it. This shouldn't happen`)); + this.log.warn( + normalizeWhitespaces(`File "${mutantResult.sourceFilePath}" not found + in input files, but did receive mutant result for it. This shouldn't happen`) + ); } } }); diff --git a/packages/core/src/reporters/ProgressAppendOnlyReporter.ts b/packages/core/src/reporters/ProgressAppendOnlyReporter.ts index 1801b8427d..1a10a1c7b1 100644 --- a/packages/core/src/reporters/ProgressAppendOnlyReporter.ts +++ b/packages/core/src/reporters/ProgressAppendOnlyReporter.ts @@ -5,7 +5,7 @@ import ProgressKeeper from './ProgressKeeper'; export default class ProgressAppendOnlyReporter extends ProgressKeeper { private intervalReference: NodeJS.Timer; - public onAllMutantsMatchedWithTests(matchedMutants: ReadonlyArray): void { + public onAllMutantsMatchedWithTests(matchedMutants: readonly MatchedMutant[]): void { super.onAllMutantsMatchedWithTests(matchedMutants); if (matchedMutants.length) { this.intervalReference = setInterval(() => this.render(), 10000); @@ -17,12 +17,14 @@ export default class ProgressAppendOnlyReporter extends ProgressKeeper { } private render() { - process.stdout.write(`Mutation testing ${this.getPercentDone()} (ETC ${this.getEtc()}) ` + - `${this.progress.tested}/${this.progress.total} tested (${this.progress.survived} survived)` + - os.EOL); + process.stdout.write( + `Mutation testing ${this.getPercentDone()} (ETC ${this.getEtc()}) ` + + `${this.progress.tested}/${this.progress.total} tested (${this.progress.survived} survived)` + + os.EOL + ); } private getPercentDone() { - return Math.floor(this.progress.tested / this.progress.total * 100) + '%'; + return `${Math.floor((this.progress.tested / this.progress.total) * 100)}%`; } } diff --git a/packages/core/src/reporters/ProgressKeeper.ts b/packages/core/src/reporters/ProgressKeeper.ts index 05b0866107..38ed36c227 100644 --- a/packages/core/src/reporters/ProgressKeeper.ts +++ b/packages/core/src/reporters/ProgressKeeper.ts @@ -7,12 +7,12 @@ abstract class ProgressKeeper implements Reporter { protected progress = { survived: 0, tested: 0, - total: 0, + total: 0 }; private mutantIdsWithoutCoverage: string[]; - public onAllMutantsMatchedWithTests(matchedMutants: ReadonlyArray): void { + public onAllMutantsMatchedWithTests(matchedMutants: readonly MatchedMutant[]): void { this.timer = new Timer(); this.mutantIdsWithoutCoverage = matchedMutants.filter(m => m.scopedTestIds.length === 0).map(m => m.id); this.progress.total = matchedMutants.length - this.mutantIdsWithoutCoverage.length; @@ -28,11 +28,11 @@ abstract class ProgressKeeper implements Reporter { } protected getEtc() { - const totalSecondsLeft = Math.floor(this.timer.elapsedSeconds() / this.progress.tested * (this.progress.total - this.progress.tested)); + const totalSecondsLeft = Math.floor((this.timer.elapsedSeconds() / this.progress.tested) * (this.progress.total - this.progress.tested)); if (isFinite(totalSecondsLeft) && totalSecondsLeft > 0) { const hours = Math.floor(totalSecondsLeft / 3600); - const minutes = Math.floor(totalSecondsLeft / 60 % 60); + const minutes = Math.floor((totalSecondsLeft / 60) % 60); const seconds = Math.floor(totalSecondsLeft % 60); return this.formatEtc(hours, minutes, seconds); diff --git a/packages/core/src/reporters/ProgressReporter.ts b/packages/core/src/reporters/ProgressReporter.ts index 182de4fbb4..cfa18ea07c 100644 --- a/packages/core/src/reporters/ProgressReporter.ts +++ b/packages/core/src/reporters/ProgressReporter.ts @@ -5,10 +5,9 @@ import ProgressKeeper from './ProgressKeeper'; export default class ProgressBarReporter extends ProgressKeeper { private progressBar: ProgressBar; - public onAllMutantsMatchedWithTests(matchedMutants: ReadonlyArray): void { + public onAllMutantsMatchedWithTests(matchedMutants: readonly MatchedMutant[]): void { super.onAllMutantsMatchedWithTests(matchedMutants); - const progressBarContent = - `Mutation testing [:bar] :percent (ETC :etc) :tested/:total tested (:survived survived)`; + const progressBarContent = 'Mutation testing [:bar] :percent (ETC :etc) :tested/:total tested (:survived survived)'; this.progressBar = new ProgressBar(progressBarContent, { complete: '=', diff --git a/packages/core/src/reporters/ci/CircleProvider.ts b/packages/core/src/reporters/ci/CircleProvider.ts index 75711bb136..ccc9e9d954 100644 --- a/packages/core/src/reporters/ci/CircleProvider.ts +++ b/packages/core/src/reporters/ci/CircleProvider.ts @@ -15,7 +15,7 @@ class CircleProvider implements CIProvider { } else { return '(unknown)'; } - } + }; } export default CircleProvider; diff --git a/packages/core/src/reporters/ci/Provider.ts b/packages/core/src/reporters/ci/Provider.ts index da3df02f50..8bbcccdef6 100644 --- a/packages/core/src/reporters/ci/Provider.ts +++ b/packages/core/src/reporters/ci/Provider.ts @@ -19,14 +19,13 @@ export interface CIProvider { * Return an approriate instance of CiProvider. * @returns An instance of CiProvider, or undefined if it appears Stryker is not running in a CI/CD environment. */ -export function determineCIProvider() { +export function determineCIProvider() { // By far the coolest env. variable from all those listed at // https://docs.travis-ci.com/user/environment-variables/#Default-Environment-Variables if (getEnvironmentVariable('HAS_JOSH_K_SEAL_OF_APPROVAL')) { return new TravisProvider(); } else if (getEnvironmentVariable('CIRCLECI')) { return new CircleProvider(); - } return undefined; diff --git a/packages/core/src/reporters/dashboard-reporter/DashboardReporter.ts b/packages/core/src/reporters/dashboard-reporter/DashboardReporter.ts index 499c7c4d9c..354812d3e6 100644 --- a/packages/core/src/reporters/dashboard-reporter/DashboardReporter.ts +++ b/packages/core/src/reporters/dashboard-reporter/DashboardReporter.ts @@ -8,14 +8,10 @@ import DashboardReporterClient from './DashboardReporterClient'; import { dashboardReporterTokens } from './tokens'; export default class DashboardReporter implements Reporter { - private readonly ciProvider = determineCIProvider(); public static readonly inject = tokens(commonTokens.logger, dashboardReporterTokens.dashboardReporterClient); - constructor( - private readonly log: Logger, - private readonly dashboardReporterClient: DashboardReporterClient - ) { } + constructor(private readonly log: Logger, private readonly dashboardReporterClient: DashboardReporterClient) {} private readEnvironmentVariable(name: string) { const environmentVariable = getEnvironmentVariable(name); diff --git a/packages/core/src/reporters/dashboard-reporter/DashboardReporterClient.ts b/packages/core/src/reporters/dashboard-reporter/DashboardReporterClient.ts index cfc34a8e1f..d10d930c15 100644 --- a/packages/core/src/reporters/dashboard-reporter/DashboardReporterClient.ts +++ b/packages/core/src/reporters/dashboard-reporter/DashboardReporterClient.ts @@ -14,21 +14,17 @@ export interface StrykerDashboardReport { const URL_STRYKER_DASHBOARD_REPORTER = 'https://dashboard.stryker-mutator.io/api/reports'; export default class DashboardReporterClient { - public static inject = tokens(commonTokens.logger, dashboardReporterTokens.httpClient); - constructor( - private readonly log: Logger, - private readonly dashboardReporterClient: HttpClient) { - } + constructor(private readonly log: Logger, private readonly dashboardReporterClient: HttpClient) {} public postStrykerDashboardReport(report: StrykerDashboardReport): Promise { this.log.info(`Posting report to ${URL_STRYKER_DASHBOARD_REPORTER}`); const reportString = JSON.stringify(report); this.log.debug('Posting data %s', reportString); - return this.dashboardReporterClient.post(URL_STRYKER_DASHBOARD_REPORTER, reportString, - { - ['Content-Type']: 'application/json' - }) + return this.dashboardReporterClient + .post(URL_STRYKER_DASHBOARD_REPORTER, reportString, { + ['Content-Type']: 'application/json' + }) .then(body => { const statusCode = body.message.statusCode; if (statusCode !== 201) { diff --git a/packages/core/src/reporters/dashboard-reporter/tokens.ts b/packages/core/src/reporters/dashboard-reporter/tokens.ts index 2b5752d528..d5f5f6152f 100644 --- a/packages/core/src/reporters/dashboard-reporter/tokens.ts +++ b/packages/core/src/reporters/dashboard-reporter/tokens.ts @@ -1,6 +1,6 @@ export const dashboardReporterTokens = Object.freeze<{ - dashboardReporterClient: 'dashboardReporterClient', - httpClient: 'httpClient' + dashboardReporterClient: 'dashboardReporterClient'; + httpClient: 'httpClient'; }>({ dashboardReporterClient: 'dashboardReporterClient', httpClient: 'httpClient' diff --git a/packages/core/src/test-runner/ChildProcessTestRunnerDecorator.ts b/packages/core/src/test-runner/ChildProcessTestRunnerDecorator.ts index d796b54d16..a362a50f38 100644 --- a/packages/core/src/test-runner/ChildProcessTestRunnerDecorator.ts +++ b/packages/core/src/test-runner/ChildProcessTestRunnerDecorator.ts @@ -12,21 +12,17 @@ const MAX_WAIT_FOR_DISPOSE = 2000; * Runs the given test runner in a child process and forwards reports about test results */ export default class ChildProcessTestRunnerDecorator implements TestRunner { - private readonly worker: ChildProcessProxy; - constructor( - options: StrykerOptions, - sandboxFileNames: ReadonlyArray, - sandboxWorkingDirectory: string, - loggingContext: LoggingClientContext) { + constructor(options: StrykerOptions, sandboxFileNames: readonly string[], sandboxWorkingDirectory: string, loggingContext: LoggingClientContext) { this.worker = ChildProcessProxy.create( require.resolve(`./${ChildProcessTestRunnerWorker.name}`), loggingContext, options, { sandboxFileNames }, sandboxWorkingDirectory, - ChildProcessTestRunnerWorker); + ChildProcessTestRunnerWorker + ); } public init(): Promise { @@ -38,7 +34,6 @@ export default class ChildProcessTestRunnerDecorator implements TestRunner { } public async dispose(): Promise { - await timeout( // First let the inner test runner dispose this.worker.proxy.dispose().catch(error => { diff --git a/packages/core/src/test-runner/ChildProcessTestRunnerWorker.ts b/packages/core/src/test-runner/ChildProcessTestRunnerWorker.ts index 2a0f34758e..2215904a6f 100644 --- a/packages/core/src/test-runner/ChildProcessTestRunnerWorker.ts +++ b/packages/core/src/test-runner/ChildProcessTestRunnerWorker.ts @@ -5,11 +5,10 @@ import { errorToString } from '@stryker-mutator/util'; import { PluginCreator } from '../di'; export class ChildProcessTestRunnerWorker implements TestRunner { - private readonly underlyingTestRunner: TestRunner; public static inject = tokens(commonTokens.sandboxFileNames, commonTokens.options, commonTokens.injector); - constructor(sandboxFileNames: ReadonlyArray, { testRunner }: StrykerOptions, injector: Injector) { + constructor(sandboxFileNames: readonly string[], { testRunner }: StrykerOptions, injector: Injector) { this.underlyingTestRunner = injector .provideValue(commonTokens.sandboxFileNames, sandboxFileNames) .injectFunction(PluginCreator.createFactory(PluginKind.TestRunner)) diff --git a/packages/core/src/test-runner/CommandTestRunner.ts b/packages/core/src/test-runner/CommandTestRunner.ts index 62847a03a8..d90edbaa82 100644 --- a/packages/core/src/test-runner/CommandTestRunner.ts +++ b/packages/core/src/test-runner/CommandTestRunner.ts @@ -17,7 +17,6 @@ export interface CommandRunnerSettings { * The command can be configured, but defaults to `npm test`. */ export default class CommandTestRunner implements TestRunner { - /** * "command" */ @@ -36,15 +35,18 @@ export default class CommandTestRunner implements TestRunner { private timeoutHandler: undefined | (() => Promise); constructor(private readonly workingDir: string, options: StrykerOptions) { - this.settings = Object.assign({ - command: 'npm test' - }, options.commandRunner); + this.settings = Object.assign( + { + command: 'npm test' + }, + options.commandRunner + ); } public run(): Promise { return new Promise((res, rej) => { const timer = new Timer(); - const output: (string | Buffer)[] = []; + const output: Array = []; const childProcess = exec(this.settings.command, { cwd: this.workingDir }); childProcess.on('error', error => { kill(childProcess.pid) @@ -92,26 +94,29 @@ export default class CommandTestRunner implements TestRunner { if (exitCode === 0) { return { status: RunStatus.Complete, - tests: [{ - name: 'All tests', - status: TestStatus.Success, - timeSpentMs: duration - }] + tests: [ + { + name: 'All tests', + status: TestStatus.Success, + timeSpentMs: duration + } + ] }; } else { return { status: RunStatus.Complete, - tests: [{ - failureMessages: [output.map(buf => buf.toString()).join(os.EOL)], - name: 'All tests', - status: TestStatus.Failed, - timeSpentMs: duration - }] + tests: [ + { + failureMessages: [output.map(buf => buf.toString()).join(os.EOL)], + name: 'All tests', + status: TestStatus.Failed, + timeSpentMs: duration + } + ] }; } } }); - } public async dispose(): Promise { if (this.timeoutHandler) { diff --git a/packages/core/src/test-runner/ResilientTestRunnerFactory.ts b/packages/core/src/test-runner/ResilientTestRunnerFactory.ts index 74049ff20f..246b18f189 100644 --- a/packages/core/src/test-runner/ResilientTestRunnerFactory.ts +++ b/packages/core/src/test-runner/ResilientTestRunnerFactory.ts @@ -7,12 +7,18 @@ import RetryDecorator from './RetryDecorator'; import TimeoutDecorator from './TimeoutDecorator'; export default { - create(options: StrykerOptions, sandboxFileNames: ReadonlyArray, sandboxWorkingDirectory: string, loggingContext: LoggingClientContext): Required { + create( + options: StrykerOptions, + sandboxFileNames: readonly string[], + sandboxWorkingDirectory: string, + loggingContext: LoggingClientContext + ): Required { if (CommandTestRunner.is(options.testRunner)) { return new RetryDecorator(() => new TimeoutDecorator(() => new CommandTestRunner(sandboxWorkingDirectory, options))); } else { - return new RetryDecorator(() => - new TimeoutDecorator(() => new ChildProcessTestRunnerDecorator(options, sandboxFileNames, sandboxWorkingDirectory, loggingContext))); + return new RetryDecorator( + () => new TimeoutDecorator(() => new ChildProcessTestRunnerDecorator(options, sandboxFileNames, sandboxWorkingDirectory, loggingContext)) + ); } } }; diff --git a/packages/core/src/test-runner/RetryDecorator.ts b/packages/core/src/test-runner/RetryDecorator.ts index 307d1c8a91..cd88c08e99 100644 --- a/packages/core/src/test-runner/RetryDecorator.ts +++ b/packages/core/src/test-runner/RetryDecorator.ts @@ -10,7 +10,6 @@ const ERROR_MESSAGE = 'Test runner crashed. Tried twice to restart it without an * Wraps a test runner and implements the retry functionality. */ export default class RetryDecorator extends TestRunnerDecorator { - private readonly log = getLogger(RetryDecorator.name); public async run(options: RunOptions, attemptsLeft = 2, lastError?: Error): Promise { @@ -19,14 +18,17 @@ export default class RetryDecorator extends TestRunnerDecorator { return await this.innerRunner.run(options); } catch (error) { if (error instanceof OutOfMemoryError) { - this.log.info('Test runner process [%s] ran out of memory. You probably have a memory leak in your tests. Don\'t worry, Stryker will restart the process, but you might want to investigate this later, because this decreases performance.', error.pid); + this.log.info( + "Test runner process [%s] ran out of memory. You probably have a memory leak in your tests. Don't worry, Stryker will restart the process, but you might want to investigate this later, because this decreases performance.", + error.pid + ); } await this.recover(); return this.run(options, attemptsLeft - 1, error); } } else { await this.recover(); - return { status: RunStatus.Error, errorMessages: [ERROR_MESSAGE + errorToString(lastError)], tests: [] }; + return { status: RunStatus.Error, errorMessages: [`${ERROR_MESSAGE}${errorToString(lastError)}`], tests: [] }; } } diff --git a/packages/core/src/test-runner/TimeoutDecorator.ts b/packages/core/src/test-runner/TimeoutDecorator.ts index aafaf9bd5e..fd10ab82b7 100644 --- a/packages/core/src/test-runner/TimeoutDecorator.ts +++ b/packages/core/src/test-runner/TimeoutDecorator.ts @@ -7,7 +7,6 @@ import TestRunnerDecorator from './TestRunnerDecorator'; * Wraps a test runner and implements the timeout functionality. */ export default class TimeoutDecorator extends TestRunnerDecorator { - private readonly log = getLogger(TimeoutDecorator.name); public async run(options: RunOptions): Promise { diff --git a/packages/core/src/transpiler/ChildProcessTranspiler.ts b/packages/core/src/transpiler/ChildProcessTranspiler.ts index f998838c03..3e3cd94313 100644 --- a/packages/core/src/transpiler/ChildProcessTranspiler.ts +++ b/packages/core/src/transpiler/ChildProcessTranspiler.ts @@ -8,17 +8,11 @@ import LoggingClientContext from '../logging/LoggingClientContext'; import { ChildProcessTranspilerWorker } from './ChildProcessTranspilerWorker'; export class ChildProcessTranspiler implements Transpiler, Disposable { - private readonly childProcess: ChildProcessProxy; - public static inject = tokens( - commonTokens.options, - coreTokens.loggingContext, - commonTokens.produceSourceMaps); + public static inject = tokens(commonTokens.options, coreTokens.loggingContext, commonTokens.produceSourceMaps); - constructor(options: StrykerOptions, - loggingContext: LoggingClientContext, - produceSourceMaps: boolean) { + constructor(options: StrykerOptions, loggingContext: LoggingClientContext, produceSourceMaps: boolean) { this.childProcess = ChildProcessProxy.create( require.resolve(`./${ChildProcessTranspilerWorker.name}`), loggingContext, @@ -29,7 +23,7 @@ export class ChildProcessTranspiler implements Transpiler, Disposable { ); } - public transpile(files: ReadonlyArray): Promise> { + public transpile(files: readonly File[]): Promise { return this.childProcess.proxy.transpile(files); } diff --git a/packages/core/src/transpiler/ChildProcessTranspilerWorker.ts b/packages/core/src/transpiler/ChildProcessTranspilerWorker.ts index d9d692f36c..0d267562ca 100644 --- a/packages/core/src/transpiler/ChildProcessTranspilerWorker.ts +++ b/packages/core/src/transpiler/ChildProcessTranspilerWorker.ts @@ -16,7 +16,7 @@ export class ChildProcessTranspilerWorker implements Transpiler { .injectClass(TranspilerFacade); } - public transpile(files: ReadonlyArray): Promise> { + public transpile(files: readonly File[]): Promise { return this.innerTranspiler.transpile(files); } } diff --git a/packages/core/src/transpiler/CoverageInstrumenterTranspiler.ts b/packages/core/src/transpiler/CoverageInstrumenterTranspiler.ts index c6cdecd77b..de54ebfdcf 100644 --- a/packages/core/src/transpiler/CoverageInstrumenterTranspiler.ts +++ b/packages/core/src/transpiler/CoverageInstrumenterTranspiler.ts @@ -15,15 +15,14 @@ export interface CoverageMapsByFile { } export default class CoverageInstrumenterTranspiler implements Transpiler { - private readonly instrumenter: Instrumenter; public fileCoverageMaps: CoverageMapsByFile = Object.create(null); - constructor(private readonly settings: StrykerOptions, private readonly filesToInstrument: ReadonlyArray) { + constructor(private readonly settings: StrykerOptions, private readonly filesToInstrument: readonly string[]) { this.instrumenter = createInstrumenter({ coverageVariable: this.coverageVariable, preserveComments: true }); } - public async transpile(files: ReadonlyArray): Promise> { + public async transpile(files: readonly File[]): Promise { return files.map(file => this.instrumentFileIfNeeded(file)); } @@ -91,7 +90,7 @@ export default class CoverageInstrumenterTranspiler implements Transpiler { fnMap: {}, statementMap: input.statementMap }; - Object.keys(input.fnMap).forEach(key => output.fnMap[key] = input.fnMap[key].loc); + Object.keys(input.fnMap).forEach(key => (output.fnMap[key] = input.fnMap[key].loc)); return output; } } diff --git a/packages/core/src/transpiler/MutantTranspileScheduler.ts b/packages/core/src/transpiler/MutantTranspileScheduler.ts index bfbebc229e..d906784c5e 100644 --- a/packages/core/src/transpiler/MutantTranspileScheduler.ts +++ b/packages/core/src/transpiler/MutantTranspileScheduler.ts @@ -16,7 +16,6 @@ import TranspileResult from './TranspileResult'; const INITIAL_CONCURRENCY = 100; export class MutantTranspileScheduler implements Disposable { - private currentMutatedFile: SourceFile; private readonly concurrencyTicket$ = new BehaviorSubject(INITIAL_CONCURRENCY); @@ -25,13 +24,11 @@ export class MutantTranspileScheduler implements Disposable { /** * Creates a mutant transpiler */ - constructor(private readonly transpiler: Transpiler, private readonly unMutatedFiles: ReadonlyArray) { } + constructor(private readonly transpiler: Transpiler, private readonly unMutatedFiles: readonly File[]) {} - public scheduleTranspileMutants(allMutants: ReadonlyArray): Observable { + public scheduleTranspileMutants(allMutants: readonly TestableMutant[]): Observable { return from(allMutants).pipe( - zip(this.concurrencyTicket$.pipe( - flatMap(n => range(0, n)) - )), + zip(this.concurrencyTicket$.pipe(flatMap(n => range(0, n)))), flatMap(([mutant]) => this.transpileMutant(mutant), 1 /* IMPORTANT! Never transpile multiple mutants at once! */) ); } @@ -41,7 +38,7 @@ export class MutantTranspileScheduler implements Disposable { */ public readonly scheduleNext = () => { this.concurrencyTicket$.next(1); - } + }; /** * Dispose @@ -53,11 +50,11 @@ export class MutantTranspileScheduler implements Disposable { private createTranspiledMutant(mutant: TestableMutant, transpileResult: TranspileResult) { return new TranspiledMutant(mutant, transpileResult, someFilesChanged(this.unMutatedFiles)); - function someFilesChanged(unMutatedFiles: ReadonlyArray): boolean { + function someFilesChanged(unMutatedFiles: readonly File[]): boolean { return transpileResult.outputFiles.some(file => fileChanged(file, unMutatedFiles)); } - function fileChanged(file: File, unMutatedFiles: ReadonlyArray) { + function fileChanged(file: File, unMutatedFiles: readonly File[]) { if (unMutatedFiles) { const unMutatedFile = unMutatedFiles.find(f => f.name === file.name); return !unMutatedFile || unMutatedFile.textContent !== file.textContent; diff --git a/packages/core/src/transpiler/SourceMapper.ts b/packages/core/src/transpiler/SourceMapper.ts index de0f35bca0..a8dece6586 100644 --- a/packages/core/src/transpiler/SourceMapper.ts +++ b/packages/core/src/transpiler/SourceMapper.ts @@ -18,8 +18,10 @@ export interface MappedLocation { export class SourceMapError extends StrykerError { constructor(message: string, innerError?: Error) { - super(`${message}. Cannot analyse code coverage. Setting \`coverageAnalysis: "off"\` in your stryker.conf.js will prevent this error, but forces Stryker to run each test for each mutant.`, - innerError); + super( + `${message}. Cannot analyse code coverage. Setting \`coverageAnalysis: "off"\` in your stryker.conf.js will prevent this error, but forces Stryker to run each test for each mutant.`, + innerError + ); Error.captureStackTrace(this, SourceMapError); // TS recommendation: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work Object.setPrototypeOf(this, SourceMapError.prototype); @@ -40,7 +42,7 @@ export default abstract class SourceMapper { public abstract transpiledFileNameFor(originalFileName: string): string; - public static create(transpiledFiles: ReadonlyArray, options: StrykerOptions): SourceMapper { + public static create(transpiledFiles: readonly File[], options: StrykerOptions): SourceMapper { if (options.transpilers.length && options.coverageAnalysis !== 'off') { return new TranspiledSourceMapper(transpiledFiles); } else { @@ -50,11 +52,10 @@ export default abstract class SourceMapper { } export class TranspiledSourceMapper extends SourceMapper { - private sourceMaps: SourceMapBySource; private readonly log = getLogger(SourceMapper.name); - constructor(private readonly transpiledFiles: ReadonlyArray) { + constructor(private readonly transpiledFiles: readonly File[]) { super(); } @@ -84,8 +85,7 @@ export class TranspiledSourceMapper extends SourceMapper { } private getRelativeSource(from: SourceMap, to: MappedLocation) { - return path.relative(path.dirname(from.sourceMapFileName), to.fileName) - .replace(/\\/g, '/'); + return path.relative(path.dirname(from.sourceMapFileName), to.fileName).replace(/\\/g, '/'); } /** @@ -145,8 +145,9 @@ export class TranspiledSourceMapper extends SourceMapper { * @param transpiledFile The transpiled file for which the data url is */ private getSourceMapFileFromUrl(sourceMapUrl: string, transpiledFile: File): File { - const sourceMapFile = this.isInlineUrl(sourceMapUrl) ? - this.getInlineSourceMap(sourceMapUrl, transpiledFile) : this.getExternalSourceMap(sourceMapUrl, transpiledFile); + const sourceMapFile = this.isInlineUrl(sourceMapUrl) + ? this.getInlineSourceMap(sourceMapUrl, transpiledFile) + : this.getExternalSourceMap(sourceMapUrl, transpiledFile); return sourceMapFile; } @@ -163,7 +164,12 @@ export class TranspiledSourceMapper extends SourceMapper { const content = base64Decode(sourceMapUrl.substr(supportedDataPrefix.length)); return new File(transpiledFile.name, content); } else { - throw new SourceMapError(`Source map file for "${transpiledFile.name}" cannot be read. Data url "${sourceMapUrl.substr(0, sourceMapUrl.lastIndexOf(','))}" found, where "${supportedDataPrefix.substr(0, supportedDataPrefix.length - 1)}" was expected`); + throw new SourceMapError( + `Source map file for "${transpiledFile.name}" cannot be read. Data url "${sourceMapUrl.substr( + 0, + sourceMapUrl.lastIndexOf(',') + )}" found, where "${supportedDataPrefix.substr(0, supportedDataPrefix.length - 1)}" was expected` + ); } } @@ -176,7 +182,9 @@ export class TranspiledSourceMapper extends SourceMapper { if (sourceMapFile) { return sourceMapFile; } else { - throw new SourceMapError(`Source map file "${sourceMapUrl}" (referenced by "${transpiledFile.name}") cannot be found in list of transpiled files`); + throw new SourceMapError( + `Source map file "${sourceMapUrl}" (referenced by "${transpiledFile.name}") cannot be found in list of transpiled files` + ); } } @@ -188,7 +196,7 @@ export class TranspiledSourceMapper extends SourceMapper { let currentMatch: RegExpExecArray | null; let lastMatch: RegExpExecArray | null = null; // Retrieve the final sourceMappingURL comment in the file - while (currentMatch = SOURCE_MAP_URL_REGEX.exec(transpiledFile.textContent)) { + while ((currentMatch = SOURCE_MAP_URL_REGEX.exec(transpiledFile.textContent))) { lastMatch = currentMatch; } if (lastMatch) { @@ -202,7 +210,6 @@ export class TranspiledSourceMapper extends SourceMapper { } export class PassThroughSourceMapper extends SourceMapper { - /** * @inheritdoc */ @@ -220,8 +227,7 @@ export class PassThroughSourceMapper extends SourceMapper { class SourceMap { private sourceMap: SourceMapConsumer | undefined; - constructor(public transpiledFile: File, public sourceMapFileName: string, private readonly rawSourceMap: RawSourceMap) { - } + constructor(public transpiledFile: File, public sourceMapFileName: string, private readonly rawSourceMap: RawSourceMap) {} public async generatedPositionFor(originalPosition: Position, relativeSource: string): Promise { if (!this.sourceMap) { this.sourceMap = await new SourceMapConsumer(this.rawSourceMap); @@ -236,7 +242,7 @@ class SourceMap { return Promise.resolve({ column: transpiledPosition.column || 0, - line: (transpiledPosition.line || 1) - 1 // Stryker works 0-based + line: (transpiledPosition.line || 1) - 1 // Stryker works 0-based }); } } diff --git a/packages/core/src/transpiler/TranspileResult.ts b/packages/core/src/transpiler/TranspileResult.ts index 018dd84791..85b5ff8adf 100644 --- a/packages/core/src/transpiler/TranspileResult.ts +++ b/packages/core/src/transpiler/TranspileResult.ts @@ -1,6 +1,6 @@ import { File } from '@stryker-mutator/api/core'; export default interface TranspileResult { - outputFiles: ReadonlyArray; + outputFiles: readonly File[]; error: string | null; } diff --git a/packages/core/src/transpiler/TranspilerFacade.ts b/packages/core/src/transpiler/TranspilerFacade.ts index d7a304073a..a7d8a607e0 100644 --- a/packages/core/src/transpiler/TranspilerFacade.ts +++ b/packages/core/src/transpiler/TranspilerFacade.ts @@ -6,36 +6,31 @@ import { coreTokens } from '../di'; import { PluginCreator } from '../di/PluginCreator'; class NamedTranspiler { - constructor(public name: string, public transpiler: Transpiler) { } + constructor(public name: string, public transpiler: Transpiler) {} } export class TranspilerFacade implements Transpiler { - private readonly innerTranspilers: NamedTranspiler[]; - public static inject = tokens( - commonTokens.options, - coreTokens.pluginCreatorTranspiler); + public static inject = tokens(commonTokens.options, coreTokens.pluginCreatorTranspiler); constructor(options: StrykerOptions, pluginCreator: PluginCreator) { - this.innerTranspilers = options.transpilers - .map(transpilerName => new NamedTranspiler(transpilerName, pluginCreator.create(transpilerName))); + this.innerTranspilers = options.transpilers.map(transpilerName => new NamedTranspiler(transpilerName, pluginCreator.create(transpilerName))); } - public transpile(files: ReadonlyArray): Promise> { + public transpile(files: readonly File[]): Promise { return this.performTranspileChain(files); } private async performTranspileChain( - input: ReadonlyArray, + input: readonly File[], remainingChain: NamedTranspiler[] = this.innerTranspilers.slice() - ): Promise> { + ): Promise { const current = remainingChain.shift(); if (current) { - const output = await current.transpiler.transpile(input) - .catch(error => { - throw new StrykerError(`An error occurred in transpiler "${current.name}"`, error); - }); + const output = await current.transpiler.transpile(input).catch(error => { + throw new StrykerError(`An error occurred in transpiler "${current.name}"`, error); + }); return this.performTranspileChain(output, remainingChain); } else { return input; diff --git a/packages/core/src/transpiler/index.ts b/packages/core/src/transpiler/index.ts index 1ae5e1ba00..1acf55fbf7 100644 --- a/packages/core/src/transpiler/index.ts +++ b/packages/core/src/transpiler/index.ts @@ -8,14 +8,15 @@ import { ChildProcessTranspiler } from './ChildProcessTranspiler'; /** * Creates a transpiler. If one is configured, spawns that in a child process */ -export function transpilerFactory(options: StrykerOptions, - injector: Injector) - : Transpiler & Disposable { +export function transpilerFactory( + options: StrykerOptions, + injector: Injector +): Transpiler & Disposable { if (options.transpilers.length) { return injector.injectClass(ChildProcessTranspiler); } else { return { - transpile(files: ReadonlyArray) { + transpile(files: readonly File[]) { return Promise.resolve(files); }, dispose() { diff --git a/packages/core/src/utils/LocationHelper.ts b/packages/core/src/utils/LocationHelper.ts index 440f0d3331..1203e23f1b 100644 --- a/packages/core/src/utils/LocationHelper.ts +++ b/packages/core/src/utils/LocationHelper.ts @@ -1,13 +1,14 @@ import { Location } from '@stryker-mutator/api/core'; export default class LocationHelper { + public static MAX_VALUE = new LocationHelper( + Object.freeze({ + end: Object.freeze({ column: Number.POSITIVE_INFINITY, line: Number.POSITIVE_INFINITY }), + start: Object.freeze({ column: 0, line: -1 }) + }) + ); - public static MAX_VALUE = new LocationHelper(Object.freeze({ - end: Object.freeze({ column: Number.POSITIVE_INFINITY, line: Number.POSITIVE_INFINITY }), - start: Object.freeze({ column: 0, line: -1 }) - })); - - constructor(private readonly loc: Location) { } + constructor(private readonly loc: Location) {} /** * Indicates whether the current location is covered by an other location. @@ -15,10 +16,11 @@ export default class LocationHelper { * @returns true if this location is covered by given location, otherwise false */ public isCoveredBy(maybeWrapper: Location): boolean { - const isAfterStart = this.loc.start.line > maybeWrapper.start.line || + const isAfterStart = + this.loc.start.line > maybeWrapper.start.line || (this.loc.start.line === maybeWrapper.start.line && this.loc.start.column >= maybeWrapper.start.column); - const isBeforeEnd = this.loc.end.line < maybeWrapper.end.line || - (this.loc.end.line === maybeWrapper.end.line && this.loc.end.column <= maybeWrapper.end.column); + const isBeforeEnd = + this.loc.end.line < maybeWrapper.end.line || (this.loc.end.line === maybeWrapper.end.line && this.loc.end.column <= maybeWrapper.end.column); return isAfterStart && isBeforeEnd; } @@ -29,9 +31,10 @@ export default class LocationHelper { */ public isSmallerArea(maybeSmaller: Location) { let firstLocationHasSmallerArea = false; - const lineDifference = (this.loc.end.line - this.loc.start.line) - (maybeSmaller.end.line - maybeSmaller.start.line); + const lineDifference = this.loc.end.line - this.loc.start.line - (maybeSmaller.end.line - maybeSmaller.start.line); const coversLessLines = lineDifference > 0; - const coversLessColumns = lineDifference === 0 && (maybeSmaller.start.column - this.loc.start.column) + (this.loc.end.column - maybeSmaller.end.column) > 0; + const coversLessColumns = + lineDifference === 0 && maybeSmaller.start.column - this.loc.start.column + (this.loc.end.column - maybeSmaller.end.column) > 0; if (coversLessLines || coversLessColumns) { firstLocationHasSmallerArea = true; } diff --git a/packages/core/src/utils/StringBuilder.ts b/packages/core/src/utils/StringBuilder.ts index 0fdfd36d9e..bb51257122 100644 --- a/packages/core/src/utils/StringBuilder.ts +++ b/packages/core/src/utils/StringBuilder.ts @@ -21,7 +21,8 @@ export default class StringBuilder { } public static concat(...builders: StringBuilder[]): string { - return builders.map(b => b.toString()) + return builders + .map(b => b.toString()) .filter(Boolean) .join(EOL); } diff --git a/packages/core/src/utils/Task.ts b/packages/core/src/utils/Task.ts index 0f840b98f6..69cfd7c268 100644 --- a/packages/core/src/utils/Task.ts +++ b/packages/core/src/utils/Task.ts @@ -4,7 +4,6 @@ import { timeout, TimeoutExpired } from './objectUtils'; * Wraps a promise in a Task api for convenience. */ export class Task { - protected _promise: Promise; private resolveFn: (value?: T | PromiseLike) => void; private rejectFn: (reason: any) => void; @@ -17,23 +16,23 @@ export class Task { }); } - get promise() { + public get promise() { return this._promise; } - get isCompleted() { + public get isCompleted() { return this._isCompleted; } public resolve = (result: T | PromiseLike): void => { this._isCompleted = true; this.resolveFn(result); - } + }; public reject = (reason: any): void => { this._isCompleted = true; this.rejectFn(reason); - } + }; } /** diff --git a/packages/core/src/utils/TemporaryDirectory.ts b/packages/core/src/utils/TemporaryDirectory.ts index 95982377ee..e85a78a6f4 100644 --- a/packages/core/src/utils/TemporaryDirectory.ts +++ b/packages/core/src/utils/TemporaryDirectory.ts @@ -64,11 +64,10 @@ export class TemporaryDirectory implements Disposable { if (!this.isInitialized) { throw new Error('initialize() was not called!'); } - this.log.debug(`Deleting stryker temp directory %s`, this.temporaryDirectory); + this.log.debug('Deleting stryker temp directory %s', this.temporaryDirectory); try { await deleteDir(this.temporaryDirectory); - } - catch (e) { + } catch (e) { return this.log.info(`Failed to delete stryker temp directory ${this.temporaryDirectory}`); } } @@ -80,5 +79,4 @@ export class TemporaryDirectory implements Disposable { public random(): number { return Math.ceil(Math.random() * 10000000); } - } diff --git a/packages/core/src/utils/fileUtils.ts b/packages/core/src/utils/fileUtils.ts index d0381695b2..4d880c5f4b 100644 --- a/packages/core/src/utils/fileUtils.ts +++ b/packages/core/src/utils/fileUtils.ts @@ -6,13 +6,17 @@ import * as rimraf from 'rimraf'; export function glob(expression: string): Promise { return new Promise((resolve, reject) => { - nodeGlob(expression, { nodir: true }, (error, matches) => { error ? reject(error) : resolve(matches); }); + nodeGlob(expression, { nodir: true }, (error, matches) => { + error ? reject(error) : resolve(matches); + }); }); } export function deleteDir(dirToDelete: string): Promise { return new Promise((resolve, reject) => { - rimraf(dirToDelete, error => { error ? reject(error) : resolve(); }); + rimraf(dirToDelete, error => { + error ? reject(error) : resolve(); + }); }); } @@ -22,7 +26,6 @@ export async function cleanFolder(folderName: string) { await deleteDir(folderName); return mkdirp.sync(folderName); } catch (e) { - return mkdirp.sync(folderName); } } diff --git a/packages/core/src/utils/objectUtils.ts b/packages/core/src/utils/objectUtils.ts index 563f0989e7..a7296a98f7 100644 --- a/packages/core/src/utils/objectUtils.ts +++ b/packages/core/src/utils/objectUtils.ts @@ -2,9 +2,7 @@ import * as _ from 'lodash'; import treeKill = require('tree-kill'); export { serialize, deserialize } from 'surrial'; -export function freezeRecursively( - target: T -): T { +export function freezeRecursively(target: T): T { Object.freeze(target); Object.keys(target).forEach(key => { if (_.isObject(target[key])) { @@ -14,7 +12,7 @@ export function freezeRecursively( return target; } -export function filterEmpty(input: (T | null | void)[]) { +export function filterEmpty(input: Array) { return input.filter(item => item !== undefined && item !== null) as T[]; } @@ -36,9 +34,7 @@ export function wrapInClosure(codeFragment: string) { /** * A wrapper around `process.env` (for testability) */ -export function getEnvironmentVariable( - nameEnvironmentVariable: string -): string | undefined { +export function getEnvironmentVariable(nameEnvironmentVariable: string): string | undefined { return process.env[nameEnvironmentVariable]; } @@ -72,10 +68,7 @@ export function kill(pid: number): Promise { } export const TimeoutExpired: unique symbol = Symbol('TimeoutExpired'); -export function timeout( - promise: Promise, - ms: number -): Promise { +export function timeout(promise: Promise, ms: number): Promise { const sleep = new Promise((res, rej) => { const timer = setTimeout(() => res(TimeoutExpired), ms); promise diff --git a/packages/core/test/helpers/ChildProcessMock.ts b/packages/core/test/helpers/ChildProcessMock.ts index 035a1875fb..12603c120f 100644 --- a/packages/core/test/helpers/ChildProcessMock.ts +++ b/packages/core/test/helpers/ChildProcessMock.ts @@ -1,11 +1,10 @@ import { EventEmitter } from 'events'; export default class ChildProcessMock extends EventEmitter { - public stdout = new EventEmitter(); public stderr = new EventEmitter(); - constructor(readonly pid: number) { + constructor(public readonly pid: number) { super(); } } diff --git a/packages/core/test/helpers/LoggingServer.ts b/packages/core/test/helpers/LoggingServer.ts index 559e0042a2..0152d555f0 100644 --- a/packages/core/test/helpers/LoggingServer.ts +++ b/packages/core/test/helpers/LoggingServer.ts @@ -4,7 +4,6 @@ import * as net from 'net'; import { Observable, Subscriber } from 'rxjs'; export default class LoggingServer { - private readonly server: net.Server; private subscriber: Subscriber | undefined; public readonly event$: Observable; @@ -14,7 +13,10 @@ export default class LoggingServer { this.server = net.createServer(socket => { socket.on('data', data => { // Log4js also sends "__LOG4JS__" to signal an event end. Ignore those. - const logEventStrings = data.toString().split('__LOG4JS__').filter(Boolean); + const logEventStrings = data + .toString() + .split('__LOG4JS__') + .filter(Boolean); const loggingEvents: log4js.LoggingEvent[] = logEventStrings.map(logEventString => parse(logEventString)); loggingEvents.forEach(event => this.subscriber && this.subscriber.next(event)); }); diff --git a/packages/core/test/helpers/producers.ts b/packages/core/test/helpers/producers.ts index 00aa5ce7a0..95198f32a1 100644 --- a/packages/core/test/helpers/producers.ts +++ b/packages/core/test/helpers/producers.ts @@ -81,14 +81,16 @@ export const transpileResult = factoryMethod(() => ({ export const sourceFile = () => new SourceFile(factory.file()); -export const testableMutant = (fileName = 'file', mutatorName = 'foobarMutator') => new TestableMutant('1337', factory.mutant({ - fileName, - mutatorName, - range: [12, 13], - replacement: '-' -}), new SourceFile( - new File(fileName, Buffer.from('const a = 4 + 5')) -)); +export const testableMutant = (fileName = 'file', mutatorName = 'foobarMutator') => + new TestableMutant( + '1337', + factory.mutant({ + fileName, + mutatorName, + range: [12, 13], + replacement: '-' + }), + new SourceFile(new File(fileName, Buffer.from('const a = 4 + 5'))) + ); -export const transpiledMutant = (fileName = 'file') => - new TranspiledMutant(testableMutant(fileName), transpileResult(), true); +export const transpiledMutant = (fileName = 'file') => new TranspiledMutant(testableMutant(fileName), transpileResult(), true); diff --git a/packages/core/test/integration/child-proxy/ChildProcessProxy.it.spec.ts b/packages/core/test/integration/child-proxy/ChildProcessProxy.it.spec.ts index 7129643e7f..32ec17e4a4 100644 --- a/packages/core/test/integration/child-proxy/ChildProcessProxy.it.spec.ts +++ b/packages/core/test/integration/child-proxy/ChildProcessProxy.it.spec.ts @@ -18,7 +18,6 @@ import { sleep } from '../../helpers/testUtils'; import { Echo } from './Echo'; describe(ChildProcessProxy.name, () => { - let sut: ChildProcessProxy; let loggingServer: LoggingServer; let log: Mock; @@ -78,9 +77,7 @@ describe(ChildProcessProxy.name, () => { it('should be able to log on debug when LogLevel.Debug is allowed', async () => { const logEventTask = new Task(); - loggingServer.event$.pipe( - filter(event => event.categoryName === Echo.name) - ).subscribe(logEventTask.resolve.bind(logEventTask)); + loggingServer.event$.pipe(filter(event => event.categoryName === Echo.name)).subscribe(logEventTask.resolve.bind(logEventTask)); sut.proxy.debug('test message'); const log = await logEventTask.promise; expect(log.categoryName).eq(Echo.name); @@ -89,9 +86,7 @@ describe(ChildProcessProxy.name, () => { it('should not log on trace if LogLevel.Debug is allowed as min log level', async () => { const logEventTask = new Task(); - loggingServer.event$.pipe( - filter(event => event.categoryName === Echo.name) - ).subscribe(logEventTask.resolve.bind(logEventTask)); + loggingServer.event$.pipe(filter(event => event.categoryName === Echo.name)).subscribe(logEventTask.resolve.bind(logEventTask)); sut.proxy.trace('foo'); sut.proxy.debug('bar'); const log = await logEventTask.promise; @@ -111,7 +106,9 @@ describe(ChildProcessProxy.name, () => { await sleep(10); await expect(sut.proxy.exit(12)).rejected; const call = log.warn.getCall(0); - expect(call.args[0]).matches(/Child process \[pid \d+\] exited unexpectedly with exit code 12 \(without signal\)\. Last part of stdout and stderr was/g); + expect(call.args[0]).matches( + /Child process \[pid \d+\] exited unexpectedly with exit code 12 \(without signal\)\. Last part of stdout and stderr was/g + ); expect(call.args[0]).includes('stdout message'); expect(call.args[0]).includes('stderr message'); }); @@ -128,5 +125,7 @@ describe(ChildProcessProxy.name, () => { function toLogLevel(level: log4js.Level) { const levelName = (level as any).levelStr.toLowerCase(); - return [LogLevel.Debug, LogLevel.Error, LogLevel.Fatal, LogLevel.Information, LogLevel.Off, LogLevel.Trace, LogLevel.Warning].find(level => level === levelName); + return [LogLevel.Debug, LogLevel.Error, LogLevel.Fatal, LogLevel.Information, LogLevel.Off, LogLevel.Trace, LogLevel.Warning].find( + level => level === levelName + ); } diff --git a/packages/core/test/integration/child-proxy/Echo.ts b/packages/core/test/integration/child-proxy/Echo.ts index 1cb027300b..7548d73852 100644 --- a/packages/core/test/integration/child-proxy/Echo.ts +++ b/packages/core/test/integration/child-proxy/Echo.ts @@ -3,11 +3,10 @@ import { tokens } from '@stryker-mutator/api/plugin'; import { getLogger } from 'log4js'; export class Echo { - private readonly logger = getLogger(Echo.name); public static inject = tokens('name'); - constructor(public name: string) { } + constructor(public name: string) {} public say(value: string) { return `${this.name}: ${value}`; @@ -27,7 +26,9 @@ export class Echo { public exit(code: number) { process.exit(code); - return new Promise(() => {/*never resolve*/ }); + return new Promise(() => { + /*never resolve*/ + }); } public readFile() { @@ -60,6 +61,7 @@ export class Echo { public memoryLeak() { const arr: number[] = []; + // eslint-disable-next-line no-constant-condition while (true) { arr.push(1); } diff --git a/packages/core/test/integration/command-test-runner/CommandTestRunner.it.spec.ts b/packages/core/test/integration/command-test-runner/CommandTestRunner.it.spec.ts index 4167533752..e4abdc00bd 100644 --- a/packages/core/test/integration/command-test-runner/CommandTestRunner.it.spec.ts +++ b/packages/core/test/integration/command-test-runner/CommandTestRunner.it.spec.ts @@ -7,7 +7,6 @@ import CommandTestRunner, { CommandRunnerSettings } from '../../../src/test-runn import * as objectUtils from '../../../src/utils/objectUtils'; describe(`${CommandTestRunner.name} integration`, () => { - const workingDir = path.resolve(__dirname, '..', '..', '..', 'testResources', 'command-runner'); it('should report test as successful', async () => { diff --git a/packages/core/test/integration/config-reader/ConfigReader.it.spec.ts b/packages/core/test/integration/config-reader/ConfigReader.it.spec.ts index 6ce0605a34..7a4dc102c5 100644 --- a/packages/core/test/integration/config-reader/ConfigReader.it.spec.ts +++ b/packages/core/test/integration/config-reader/ConfigReader.it.spec.ts @@ -8,19 +8,15 @@ import ConfigReader from '../../../src/config/ConfigReader'; import { coreTokens } from '../../../src/di'; describe(ConfigReader.name, () => { - let sut: ConfigReader; function createSut(cliOptions: Partial): ConfigReader { - return testInjector.injector - .provideValue(coreTokens.cliOptions, cliOptions) - .injectClass(ConfigReader); + return testInjector.injector.provideValue(coreTokens.cliOptions, cliOptions).injectClass(ConfigReader); } describe('readConfig()', () => { let result: Config; describe('without config file', () => { - beforeEach(() => { sut = createSut({ some: 'option', someOther: 2 }); result = sut.readConfig(); @@ -99,7 +95,6 @@ describe(ConfigReader.name, () => { }); describe('with an existing file, but not a function', () => { - beforeEach(() => { sut = createSut({ configFile: 'testResources/config-reader/invalid.conf.js' }); }); @@ -120,7 +115,6 @@ describe(ConfigReader.name, () => { }); describe('with an existing file, but has syntax errors', () => { - beforeEach(() => { sut = createSut({ configFile: 'testResources/config-reader/syntax-error.conf.js' }); }); diff --git a/packages/core/test/integration/source-mapper/SourceMapper.it.spec.ts b/packages/core/test/integration/source-mapper/SourceMapper.it.spec.ts index 9dcecba968..db2714d72f 100644 --- a/packages/core/test/integration/source-mapper/SourceMapper.it.spec.ts +++ b/packages/core/test/integration/source-mapper/SourceMapper.it.spec.ts @@ -9,20 +9,17 @@ function resolve(...filePart: string[]) { } function readFiles(...files: string[]): Promise { - return Promise.all(files - .map(relative => resolve(relative)) - .map(fileName => fsAsPromised.readFile(fileName).then(content => new File(fileName, content)))); + return Promise.all( + files.map(relative => resolve(relative)).map(fileName => fsAsPromised.readFile(fileName).then(content => new File(fileName, content))) + ); } describe('Source mapper integration', () => { - let sut: TranspiledSourceMapper; describe('with external source maps', () => { beforeEach(async () => { - const files = await readFiles( - path.join('external-source-maps', 'js', 'math.js'), - path.join('external-source-maps', 'js', 'math.js.map')); + const files = await readFiles(path.join('external-source-maps', 'js', 'math.js'), path.join('external-source-maps', 'js', 'math.js.map')); sut = new TranspiledSourceMapper(files); }); diff --git a/packages/core/test/integration/test-runner/AdditionalTestRunners.ts b/packages/core/test/integration/test-runner/AdditionalTestRunners.ts index 997c0cd32d..5d10f99ff0 100644 --- a/packages/core/test/integration/test-runner/AdditionalTestRunners.ts +++ b/packages/core/test/integration/test-runner/AdditionalTestRunners.ts @@ -35,10 +35,8 @@ class DirectResolvedTestRunner implements TestRunner { } class DiscoverRegexTestRunner implements TestRunner { - public static inject = tokens(commonTokens.options); - constructor(private readonly options: StrykerOptions) { - } + constructor(private readonly options: StrykerOptions) {} public run(): Promise { if (isRegExp(this.options.someRegex)) { @@ -50,7 +48,6 @@ class DiscoverRegexTestRunner implements TestRunner { } class ErroredTestRunner implements TestRunner { - public run() { let expectedError: any = null; try { @@ -63,7 +60,6 @@ class ErroredTestRunner implements TestRunner { } class RejectInitRunner implements TestRunner { - public init() { return Promise.reject(new Error('Init was rejected')); } @@ -75,12 +71,11 @@ class RejectInitRunner implements TestRunner { class NeverResolvedTestRunner implements TestRunner { public run() { - return new Promise(() => { }); + return new Promise(() => {}); } } class SlowInitAndDisposeTestRunner implements TestRunner { - public inInit: boolean; public init() { @@ -105,7 +100,6 @@ class SlowInitAndDisposeTestRunner implements TestRunner { } } class VerifyWorkingFolderTestRunner implements TestRunner { - public runResult: RunResult = { status: RunStatus.Complete, tests: [] }; public run() { @@ -125,7 +119,7 @@ class AsyncronousPromiseRejectionHandlerTestRunner implements TestRunner { } public run() { - this.promise.catch(() => { }); + this.promise.catch(() => {}); return Promise.resolve({ status: RunStatus.Complete, tests: [] }); } } diff --git a/packages/core/test/integration/test-runner/ResilientTestRunnerFactory.it.spec.ts b/packages/core/test/integration/test-runner/ResilientTestRunnerFactory.it.spec.ts index 7fdde0fba0..cc1365d400 100644 --- a/packages/core/test/integration/test-runner/ResilientTestRunnerFactory.it.spec.ts +++ b/packages/core/test/integration/test-runner/ResilientTestRunnerFactory.it.spec.ts @@ -12,7 +12,6 @@ import LoggingServer from '../../helpers/LoggingServer'; import { sleep } from '../../helpers/testUtils'; describe('ResilientTestRunnerFactory integration', () => { - let sut: Required; let options: StrykerOptions; const sandboxWorkingDirectory = path.resolve('./test/integration/test-runner'); @@ -98,7 +97,9 @@ describe('ResilientTestRunnerFactory integration', () => { const result = await actRun(1000); expect(RunStatus[result.status]).to.be.eq(RunStatus[RunStatus.Error]); expect(result.errorMessages).to.have.length(1); - expect((result.errorMessages as any)[0]).includes('SyntaxError: This is invalid syntax!').and.includes('at ErroredTestRunner.run'); + expect((result.errorMessages as any)[0]) + .includes('SyntaxError: This is invalid syntax!') + .and.includes('at ErroredTestRunner.run'); }); it('should run only after initialization, even when it is slow', async () => { @@ -137,7 +138,9 @@ describe('ResilientTestRunnerFactory integration', () => { await arrangeSut('proximity-mine'); const result = await actRun(); expect(RunStatus[result.status]).eq(RunStatus[RunStatus.Error]); - expect(result.errorMessages).property('0').contains('Test runner crashed'); + expect(result.errorMessages) + .property('0') + .contains('Test runner crashed'); }); it('should handle asynchronously handled promise rejections from the underlying test runner', async () => { @@ -149,9 +152,11 @@ describe('ResilientTestRunnerFactory integration', () => { await loggingServer.dispose(); const actualLogEvents = await logEvents; expect( - actualLogEvents.find(logEvent => - log4js.levels.DEBUG.isEqualTo(logEvent.level) - && logEvent.data.toString().indexOf('UnhandledPromiseRejectionWarning: Unhandled promise rejection') > -1) + actualLogEvents.find( + logEvent => + log4js.levels.DEBUG.isEqualTo(logEvent.level) && + logEvent.data.toString().includes('UnhandledPromiseRejectionWarning: Unhandled promise rejection') + ) ).ok; }); }); diff --git a/packages/core/test/integration/utils/fileUtils.spec.ts b/packages/core/test/integration/utils/fileUtils.spec.ts index bc2077f312..47192806e9 100644 --- a/packages/core/test/integration/utils/fileUtils.spec.ts +++ b/packages/core/test/integration/utils/fileUtils.spec.ts @@ -2,13 +2,10 @@ import { expect } from 'chai'; import * as fileUtils from '../../../src/utils/fileUtils'; describe('fileUtils', () => { - describe('glob', () => { - it('should resolve files', () => - expect(fileUtils.glob('testResources/globTestFiles/sample/**/*.js')).to.eventually.have.length(10)); + it('should resolve files', () => expect(fileUtils.glob('testResources/globTestFiles/sample/**/*.js')).to.eventually.have.length(10)); it('should not resolve to directories', () => expect(fileUtils.glob('testResources/globTestFiles/notResolveDirs/**/*.js')).to.eventually.have.length(1)); }); - }); diff --git a/packages/core/test/unit/Sandbox.spec.ts b/packages/core/test/unit/Sandbox.spec.ts index 644309aac8..1c8f5020c7 100644 --- a/packages/core/test/unit/Sandbox.spec.ts +++ b/packages/core/test/unit/Sandbox.spec.ts @@ -5,7 +5,7 @@ import { Mutant } from '@stryker-mutator/api/mutant'; import { MutantStatus } from '@stryker-mutator/api/report'; import { TestFramework } from '@stryker-mutator/api/test_framework'; import { RunResult, RunStatus } from '@stryker-mutator/api/test_runner'; -import { fileAlreadyExistsError, mutant as createMutant, testResult } from '@stryker-mutator/test-helpers/src/factory'; +import { mutant as createMutant, fileAlreadyExistsError, testResult } from '@stryker-mutator/test-helpers/src/factory'; import { normalizeWhitespaces } from '@stryker-mutator/util'; import { expect } from 'chai'; import * as mkdirp from 'mkdirp'; @@ -60,10 +60,7 @@ describe(Sandbox.name, () => { sandboxDirectory = path.resolve('random-folder-3'); expectedTargetFileToMutate = path.join(sandboxDirectory, 'file1'); expectedTestFrameworkHooksFile = path.join(sandboxDirectory, '___testHooksForStryker.js'); - inputFiles = [ - expectedFileToMutate, - notMutatedFile, - ]; + inputFiles = [expectedFileToMutate, notMutatedFile]; temporaryDirectoryMock = sinon.createStubInstance(TemporaryDirectory); temporaryDirectoryMock.createRandomDirectory.returns(sandboxDirectory); @@ -82,20 +79,19 @@ describe(Sandbox.name, () => { interface CreateArgs { testFramework: TestFramework | null; overheadTimeMS: number; - files: ReadonlyArray; + files: readonly File[]; } function createSut(overrides?: Partial) { const args: CreateArgs = { files: inputFiles, overheadTimeMS: OVERHEAD_TIME_MS, - testFramework: null, + testFramework: null }; - const { files, testFramework, overheadTimeMS } = {...args, ...overrides }; + const { files, testFramework, overheadTimeMS } = { ...args, ...overrides }; return Sandbox.create(options, SANDBOX_INDEX, files, testFramework, overheadTimeMS, LOGGING_CONTEXT, temporaryDirectoryMock as any); } describe('create()', () => { - it('should copy input files when created', async () => { await createSut(); expect(fileUtils.writeFile).calledWith(expectedTargetFileToMutate, inputFiles[0].content); @@ -136,10 +132,13 @@ describe(Sandbox.name, () => { findNodeModulesStub.resolves('node_modules'); symlinkJunctionStub.rejects(fileAlreadyExistsError()); await createSut(testFrameworkStub); - expect(log.warn).calledWithMatch(normalizeWhitespaces( - `Could not symlink "node_modules" in sandbox directory, it is already created in the sandbox. + expect(log.warn).calledWithMatch( + normalizeWhitespaces( + `Could not symlink "node_modules" in sandbox directory, it is already created in the sandbox. Please remove the node_modules from your sandbox files. Alternatively, set \`symlinkNodeModules\` - to \`false\` to disable this warning.`)); + to \`false\` to disable this warning.` + ) + ); }); it('should log a warning if linking "node_modules" results in an unknown error', async () => { @@ -147,8 +146,10 @@ describe(Sandbox.name, () => { const error = new Error('unknown'); symlinkJunctionStub.rejects(error); await createSut(testFrameworkStub); - expect(log.warn).calledWithMatch(normalizeWhitespaces( - `Unexpected error while trying to symlink "basePath/node_modules" in sandbox directory.`), error); + expect(log.warn).calledWithMatch( + normalizeWhitespaces('Unexpected error while trying to symlink "basePath/node_modules" in sandbox directory.'), + error + ); }); it('should symlink node modules in sandbox directory if `symlinkNodeModules` is `false`', async () => { @@ -166,7 +167,7 @@ describe(Sandbox.name, () => { expect(testRunner.run).to.have.been.calledWith({ mutatedFileName: undefined, testHooks: 'hooks', - timeout: 231313, + timeout: 231313 }); }); @@ -176,7 +177,7 @@ describe(Sandbox.name, () => { expect(testRunner.run).to.have.been.calledWith({ mutatedFileName: 'path/to/file', testHooks: 'hooks', - timeout: 231313, + timeout: 231313 }); }); }); @@ -189,13 +190,14 @@ describe(Sandbox.name, () => { beforeEach(() => { mutant = createMutant({ fileName: expectedFileToMutate.name, replacement: 'mutated', range: [0, 8] }); - const testableMutant = new TestableMutant( - '1', - mutant, - new SourceFile(new File('foobar.js', 'original code'))); + const testableMutant = new TestableMutant('1', mutant, new SourceFile(new File('foobar.js', 'original code'))); testableMutant.selectTest(testResult({ timeSpentMs: 10 }), 1); testableMutant.selectTest(testResult({ timeSpentMs: 2 }), 2); - transpiledMutant = new TranspiledMutant(testableMutant, { outputFiles: [new File(expectedFileToMutate.name, 'mutated code')], error: null }, true); + transpiledMutant = new TranspiledMutant( + testableMutant, + { outputFiles: [new File(expectedFileToMutate.name, 'mutated code')], error: null }, + true + ); testFrameworkStub.filter.returns(testFilterCodeFragment); }); @@ -261,8 +263,11 @@ describe(Sandbox.name, () => { runResult.errorMessages = ['Cannot call "foo" of undefined (or something)']; const sut = await createSut(); const actualResult = await sut.runMutant(transpiledMutant); - expect(log.debug).calledWith('A runtime error occurred: %s during execution of mutant: %s', - runResult.errorMessages[0], transpiledMutant.mutant.toString()); + expect(log.debug).calledWith( + 'A runtime error occurred: %s during execution of mutant: %s', + runResult.errorMessages[0], + transpiledMutant.mutant.toString() + ); expect(actualResult.status).eq(MutantStatus.RuntimeError); }); @@ -270,8 +275,9 @@ describe(Sandbox.name, () => { transpiledMutant.transpileResult.error = 'Error! Cannot negate a string (or something)'; const sut = await createSut(); const mutantResult = await sut.runMutant(transpiledMutant); - expect(log.debug).calledWith(`Transpile error occurred: "Error! Cannot negate a string (or something)" during transpiling of mutant ${ - transpiledMutant.mutant.toString()}`); + expect(log.debug).calledWith( + `Transpile error occurred: "Error! Cannot negate a string (or something)" during transpiling of mutant ${transpiledMutant.mutant.toString()}` + ); expect(mutantResult.status).eq(MutantStatus.TranspileError); }); diff --git a/packages/core/test/unit/SandboxPool.spec.ts b/packages/core/test/unit/SandboxPool.spec.ts index 54d8c12b77..fe73a2bf4b 100644 --- a/packages/core/test/unit/SandboxPool.spec.ts +++ b/packages/core/test/unit/SandboxPool.spec.ts @@ -49,10 +49,13 @@ describe(SandboxPool.name, () => { genericSandboxForAllSubsequentCallsToNewSandbox.runMutant.resolves(factory.runResult()); secondSandbox.runMutant.resolves(factory.runResult()); inputMutants = [transpiledMutant()]; - createStub = sinon.stub(Sandbox, 'create') + createStub = sinon + .stub(Sandbox, 'create') .resolves(genericSandboxForAllSubsequentCallsToNewSandbox) - .onCall(0).resolves(firstSandbox) - .onCall(1).resolves(secondSandbox); + .onCall(0) + .resolves(firstSandbox) + .onCall(1) + .resolves(secondSandbox); initialTranspiledFiles = [file()]; }); @@ -72,7 +75,7 @@ describe(SandboxPool.name, () => { return testInjector.injector .provideValue(commonTokens.logger, factory.logger()) - .provideValue(coreTokens.testFramework, expectedTestFramework as unknown as TestFramework) + .provideValue(coreTokens.testFramework, (expectedTestFramework as unknown) as TestFramework) .provideValue(coreTokens.initialRunResult, initialRunResult) .provideValue(coreTokens.loggingContext, LOGGING_CONTEXT) .provideValue(coreTokens.transpiledFiles, initialTranspiledFiles) @@ -81,13 +84,13 @@ describe(SandboxPool.name, () => { } function actRunMutants() { - return sut.runMutants(from(inputMutants)) + return sut + .runMutants(from(inputMutants)) .pipe(toArray()) .toPromise(); } describe('runMutants', () => { - it('should use maxConcurrentTestRunners when set', async () => { testInjector.options.maxConcurrentTestRunners = 1; sut = createSut(); @@ -133,9 +136,12 @@ describe(SandboxPool.name, () => { const secondMutantTask = new Task(); createStub.reset(); createStub - .onFirstCall().returns(createFirstSandboxTask.promise) - .onSecondCall().returns(createSecondSandboxTask.promise) - .onThirdCall().returns(createThirdSandboxTask.promise); + .onFirstCall() + .returns(createFirstSandboxTask.promise) + .onSecondCall() + .returns(createSecondSandboxTask.promise) + .onThirdCall() + .returns(createThirdSandboxTask.promise); firstSandbox.runMutant.reset(); firstSandbox.runMutant.returns(firstRunMutantTask.promise); secondSandbox.runMutant.reset(); @@ -155,8 +161,8 @@ describe(SandboxPool.name, () => { expect(createStub).callCount(2); // Act - createFirstSandboxTask.resolve(firstSandbox as unknown as Sandbox); - createThirdSandboxTask.resolve(genericSandboxForAllSubsequentCallsToNewSandbox as unknown as Sandbox); + createFirstSandboxTask.resolve((firstSandbox as unknown) as Sandbox); + createThirdSandboxTask.resolve((genericSandboxForAllSubsequentCallsToNewSandbox as unknown) as Sandbox); await secondResultTask.promise; expect(createStub).callCount(3); firstRunMutantTask.resolve(factory.mutantResult()); @@ -178,7 +184,7 @@ describe(SandboxPool.name, () => { expect(Sandbox.create).to.have.callCount(1); }); - it('should reject when a sandbox couldn\'t be created', async () => { + it("should reject when a sandbox couldn't be created", async () => { createStub.reset(); const expectedError = new Error('foo error'); createStub.rejects(expectedError); @@ -211,17 +217,20 @@ describe(SandboxPool.name, () => { const task2 = new Task(); createStub.reset(); createStub - .onCall(0).returns(task.promise) - .onCall(1).returns(task2.promise) - .onCall(2).resolves(genericSandboxForAllSubsequentCallsToNewSandbox); // promise is not yet resolved + .onCall(0) + .returns(task.promise) + .onCall(1) + .returns(task2.promise) + .onCall(2) + .resolves(genericSandboxForAllSubsequentCallsToNewSandbox); // promise is not yet resolved inputMutants.push(transpiledMutant()); // Act const runPromise = sut.runMutants(from(inputMutants)).toPromise(); - task.resolve(firstSandbox as unknown as Sandbox); + task.resolve((firstSandbox as unknown) as Sandbox); await runPromise; const disposePromise = sut.dispose(); - task2.resolve(secondSandbox as unknown as Sandbox); + task2.resolve((secondSandbox as unknown) as Sandbox); await disposePromise; expect(secondSandbox.dispose).called; }); @@ -234,18 +243,22 @@ describe(SandboxPool.name, () => { const task2 = new Task(); createStub.reset(); createStub - .onCall(0).returns(task.promise) - .onCall(1).returns(task2.promise) - .onCall(2).resolves(genericSandboxForAllSubsequentCallsToNewSandbox); // promise is not yet resolved + .onCall(0) + .returns(task.promise) + .onCall(1) + .returns(task2.promise) + .onCall(2) + .resolves(genericSandboxForAllSubsequentCallsToNewSandbox); // promise is not yet resolved inputMutants.push(transpiledMutant(), transpiledMutant()); // 3 mutants // Act - const runPromise = sut.runMutants(from(inputMutants)) + const runPromise = sut + .runMutants(from(inputMutants)) .pipe(toArray()) .toPromise(); const disposePromise = sut.dispose(); - task.resolve(firstSandbox as unknown as Sandbox); - task2.resolve(secondSandbox as unknown as Sandbox); + task.resolve((firstSandbox as unknown) as Sandbox); + task2.resolve((secondSandbox as unknown) as Sandbox); await disposePromise; await runPromise; diff --git a/packages/core/test/unit/ScoreResultCalculator.spec.ts b/packages/core/test/unit/ScoreResultCalculator.spec.ts index fb89d52696..51a943e210 100644 --- a/packages/core/test/unit/ScoreResultCalculator.spec.ts +++ b/packages/core/test/unit/ScoreResultCalculator.spec.ts @@ -25,14 +25,14 @@ describe(ScoreResultCalculator.name, () => { it('should count results of a single file', () => { const fileName = path.join('base', 'something'); - const actual = sut.calculate([ + const actual = sut.calculate([ mutantResult({ status: MutantStatus.RuntimeError, sourceFilePath: fileName }), mutantResult({ status: MutantStatus.Killed, sourceFilePath: fileName }), mutantResult({ status: MutantStatus.TranspileError, sourceFilePath: fileName }), mutantResult({ status: MutantStatus.NoCoverage, sourceFilePath: fileName }), mutantResult({ status: MutantStatus.Survived, sourceFilePath: fileName }), mutantResult({ status: MutantStatus.Killed, sourceFilePath: fileName }), - mutantResult({ status: MutantStatus.TimedOut, sourceFilePath: fileName }), + mutantResult({ status: MutantStatus.TimedOut, sourceFilePath: fileName }) ]); expect(actual.name).to.eq('base'); function assertNumbers(actual: ScoreResult) { @@ -57,30 +57,27 @@ describe(ScoreResultCalculator.name, () => { it('should wrap a single result in its base directory', () => { const fileName = path.join('base', 'something'); - const actual = sut.calculate([ - mutantResult({ status: MutantStatus.RuntimeError, sourceFilePath: fileName }) - ]); + const actual = sut.calculate([mutantResult({ status: MutantStatus.RuntimeError, sourceFilePath: fileName })]); expect(actual.name).eq('base'); expect(actual.childResults).lengthOf(1); expect(actual.childResults[0].name).eq('something'); }); it('should count results of multiple files', () => { - const actual = sut.calculate( - [ - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.NoCoverage }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.RuntimeError }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.RuntimeError }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.NoCoverage }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.Survived }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.Survived }) - ]); + const actual = sut.calculate([ + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.NoCoverage }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.RuntimeError }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.RuntimeError }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.NoCoverage }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.Survived }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Add.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('karma-jasmine', 'src', 'Circle.js'), status: MutantStatus.Survived }) + ]); expect(actual.name).to.be.eq(path.join('karma-jasmine', 'src')); expect(extractNumbers(actual)).to.deep.eq({ killed: 6, runtimeErrors: 2, transpileErrors: 0, survived: 2, noCoverage: 2 }); expect(actual.childResults).to.have.lengthOf(2); @@ -95,13 +92,12 @@ describe(ScoreResultCalculator.name, () => { }); it('should group results per directory', () => { - const actual = sut.calculate( - [ - mutantResult({ sourceFilePath: path.join('a', 'b', 'c', 'd', 'e.js'), status: MutantStatus.Killed }), - mutantResult({ sourceFilePath: path.join('a', 'b', 'c', 'd', 'f.js'), status: MutantStatus.Survived }), - mutantResult({ sourceFilePath: path.join('a', 'b', 'g.js'), status: MutantStatus.NoCoverage }), - mutantResult({ sourceFilePath: path.join('a', 'b', 'h.js'), status: MutantStatus.RuntimeError }), - ]); + const actual = sut.calculate([ + mutantResult({ sourceFilePath: path.join('a', 'b', 'c', 'd', 'e.js'), status: MutantStatus.Killed }), + mutantResult({ sourceFilePath: path.join('a', 'b', 'c', 'd', 'f.js'), status: MutantStatus.Survived }), + mutantResult({ sourceFilePath: path.join('a', 'b', 'g.js'), status: MutantStatus.NoCoverage }), + mutantResult({ sourceFilePath: path.join('a', 'b', 'h.js'), status: MutantStatus.RuntimeError }) + ]); expect(actual.name).to.eq(path.join('a', 'b')); expect(extractNumbers(actual)).to.deep.eq({ killed: 1, survived: 1, noCoverage: 1, runtimeErrors: 1, transpileErrors: 0 }); expect(actual.childResults).to.have.lengthOf(3); @@ -120,14 +116,13 @@ describe(ScoreResultCalculator.name, () => { }); it('should order by directory/files first and than on alphabet', () => { - const actual = sut.calculate( - [ - mutantResult({ sourceFilePath: path.join('a', 'z', 'c.js') }), - mutantResult({ sourceFilePath: path.join('a', 'z', 'a.js') }), - mutantResult({ sourceFilePath: path.join('a', 'b.js') }), - mutantResult({ sourceFilePath: path.join('a', 'a.js') }), - mutantResult({ sourceFilePath: path.join('a', 'A.js') }) - ]); + const actual = sut.calculate([ + mutantResult({ sourceFilePath: path.join('a', 'z', 'c.js') }), + mutantResult({ sourceFilePath: path.join('a', 'z', 'a.js') }), + mutantResult({ sourceFilePath: path.join('a', 'b.js') }), + mutantResult({ sourceFilePath: path.join('a', 'a.js') }), + mutantResult({ sourceFilePath: path.join('a', 'A.js') }) + ]); expect(actual.name).to.eq('a'); expect(actual.childResults[0].name).to.eq('z'); expect(actual.childResults[0].childResults[0].name).to.eq('a.js'); @@ -138,7 +133,7 @@ describe(ScoreResultCalculator.name, () => { }); it('should be able to handle no results', () => { - const actual = sut.calculate([]); + const actual = sut.calculate([]); expect(extractNumbers(actual)).to.deep.eq({ killed: 0, survived: 0, transpileErrors: 0, runtimeErrors: 0, noCoverage: 0 }); expect(actual.childResults.length).to.be.eq(0); expect(actual.name).to.be.eq(''); @@ -147,10 +142,7 @@ describe(ScoreResultCalculator.name, () => { }); it('should be able to handle children that do not start with the same path', () => { - const actual = sut.calculate([ - mutantResult({ sourceFilePath: 'dir1/one' }), - mutantResult({ sourceFilePath: 'dir2/two' }) - ]); + const actual = sut.calculate([mutantResult({ sourceFilePath: 'dir1/one' }), mutantResult({ sourceFilePath: 'dir2/two' })]); expect(actual.childResults.length).to.eq(2); expect(actual.name).to.eq(''); expect(actual.childResults[0].name).to.eq('dir1/one'); @@ -183,23 +175,25 @@ describe(ScoreResultCalculator.name, () => { }); it('should not set exit code = 1 if `threshold.break` is not configured', () => { - sut.determineExitCode(scoreResult({ mutationScore: 0 }), mutationScoreThresholds({ break: null })); + sut.determineExitCode(scoreResult({ mutationScore: 0 }), mutationScoreThresholds({ break: null })); - expect(setExitCodeStub).not.called; - expect(testInjector.logger.debug).calledWith('No breaking threshold configured. Won\'t fail the build no matter how low your mutation score is. Set `thresholds.break` to change this behavior.'); + expect(setExitCodeStub).not.called; + expect(testInjector.logger.debug).calledWith( + "No breaking threshold configured. Won't fail the build no matter how low your mutation score is. Set `thresholds.break` to change this behavior." + ); }); it('should not set exit code = 1 if `threshold.break` === score', () => { - sut.determineExitCode(scoreResult({ mutationScore: 10.000001 }), mutationScoreThresholds({ break: 10.000001 })); - expect(setExitCodeStub).not.called; - expect(testInjector.logger.info).calledWith('Final mutation score of 10.00 is greater than or equal to break threshold 10.000001'); + sut.determineExitCode(scoreResult({ mutationScore: 10.000001 }), mutationScoreThresholds({ break: 10.000001 })); + expect(setExitCodeStub).not.called; + expect(testInjector.logger.info).calledWith('Final mutation score of 10.00 is greater than or equal to break threshold 10.000001'); }); it('should set exit code = 1 if `threshold.break` > score', () => { - sut.determineExitCode(scoreResult({ mutationScore: 56.6 }), mutationScoreThresholds({ break: 56.7 })); - expect(setExitCodeStub).calledWith(1); - expect(testInjector.logger.error).calledWith('Final mutation score 56.60 under breaking threshold 56.7, setting exit code to 1 (failure).'); - expect(testInjector.logger.info).calledWith('(improve mutation score or set `thresholds.break = null` to prevent this error in the future)'); + sut.determineExitCode(scoreResult({ mutationScore: 56.6 }), mutationScoreThresholds({ break: 56.7 })); + expect(setExitCodeStub).calledWith(1); + expect(testInjector.logger.error).calledWith('Final mutation score 56.60 under breaking threshold 56.7, setting exit code to 1 (failure).'); + expect(testInjector.logger.info).calledWith('(improve mutation score or set `thresholds.break = null` to prevent this error in the future)'); }); }); }); diff --git a/packages/core/test/unit/SourceFile.spec.ts b/packages/core/test/unit/SourceFile.spec.ts index 87fccef1c0..49cc78e4f3 100644 --- a/packages/core/test/unit/SourceFile.spec.ts +++ b/packages/core/test/unit/SourceFile.spec.ts @@ -12,7 +12,6 @@ const content = ` const lines = content.split('\n'); describe('SourceFile', () => { - let sut: SourceFile; beforeEach(() => { @@ -40,7 +39,7 @@ describe('SourceFile', () => { it('should work for line 0', () => { sut = new SourceFile(new File('', '1234567')); - expect(sut.getLocation([2, 4])).deep.eq({ start: { line: 0, column: 2 }, end: { line: 0, column: 4 }}); + expect(sut.getLocation([2, 4])).deep.eq({ start: { line: 0, column: 2 }, end: { line: 0, column: 4 } }); }); }); }); diff --git a/packages/core/test/unit/Stryker.spec.ts b/packages/core/test/unit/Stryker.spec.ts index d6698899eb..383289dfe2 100644 --- a/packages/core/test/unit/Stryker.spec.ts +++ b/packages/core/test/unit/Stryker.spec.ts @@ -80,22 +80,37 @@ describe(Stryker.name, () => { sinon.stub(di, 'buildMainInjector').returns(injectorMock); sinon.stub(scoreResultCalculator, 'determineExitCode').returns(sinon.stub()); injectorMock.injectClass - .withArgs(BroadcastReporter).returns(reporterMock) - .withArgs(InitialTestExecutor).returns(initialTestExecutorMock) - .withArgs(InputFileResolver).returns(inputFileResolverMock) - .withArgs(MutatorFacade).returns(mutatorMock) - .withArgs(MutantTestMatcher).returns(mutantTestMatcherMock) - .withArgs(MutationTestExecutor).returns(mutationTestExecutorMock) - .withArgs(MutationTestReportCalculator).returns(mutationTestReportCalculatorMock) - .withArgs(ScoreResultCalculator).returns(scoreResultCalculator); + .withArgs(BroadcastReporter) + .returns(reporterMock) + .withArgs(InitialTestExecutor) + .returns(initialTestExecutorMock) + .withArgs(InputFileResolver) + .returns(inputFileResolverMock) + .withArgs(MutatorFacade) + .returns(mutatorMock) + .withArgs(MutantTestMatcher) + .returns(mutantTestMatcherMock) + .withArgs(MutationTestExecutor) + .returns(mutationTestExecutorMock) + .withArgs(MutationTestReportCalculator) + .returns(mutationTestReportCalculatorMock) + .withArgs(ScoreResultCalculator) + .returns(scoreResultCalculator); injectorMock.resolve - .withArgs(commonTokens.options).returns(strykerConfig) - .withArgs(di.coreTokens.timer).returns(timerMock) - .withArgs(di.coreTokens.reporter).returns(reporterMock) - .withArgs(di.coreTokens.testFramework).returns(testFrameworkMock) - .withArgs(di.coreTokens.temporaryDirectory).returns(temporaryDirectoryMock) - .withArgs(commonTokens.getLogger).returns(() => logMock) - .withArgs(di.coreTokens.transpiler).returns(transpilerMock); + .withArgs(commonTokens.options) + .returns(strykerConfig) + .withArgs(di.coreTokens.timer) + .returns(timerMock) + .withArgs(di.coreTokens.reporter) + .returns(reporterMock) + .withArgs(di.coreTokens.testFramework) + .returns(testFrameworkMock) + .withArgs(di.coreTokens.temporaryDirectory) + .returns(temporaryDirectoryMock) + .withArgs(commonTokens.getLogger) + .returns(() => logMock) + .withArgs(di.coreTokens.transpiler) + .returns(transpilerMock); }); describe('when constructed', () => { @@ -110,7 +125,6 @@ describe(Stryker.name, () => { }); describe('runMutationTest()', () => { - let inputFiles: InputFileCollection; let initialTranspiledFiles: File[]; let initialRunResult: RunResult; @@ -119,11 +133,7 @@ describe(Stryker.name, () => { let mutantResults: MutantResult[]; beforeEach(() => { - mutants = [ - testableMutant('file1', 'fooMutator'), - testableMutant('file2', 'barMutator'), - testableMutant('file3', 'bazMutator') - ]; + mutants = [testableMutant('file1', 'fooMutator'), testableMutant('file2', 'barMutator'), testableMutant('file3', 'bazMutator')]; mutantResults = [mutantResult()]; mutantTestMatcherMock.matchWithMutants.returns(mutants); mutatorMock.mutate.returns(mutants); @@ -138,7 +148,6 @@ describe(Stryker.name, () => { }); describe('sad flow', () => { - beforeEach(() => { sut = new Stryker({}); }); @@ -183,13 +192,17 @@ describe(Stryker.name, () => { it('should log the remark to run again with logLevel trace if no tests were executed in initial test run', async () => { while (initialRunResult.tests.pop()); await sut.runMutationTest(); - expect(logMock.info).to.have.been.calledWith('Trouble figuring out what went wrong? Try `npx stryker run --fileLogLevel trace --logLevel debug` to get some more info.'); + expect(logMock.info).to.have.been.calledWith( + 'Trouble figuring out what went wrong? Try `npx stryker run --fileLogLevel trace --logLevel debug` to get some more info.' + ); }); it('should log the remark to run again with logLevel trace if no mutants were generated', async () => { while (mutants.pop()); // clear all mutants await sut.runMutationTest(); - expect(logMock.info).to.have.been.calledWith('Trouble figuring out what went wrong? Try `npx stryker run --fileLogLevel trace --logLevel debug` to get some more info.'); + expect(logMock.info).to.have.been.calledWith( + 'Trouble figuring out what went wrong? Try `npx stryker run --fileLogLevel trace --logLevel debug` to get some more info.' + ); }); it('should dispose the injector', async () => { @@ -199,7 +212,6 @@ describe(Stryker.name, () => { }); describe('happy flow', () => { - it('should configure the logging server', async () => { sut = new Stryker({}); await sut.runMutationTest(); diff --git a/packages/core/test/unit/TestFrameworkOrchestrator.spec.ts b/packages/core/test/unit/TestFrameworkOrchestrator.spec.ts index be7d60190d..9172c6f16a 100644 --- a/packages/core/test/unit/TestFrameworkOrchestrator.spec.ts +++ b/packages/core/test/unit/TestFrameworkOrchestrator.spec.ts @@ -7,7 +7,6 @@ import { PluginCreator } from '../../src/di/PluginCreator'; import TestFrameworkOrchestrator from '../../src/TestFrameworkOrchestrator'; describe('TestFrameworkOrchestrator', () => { - let sut: TestFrameworkOrchestrator; let pluginCreatorMock: sinon.SinonStubbedInstance>; @@ -26,7 +25,10 @@ describe('TestFrameworkOrchestrator', () => { const itShouldLogCoverageAnalysisOffOnDebug = () => { it('should log on debug that coverageAnalysis was "off"', () => { sut.determineTestFramework(); - expect(testInjector.logger.debug).calledWith('The `coverageAnalysis` setting is "%s", not hooking into the test framework to achieve performance benefits.', 'off'); + expect(testInjector.logger.debug).calledWith( + 'The `coverageAnalysis` setting is "%s", not hooking into the test framework to achieve performance benefits.', + 'off' + ); }); }; @@ -42,7 +44,6 @@ describe('TestFrameworkOrchestrator', () => { }); describe('when options contains a testFramework', () => { - beforeEach(() => { testInjector.options.testFramework = 'fooFramework'; }); @@ -67,12 +68,10 @@ describe('TestFrameworkOrchestrator', () => { expect(actualTestFramework).eq(expectedTestFramework); expect(pluginCreatorMock.create).calledWith('foo'); }); - }); describe('when options does not contain a testFramework', () => { describe('and coverageAnalysis is not "off"', () => { - beforeEach(() => { sut = createSut(); }); @@ -80,14 +79,14 @@ describe('TestFrameworkOrchestrator', () => { it('should log a warning for the missing setting', () => { sut = createSut(); sut.determineTestFramework(); - expect(testInjector.logger.warn) - .calledWith('Missing config settings `testFramework`. Set `coverageAnalysis` option explicitly to "off" to ignore this warning.'); + expect(testInjector.logger.warn).calledWith( + 'Missing config settings `testFramework`. Set `coverageAnalysis` option explicitly to "off" to ignore this warning.' + ); }); itShouldNotRetrieveATestFramework(); }); describe('and coverageAnalysis is `off`', () => { - beforeEach(() => { testInjector.options.coverageAnalysis = 'off'; sut = createSut(); @@ -100,7 +99,7 @@ describe('TestFrameworkOrchestrator', () => { function createSut() { return testInjector.injector - .provideValue(coreTokens.pluginCreatorTestFramework, pluginCreatorMock as unknown as PluginCreator) + .provideValue(coreTokens.pluginCreatorTestFramework, (pluginCreatorMock as unknown) as PluginCreator) .injectClass(TestFrameworkOrchestrator); } }); diff --git a/packages/core/test/unit/TestableMutant.spec.ts b/packages/core/test/unit/TestableMutant.spec.ts index e944eba38a..f62c6ccfb6 100644 --- a/packages/core/test/unit/TestableMutant.spec.ts +++ b/packages/core/test/unit/TestableMutant.spec.ts @@ -6,7 +6,6 @@ import SourceFile from '../../src/SourceFile'; import TestableMutant, { TestSelectionResult } from '../../src/TestableMutant'; describe('TestableMutant', () => { - let innerMutant: Mutant; beforeEach(() => { @@ -25,7 +24,10 @@ describe('TestableMutant', () => { it('should reflect timeSpentScopedTests, scopedTestIds and TestSelectionResult', () => { const sut = new TestableMutant('3', innerMutant, new SourceFile(new File('foobar.js', 'alert("foobar")'))); - sut.selectAllTests(runResult({ tests: [testResult({ name: 'spec1', timeSpentMs: 12 }), testResult({ name: 'spec2', timeSpentMs: 42 })] }), TestSelectionResult.FailedButAlreadyReported); + sut.selectAllTests( + runResult({ tests: [testResult({ name: 'spec1', timeSpentMs: 12 }), testResult({ name: 'spec2', timeSpentMs: 42 })] }), + TestSelectionResult.FailedButAlreadyReported + ); expect(sut.timeSpentScopedTests).eq(54); expect(sut.selectedTests).deep.eq([{ id: 0, name: 'spec1' }, { id: 1, name: 'spec2' }]); expect(sut.testSelectionResult).eq(TestSelectionResult.FailedButAlreadyReported); diff --git a/packages/core/test/unit/child-proxy/ChildProcessProxy.spec.ts b/packages/core/test/unit/child-proxy/ChildProcessProxy.spec.ts index 187c6381b7..9f78311d8a 100644 --- a/packages/core/test/unit/child-proxy/ChildProcessProxy.spec.ts +++ b/packages/core/test/unit/child-proxy/ChildProcessProxy.spec.ts @@ -7,7 +7,15 @@ import { EventEmitter } from 'events'; import * as os from 'os'; import * as sinon from 'sinon'; import ChildProcessProxy from '../../../src/child-proxy/ChildProcessProxy'; -import { autoStart, DisposeMessage, InitMessage, ParentMessage, ParentMessageKind, WorkerMessage, WorkerMessageKind } from '../../../src/child-proxy/messageProtocol'; +import { + autoStart, + DisposeMessage, + InitMessage, + ParentMessage, + ParentMessageKind, + WorkerMessage, + WorkerMessageKind +} from '../../../src/child-proxy/messageProtocol'; import LoggingClientContext from '../../../src/logging/LoggingClientContext'; import { serialize } from '../../../src/utils/objectUtils'; import * as objectUtils from '../../../src/utils/objectUtils'; @@ -28,7 +36,6 @@ class ChildProcessMock extends EventEmitter { } describe(ChildProcessProxy.name, () => { - let sut: ChildProcessProxy; let forkStub: sinon.SinonStub; let childProcessMock: ChildProcessMock; @@ -52,7 +59,6 @@ describe(ChildProcessProxy.name, () => { }); describe('constructor', () => { - it('should create child process', () => { sut = createSut(); expect(forkStub).calledWith(require.resolve('../../../src/child-proxy/ChildProcessProxyWorker'), [autoStart], { silent: true, execArgv: [] }); @@ -73,7 +79,7 @@ describe(ChildProcessProxy.name, () => { createSut({ loggingContext: LOGGING_CONTEXT, name: (expectedMessage.additionalInjectableValues as { name: string }).name, - options: expectedMessage.options, + options: expectedMessage.options, requirePath: expectedMessage.requirePath, workingDir: expectedMessage.workingDirectory }); @@ -102,13 +108,16 @@ describe(ChildProcessProxy.name, () => { childProcessMock.stdout.emit('data', 'bar'); childProcessMock.stderr.emit('data', 'foo'); actClose(23, 'SIGTERM'); - expect(logMock.warn).calledWithMatch(`Child process [pid ${childProcessMock.pid}] exited unexpectedly with exit code 23 (SIGTERM). Last part of stdout and stderr was:${os.EOL - }\tfoo${os.EOL}\tbar`); + expect(logMock.warn).calledWithMatch( + `Child process [pid ${childProcessMock.pid}] exited unexpectedly with exit code 23 (SIGTERM). Last part of stdout and stderr was:${os.EOL}\tfoo${os.EOL}\tbar` + ); }); it('should log that no stdout was available when stdout and stderr are empty', () => { actClose(23, 'SIGTERM'); - expect(logMock.warn).calledWith(`Child process [pid ${childProcessMock.pid}] exited unexpectedly with exit code 23 (SIGTERM). Stdout and stderr were empty.`); + expect(logMock.warn).calledWith( + `Child process [pid ${childProcessMock.pid}] exited unexpectedly with exit code 23 (SIGTERM). Stdout and stderr were empty.` + ); }); it('should log stdout and stderr in correct order', () => { @@ -116,8 +125,9 @@ describe(ChildProcessProxy.name, () => { childProcessMock.stderr.emit('data', 'baz'); childProcessMock.stdout.emit('data', 'bar'); actClose(23, 'SIGTERM'); - expect(logMock.warn).calledWith(`Child process [pid ${childProcessMock.pid}] exited unexpectedly with exit code 23 (SIGTERM). Last part of stdout and stderr was:${os.EOL - }\tbaz${os.EOL}\tfoobar`); + expect(logMock.warn).calledWith( + `Child process [pid ${childProcessMock.pid}] exited unexpectedly with exit code 23 (SIGTERM). Last part of stdout and stderr was:${os.EOL}\tbaz${os.EOL}\tfoobar` + ); }); it('should reject any outstanding worker promises with the error', () => { @@ -138,7 +148,6 @@ describe(ChildProcessProxy.name, () => { }); describe('when calling methods', () => { - beforeEach(() => { sut = createSut(); receiveMessage({ kind: ParentMessageKind.Initialized }); @@ -171,7 +180,6 @@ describe(ChildProcessProxy.name, () => { }); describe('dispose', () => { - beforeEach(() => { sut = createSut(); }); @@ -219,7 +227,6 @@ describe(ChildProcessProxy.name, () => { receiveMessage({ kind: ParentMessageKind.DisposeCompleted }); await disposePromise; } - }); function receiveMessage(workerResponse: ParentMessage) { @@ -227,18 +234,21 @@ describe(ChildProcessProxy.name, () => { } }); -function createSut(overrides: { - requirePath?: string; - loggingContext?: LoggingClientContext; - options?: Partial; - workingDir?: string; - name?: string; -} = {}): ChildProcessProxy { +function createSut( + overrides: { + requirePath?: string; + loggingContext?: LoggingClientContext; + options?: Partial; + workingDir?: string; + name?: string; + } = {} +): ChildProcessProxy { return ChildProcessProxy.create( overrides.requirePath || 'foobar', overrides.loggingContext || LOGGING_CONTEXT, factory.strykerOptions(overrides.options), { name: overrides.name || 'someArg' }, overrides.workingDir || 'workingDir', - HelloClass); + HelloClass + ); } diff --git a/packages/core/test/unit/child-proxy/ChildProcessWorker.spec.ts b/packages/core/test/unit/child-proxy/ChildProcessWorker.spec.ts index e853859447..1fd6d86027 100644 --- a/packages/core/test/unit/child-proxy/ChildProcessWorker.spec.ts +++ b/packages/core/test/unit/child-proxy/ChildProcessWorker.spec.ts @@ -6,7 +6,15 @@ import * as path from 'path'; import * as sinon from 'sinon'; import { rootInjector } from 'typed-inject'; import ChildProcessProxyWorker from '../../../src/child-proxy/ChildProcessProxyWorker'; -import { CallMessage, InitMessage, ParentMessage, ParentMessageKind, WorkerMessage, WorkerMessageKind, WorkResult } from '../../../src/child-proxy/messageProtocol'; +import { + CallMessage, + InitMessage, + ParentMessage, + ParentMessageKind, + WorkerMessage, + WorkerMessageKind, + WorkResult +} from '../../../src/child-proxy/messageProtocol'; import * as di from '../../../src/di'; import LogConfigurator from '../../../src/logging/LogConfigurator'; import LoggingClientContext from '../../../src/logging/LoggingClientContext'; @@ -18,7 +26,6 @@ import { HelloClass } from './HelloClass'; const LOGGING_CONTEXT: LoggingClientContext = Object.freeze({ port: 4200, level: LogLevel.Fatal }); describe(ChildProcessProxyWorker.name, () => { - let processOnStub: sinon.SinonStub; let processSendStub: sinon.SinonStub; let processListenersStub: sinon.SinonStub; @@ -56,7 +63,6 @@ describe(ChildProcessProxyWorker.name, () => { }); describe('after init message', () => { - let sut: ChildProcessProxyWorker; let initMessage: InitMessage; @@ -64,7 +70,7 @@ describe(ChildProcessProxyWorker.name, () => { sut = new ChildProcessProxyWorker(); const options = factory.strykerOptions(); initMessage = { - additionalInjectableValues: { name: 'FooBarName'}, + additionalInjectableValues: { name: 'FooBarName' }, kind: WorkerMessageKind.Init, loggingContext: LOGGING_CONTEXT, options, @@ -88,7 +94,7 @@ describe(ChildProcessProxyWorker.name, () => { expect(processChdirStub).calledWith(fullWorkingDir); }); - it('should not change the current working directory if it didn\'t change', () => { + it("should not change the current working directory if it didn't change", () => { initMessage.workingDirectory = process.cwd(); processOnMessage(initMessage); expect(logMock.debug).not.called; @@ -104,7 +110,7 @@ describe(ChildProcessProxyWorker.name, () => { it('should remove any additional listeners', async () => { // Arrange - function noop() { } + function noop() {} processes.push(noop); // Act @@ -134,7 +140,6 @@ describe(ChildProcessProxyWorker.name, () => { }); describe('on worker message', () => { - async function actAndAssert(workerMessage: CallMessage, expectedResult: WorkResult) { // Act processOnMessage(initMessage); @@ -227,16 +232,12 @@ describe(ChildProcessProxyWorker.name, () => { await actAndAssert(workerMessage, expectedResult); }); - }); }); function processOnMessage(message: WorkerMessage) { - processOnStub - .withArgs('message') - .callArgWith(1, [serialize(message)]); + processOnStub.withArgs('message').callArgWith(1, [serialize(message)]); } - }); function tick() { diff --git a/packages/core/test/unit/child-proxy/HelloClass.ts b/packages/core/test/unit/child-proxy/HelloClass.ts index d1c66afe21..0ac3ea3e07 100644 --- a/packages/core/test/unit/child-proxy/HelloClass.ts +++ b/packages/core/test/unit/child-proxy/HelloClass.ts @@ -2,7 +2,7 @@ import { tokens } from '@stryker-mutator/api/plugin'; export class HelloClass { public static inject = tokens('name'); - constructor(public name: string) { } + constructor(public name: string) {} public sayHello() { return `hello from ${this.name}`; } diff --git a/packages/core/test/unit/config/ConfigEditorApplier.spec.ts b/packages/core/test/unit/config/ConfigEditorApplier.spec.ts index ad876be1c1..c3dc616d7c 100644 --- a/packages/core/test/unit/config/ConfigEditorApplier.spec.ts +++ b/packages/core/test/unit/config/ConfigEditorApplier.spec.ts @@ -13,7 +13,7 @@ describe('ConfigEditorApplier', () => { beforeEach(() => { pluginCreatorMock = sinon.createStubInstance(PluginCreator); sut = testInjector.injector - .provideValue(coreTokens.pluginCreatorConfigEditor, pluginCreatorMock as unknown as PluginCreator) + .provideValue(coreTokens.pluginCreatorConfigEditor, (pluginCreatorMock as unknown) as PluginCreator) .injectClass(ConfigEditorApplier); }); @@ -24,11 +24,12 @@ describe('ConfigEditorApplier', () => { const configEditorPlugins = [{ name: 'fooConfigEditorPlugin' }, { name: 'barConfigEditorPlugin' }]; testInjector.pluginResolver.resolveAll.returns(configEditorPlugins); pluginCreatorMock.create - .withArgs(configEditorPlugins[0].name).returns(fooConfigEditor) - .withArgs(configEditorPlugins[1].name).returns(barConfigEditor); + .withArgs(configEditorPlugins[0].name) + .returns(fooConfigEditor) + .withArgs(configEditorPlugins[1].name) + .returns(barConfigEditor); sut.edit(config); expect(fooConfigEditor.edit).calledWith(config); expect(barConfigEditor.edit).calledWith(config); }); - }); diff --git a/packages/core/test/unit/config/ConfigValidator.spec.ts b/packages/core/test/unit/config/ConfigValidator.spec.ts index 368f3b27f5..35b8aaed30 100644 --- a/packages/core/test/unit/config/ConfigValidator.spec.ts +++ b/packages/core/test/unit/config/ConfigValidator.spec.ts @@ -6,7 +6,6 @@ import ConfigValidator from '../../../src/config/ConfigValidator'; import { coreTokens } from '../../../src/di'; describe('ConfigValidator', () => { - let sut: ConfigValidator; function breakConfig(key: keyof StrykerOptions, value: any): void { @@ -14,9 +13,7 @@ describe('ConfigValidator', () => { } function createSut(testFramework: TestFramework | null = factory.testFramework()) { - return testInjector.injector - .provideValue(coreTokens.testFramework, testFramework) - .injectClass(ConfigValidator); + return testInjector.injector.provideValue(coreTokens.testFramework, testFramework).injectClass(ConfigValidator); } beforeEach(() => { @@ -34,11 +31,12 @@ describe('ConfigValidator', () => { testInjector.options.coverageAnalysis = 'perTest'; sut = createSut(null); actValidationError(); - expect(testInjector.logger.fatal).calledWith('Configured coverage analysis "perTest" requires there to be a testFramework configured. Either configure a testFramework or set coverageAnalysis to "all" or "off".'); + expect(testInjector.logger.fatal).calledWith( + 'Configured coverage analysis "perTest" requires there to be a testFramework configured. Either configure a testFramework or set coverageAnalysis to "all" or "off".' + ); }); describe('thresholds', () => { - it('should be invalid with thresholds < 0 or > 100', () => { testInjector.options.thresholds.high = -1; testInjector.options.thresholds.low = 101; @@ -61,15 +59,19 @@ describe('ConfigValidator', () => { testInjector.options.transpilers.push('a second transpiler'); testInjector.options.coverageAnalysis = 'all'; actValidationError(); - expect(testInjector.logger.fatal).calledWith('Value "all" for `coverageAnalysis` is invalid with multiple transpilers' + - ' (configured transpilers: a transpiler, a second transpiler). Please report this to the Stryker team' + - ' if you whish this feature to be implemented'); + expect(testInjector.logger.fatal).calledWith( + 'Value "all" for `coverageAnalysis` is invalid with multiple transpilers' + + ' (configured transpilers: a transpiler, a second transpiler). Please report this to the Stryker team' + + ' if you whish this feature to be implemented' + ); }); it('should be invalid with invalid logLevel', () => { testInjector.options.logLevel = 'thisTestPasses' as any; actValidationError(); - expect(testInjector.logger.fatal).calledWith('Value "thisTestPasses" is invalid for `logLevel`. Expected one of the following: "fatal", "error", "warn", "info", "debug", "trace", "off"'); + expect(testInjector.logger.fatal).calledWith( + 'Value "thisTestPasses" is invalid for `logLevel`. Expected one of the following: "fatal", "error", "warn", "info", "debug", "trace", "off"' + ); }); it('should be invalid with nonnumeric timeoutMS', () => { @@ -158,10 +160,7 @@ describe('ConfigValidator', () => { }); it('should be invalid with non-string array elements', () => { - breakConfig('reporters', [ - 'stryker-jest', - 0 - ]); + breakConfig('reporters', ['stryker-jest', 0]); actValidationError(); expect(testInjector.logger.fatal).calledWith('Value "0" is an invalid element of `reporters`. Expected a string'); }); @@ -175,10 +174,7 @@ describe('ConfigValidator', () => { }); it('should be invalid with non-string array elements', () => { - breakConfig('transpilers', [ - 'stryker-jest', - 0 - ]); + breakConfig('transpilers', ['stryker-jest', 0]); actValidationError(); expect(testInjector.logger.fatal).calledWith('Value "0" is an invalid element of `transpilers`. Expected a string'); }); @@ -187,11 +183,12 @@ describe('ConfigValidator', () => { it('should be invalid with invalid coverageAnalysis', () => { breakConfig('coverageAnalysis', 'invalid'); actValidationError(); - expect(testInjector.logger.fatal).calledWith('Value "invalid" is invalid for `coverageAnalysis`. Expected one of the following: "perTest", "all", "off"'); + expect(testInjector.logger.fatal).calledWith( + 'Value "invalid" is invalid for `coverageAnalysis`. Expected one of the following: "perTest", "all", "off"' + ); }); function actValidationError() { expect(() => sut.validate()).throws('Stryker could not recover from this configuration error, see fatal log message(s) above.'); } - }); diff --git a/packages/core/test/unit/di/PluginCreator.spec.ts b/packages/core/test/unit/di/PluginCreator.spec.ts index 23d712a53b..0ca4c4f841 100644 --- a/packages/core/test/unit/di/PluginCreator.spec.ts +++ b/packages/core/test/unit/di/PluginCreator.spec.ts @@ -7,11 +7,10 @@ describe('PluginCreator', () => { let sut: PluginCreator; beforeEach(() => { - sut = testInjector.injector - .injectFunction(PluginCreator.createFactory(PluginKind.Reporter)); + sut = testInjector.injector.injectFunction(PluginCreator.createFactory(PluginKind.Reporter)); }); - it('should create a FactoryPlugin using it\'s factory method', () => { + it("should create a FactoryPlugin using it's factory method", () => { // Arrange const expectedReporter = factory.reporter('fooReporter'); const factoryPlugin: FactoryPlugin = { @@ -31,10 +30,9 @@ describe('PluginCreator', () => { expect(actualReporter).eq(expectedReporter); }); - it('should create a ClassPlugin using it\'s constructor', () => { + it("should create a ClassPlugin using it's constructor", () => { // Arrange - class FooReporter { - } + class FooReporter {} const plugin: ClassPlugin = { injectableClass: FooReporter, kind: PluginKind.Reporter, @@ -52,7 +50,6 @@ describe('PluginCreator', () => { it('should throw if plugin is not recognized', () => { testInjector.pluginResolver.resolve.returns({}); - expect(() => sut.create('foo')) - .throws('Plugin "Reporter:foo" could not be created, missing "factory" or "injectableClass" property.'); + expect(() => sut.create('foo')).throws('Plugin "Reporter:foo" could not be created, missing "factory" or "injectableClass" property.'); }); }); diff --git a/packages/core/test/unit/di/PluginLoaderSpec.ts b/packages/core/test/unit/di/PluginLoaderSpec.ts index 13aaec1028..b0e0f66271 100644 --- a/packages/core/test/unit/di/PluginLoaderSpec.ts +++ b/packages/core/test/unit/di/PluginLoaderSpec.ts @@ -8,7 +8,6 @@ import { PluginLoader } from '../../../src/di/PluginLoader'; import * as fileUtils from '../../../src/utils/fileUtils'; describe('PluginLoader', () => { - let sut: PluginLoader; let sandbox: sinon.SinonSandbox; let importModuleStub: sinon.SinonStub; @@ -21,9 +20,7 @@ describe('PluginLoader', () => { }); function createSut(pluginDescriptors: string[]) { - return testInjector.injector - .provideValue(coreTokens.pluginDescriptors, pluginDescriptors) - .injectClass(PluginLoader); + return testInjector.injector.provideValue(coreTokens.pluginDescriptors, pluginDescriptors).injectClass(PluginLoader); } describe('without wildcards', () => { @@ -32,7 +29,6 @@ describe('PluginLoader', () => { }); describe('load()', () => { - describe('without errors', () => { beforeEach(() => { sut.load(); @@ -55,19 +51,16 @@ describe('PluginLoader', () => { expect(testInjector.logger.warn).to.have.been.calledWithMatch(/Error during loading/); }); }); - }); }); describe('with wildcard resolving to "util", "api", "core", "jasmine-framework" and "karma-runner"', () => { - beforeEach(() => { sut = createSut(['@stryker-mutator/*']); pluginDirectoryReadMock.returns(['util', 'api', 'core', 'jasmine-framework', 'karma-runner']); }); describe('load()', () => { - beforeEach(() => { sut.load(); }); @@ -78,11 +71,12 @@ describe('PluginLoader', () => { it('should load "@stryker-mutator/jasmine-framework" and "@stryker-mutator/karma-runner"', () => { expect(fileUtils.importModule).calledTwice; - expect(fileUtils.importModule).calledWithMatch(path.resolve(__dirname, '..', '..', '..', '..', '..', '@stryker-mutator', 'jasmine-framework')); + expect(fileUtils.importModule).calledWithMatch( + path.resolve(__dirname, '..', '..', '..', '..', '..', '@stryker-mutator', 'jasmine-framework') + ); expect(fileUtils.importModule).calledWithMatch(path.resolve(__dirname, '..', '..', '..', '..', '..', '@stryker-mutator', 'karma-runner')); }); }); - }); afterEach(() => { diff --git a/packages/core/test/unit/di/buildMainInjector.spec.ts b/packages/core/test/unit/di/buildMainInjector.spec.ts index b417a2c993..06bba181de 100644 --- a/packages/core/test/unit/di/buildMainInjector.spec.ts +++ b/packages/core/test/unit/di/buildMainInjector.spec.ts @@ -15,7 +15,6 @@ import TestFrameworkOrchestrator, * as testFrameworkOrchestratorModule from '../ import currentLogMock from '../../helpers/logMock'; describe(buildMainInjector.name, () => { - let testFrameworkOrchestratorMock: sinon.SinonStubbedInstance; let pluginLoaderMock: sinon.SinonStubbedInstance; let testFrameworkMock: TestFramework; @@ -52,7 +51,6 @@ describe(buildMainInjector.name, () => { } describe('resolve options', () => { - it('should supply readonly stryker options', () => { const actualOptions = buildMainInjector({}).resolve(commonTokens.options); expect(actualOptions).frozen; diff --git a/packages/core/test/unit/initializer/Presets.spec.ts b/packages/core/test/unit/initializer/Presets.spec.ts index 0744a7c844..a890be2aea 100644 --- a/packages/core/test/unit/initializer/Presets.spec.ts +++ b/packages/core/test/unit/initializer/Presets.spec.ts @@ -24,12 +24,12 @@ describe('Presets', () => { it('should mutate typescript', async () => { const config = await angularPreset.createConfig(); - expect(config.config).to.contain(`mutator: 'typescript'`); + expect(config.config).to.contain("mutator: 'typescript'"); }); it('should use the angular-cli', async () => { const config = await angularPreset.createConfig(); - expect(config.config).to.contain(`projectType: 'angular-cli'`); + expect(config.config).to.contain("projectType: 'angular-cli'"); }); }); @@ -49,7 +49,7 @@ describe('Presets', () => { choice: 'TSX' }); const config = await reactPreset.createConfig(); - expect(config.config).to.contain(`mutator: 'typescript'`); + expect(config.config).to.contain("mutator: 'typescript'"); }); it('should install @stryker-mutator/typescript when TSX is chosen', async () => { @@ -65,7 +65,7 @@ describe('Presets', () => { choice: 'JSX' }); const config = await reactPreset.createConfig(); - expect(config.config).to.include(`mutator: 'javascript'`); + expect(config.config).to.include("mutator: 'javascript'"); }); it('should install @stryker-mutator/javascript-mutator when JSX is chosen', async () => { @@ -94,7 +94,7 @@ describe('Presets', () => { it('should use the vue mutator', async () => { const config = await vueJsPreset.createConfig(); - expect(config.config).to.contain(`mutator: 'vue'`); + expect(config.config).to.contain("mutator: 'vue'"); }); it('should install @stryker-mutator/karma-runner when karma is chosen', async () => { diff --git a/packages/core/test/unit/initializer/StrykerInitializer.spec.ts b/packages/core/test/unit/initializer/StrykerInitializer.spec.ts index 3060c4fecc..ece60b2e15 100644 --- a/packages/core/test/unit/initializer/StrykerInitializer.spec.ts +++ b/packages/core/test/unit/initializer/StrykerInitializer.spec.ts @@ -43,9 +43,9 @@ describe(StrykerInitializer.name, () => { restClientSearch = sinon.createStubInstance(RestClient); restClientPackage = sinon.createStubInstance(RestClient); sut = testInjector.injector - .provideValue(initializerTokens.out, out as unknown as typeof console.log) - .provideValue(initializerTokens.restClientNpm, restClientPackage as unknown as RestClient) - .provideValue(initializerTokens.restClientNpmSearch, restClientSearch as unknown as RestClient) + .provideValue(initializerTokens.out, (out as unknown) as typeof console.log) + .provideValue(initializerTokens.restClientNpm, (restClientPackage as unknown) as RestClient) + .provideValue(initializerTokens.restClientNpmSearch, (restClientSearch as unknown) as RestClient) .provideClass(initializerTokens.inquirer, StrykerInquirer) .provideClass(initializerTokens.npmClient, NpmClient) .provideValue(initializerTokens.strykerPresets, presets) @@ -54,12 +54,12 @@ describe(StrykerInitializer.name, () => { }); describe('initialize()', () => { - beforeEach(() => { stubTestRunners('@stryker-mutator/awesome-runner', 'stryker-hyper-runner', 'stryker-ghost-runner'); stubTestFrameworks( { name: '@stryker-mutator/awesome-framework', keywords: ['@stryker-mutator/awesome-runner'], version: '1.1.1' }, - { name: 'stryker-hyper-framework', keywords: ['stryker-hyper-runner'], version: '1.1.1' }); + { name: 'stryker-hyper-framework', keywords: ['stryker-hyper-runner'], version: '1.1.1' } + ); stubMutators('@stryker-mutator/typescript', '@stryker-mutator/javascript-mutator'); stubTranspilers('@stryker-mutator/typescript', '@stryker-mutator/webpack'); stubReporters('stryker-dimension-reporter', '@stryker-mutator/mars-reporter'); @@ -93,16 +93,16 @@ describe(StrykerInitializer.name, () => { }); await sut.initialize(); expect(inquirerPrompt).callCount(7); - const [promptPreset, promptTestRunner, promptTestFramework, promptMutator, promptPackageManagers]: inquirer.ListQuestion[] = [ + const [promptPreset, promptTestRunner, promptTestFramework, promptMutator, promptPackageManagers]: Array> = [ inquirerPrompt.getCall(0).args[0], inquirerPrompt.getCall(1).args[0], inquirerPrompt.getCall(2).args[0], inquirerPrompt.getCall(3).args[0], - inquirerPrompt.getCall(6).args[0], + inquirerPrompt.getCall(6).args[0] ]; - const [promptTranspilers, promptReporters]: inquirer.CheckboxQuestion[] = [ + const [promptTranspilers, promptReporters]: Array> = [ inquirerPrompt.getCall(4).args[0], - inquirerPrompt.getCall(5).args[0], + inquirerPrompt.getCall(5).args[0] ]; expect(promptPreset.type).to.eq('list'); expect(promptPreset.name).to.eq('preset'); @@ -130,8 +130,10 @@ describe(StrykerInitializer.name, () => { resolvePresetConfig(); await sut.initialize(); expect(inquirerPrompt).callCount(2); - expect(out).calledWith('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.'); - expect(out).calledWith('Let\'s kill some mutants with this command: `stryker run`'); + expect(out).calledWith( + 'Done configuring stryker. Please review `stryker.conf.js`, you might need to configure transpilers or your test runner correctly.' + ); + expect(out).calledWith("Let's kill some mutants with this command: `stryker run`"); }); it('should correctly write and format the stryker configuration file', async () => { @@ -198,7 +200,7 @@ describe(StrykerInitializer.name, () => { }); await sut.initialize(); expect(inquirerPrompt).callCount(2); - const [promptPreset, promptPackageManager]: inquirer.ListQuestion[] = [ + const [promptPreset, promptPackageManager]: Array> = [ inquirerPrompt.getCall(0).args[0], inquirerPrompt.getCall(1).args[0] ]; @@ -240,8 +242,10 @@ describe(StrykerInitializer.name, () => { }); await sut.initialize(); expect(out).calledWith('Installing NPM dependencies...'); - expect(childExecSync).calledWith('npm i --save-dev @stryker-mutator/awesome-runner @stryker-mutator/awesome-framework @stryker-mutator/typescript @stryker-mutator/webpack stryker-dimension-reporter @stryker-mutator/mars-reporter', - { stdio: [0, 1, 2] }); + expect(childExecSync).calledWith( + 'npm i --save-dev @stryker-mutator/awesome-runner @stryker-mutator/awesome-framework @stryker-mutator/typescript @stryker-mutator/webpack stryker-dimension-reporter @stryker-mutator/mars-reporter', + { stdio: [0, 1, 2] } + ); }); it('should configure testFramework, testRunner, mutator, transpilers, reporters, and packageManager', async () => { @@ -254,15 +258,18 @@ describe(StrykerInitializer.name, () => { transpilers: ['webpack'] }); await sut.initialize(); - const matchNormalized = (expected: string) => sinon.match((actual: string) => - normalizeWhitespaces(actual).indexOf(normalizeWhitespaces(expected)) > -1); - expect(fsAsPromised.writeFile).calledWith('stryker.conf.js', matchNormalized('"testRunner": "awesome"') - .and(matchNormalized('"testFramework": "awesome"')) - .and(matchNormalized('"packageManager": "npm"')) - .and(matchNormalized('"coverageAnalysis": "perTest"')) - .and(matchNormalized('"mutator": "typescript"')) - .and(matchNormalized('"transpilers": [ "webpack" ]')) - .and(matchNormalized(`"dimension", "mars", "progress"`))); + const matchNormalized = (expected: string) => + sinon.match((actual: string) => normalizeWhitespaces(actual).includes(normalizeWhitespaces(expected))); + expect(fsAsPromised.writeFile).calledWith( + 'stryker.conf.js', + matchNormalized('"testRunner": "awesome"') + .and(matchNormalized('"testFramework": "awesome"')) + .and(matchNormalized('"packageManager": "npm"')) + .and(matchNormalized('"coverageAnalysis": "perTest"')) + .and(matchNormalized('"mutator": "typescript"')) + .and(matchNormalized('"transpilers": [ "webpack" ]')) + .and(matchNormalized('"dimension", "mars", "progress"')) + ); }); it('should configure the additional settings from the plugins', async () => { @@ -280,13 +287,14 @@ describe(StrykerInitializer.name, () => { }); describe('but no testFramework can be found that supports the testRunner', () => { - - beforeEach(() => inquirerPrompt.resolves({ - packageManager: 'npm', - reporters: ['dimension', 'mars'], - testRunner: 'ghost', - transpilers: ['webpack'] - })); + beforeEach(() => + inquirerPrompt.resolves({ + packageManager: 'npm', + reporters: ['dimension', 'mars'], + testRunner: 'ghost', + transpilers: ['webpack'] + }) + ); it('should not prompt for test framework', async () => { await sut.initialize(); @@ -329,13 +337,14 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(out).calledWith('An error occurred during installation, please try it yourself: "npm i --save-dev stryker-ghost-runner @stryker-mutator/webpack-transpiler"'); + expect(out).calledWith( + 'An error occurred during installation, please try it yourself: "npm i --save-dev stryker-ghost-runner @stryker-mutator/webpack-transpiler"' + ); expect(fsAsPromised.writeFile).called; }); }); describe('initialize() when no internet', () => { - it('should log error and continue when fetching test runners', async () => { restClientSearch.get.withArgs('/v2/search?q=keywords:@stryker-mutator/test-runner-plugin').rejects(); stubMutators('stryker-javascript'); @@ -350,7 +359,9 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(testInjector.logger.error).calledWith('Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/test-runner-plugin). Please check your internet connection.'); + expect(testInjector.logger.error).calledWith( + 'Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/test-runner-plugin). Please check your internet connection.' + ); expect(out).calledWith('Unable to select a test runner. You will need to configure it manually.'); expect(fsAsPromised.writeFile).called; }); @@ -371,7 +382,9 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(testInjector.logger.error).calledWith('Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/test-framework-plugin). Please check your internet connection.'); + expect(testInjector.logger.error).calledWith( + 'Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/test-framework-plugin). Please check your internet connection.' + ); expect(out).calledWith('No stryker test framework plugin found that is compatible with awesome, downgrading coverageAnalysis to "all"'); expect(fsAsPromised.writeFile).called; }); @@ -392,7 +405,9 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(testInjector.logger.error).calledWith('Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/mutator-plugin). Please check your internet connection.'); + expect(testInjector.logger.error).calledWith( + 'Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/mutator-plugin). Please check your internet connection.' + ); expect(out).calledWith('Unable to select a mutator. You will need to configure it manually.'); expect(fsAsPromised.writeFile).called; }); @@ -412,7 +427,9 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(testInjector.logger.error).calledWith('Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/transpiler-plugin). Please check your internet connection.'); + expect(testInjector.logger.error).calledWith( + 'Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/transpiler-plugin). Please check your internet connection.' + ); expect(out).calledWith('Unable to select transpilers. You will need to configure it manually, if you want to use any.'); expect(fsAsPromised.writeFile).called; }); @@ -433,7 +450,9 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(testInjector.logger.error).calledWith('Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/reporter-plugin). Please check your internet connection.'); + expect(testInjector.logger.error).calledWith( + 'Unable to reach npms.io (for query /v2/search?q=keywords:@stryker-mutator/reporter-plugin). Please check your internet connection.' + ); expect(fsAsPromised.writeFile).called; }); @@ -453,17 +472,20 @@ describe(StrykerInitializer.name, () => { await sut.initialize(); - expect(testInjector.logger.warn).calledWith('Could not fetch additional initialization config for dependency stryker-awesome-runner. You might need to configure it manually'); + expect(testInjector.logger.warn).calledWith( + 'Could not fetch additional initialization config for dependency stryker-awesome-runner. You might need to configure it manually' + ); expect(fsAsPromised.writeFile).called; }); - }); it('should log an error and quit when `stryker.conf.js` file already exists', async () => { fsExistsSync.resolves(true); expect(sut.initialize()).to.be.rejected; - expect(testInjector.logger.error).calledWith('Stryker config file "stryker.conf.js" already exists in the current directory. Please remove it and try again.'); + expect(testInjector.logger.error).calledWith( + 'Stryker config file "stryker.conf.js" already exists in the current directory. Please remove it and try again.' + ); }); const stubTestRunners = (...testRunners: string[]) => { @@ -510,12 +532,12 @@ describe(StrykerInitializer.name, () => { statusCode: 200 }); }; - const stubPackageClient = (packageConfigPerPackage: { [packageName: string]: object | null; }) => { + const stubPackageClient = (packageConfigPerPackage: { [packageName: string]: object | null }) => { Object.keys(packageConfigPerPackage).forEach(packageName => { const pkgConfig: PackageInfo & { initStrykerConfig?: object } = { keywords: [], name: packageName, - version: '1.1.1', + version: '1.1.1' }; const cfg = packageConfigPerPackage[packageName]; if (cfg) { @@ -539,15 +561,18 @@ describe(StrykerInitializer.name, () => { } function arrangeAnswers(answerOverrides?: Partial) { - const answers: StrykerInitAnswers = Object.assign({ - mutator: 'typescript', - packageManager: 'yarn', - preset: null, - reporters: ['dimension', 'mars'], - testFramework: 'awesome', - testRunner: 'awesome', - transpilers: ['webpack'] - }, answerOverrides); + const answers: StrykerInitAnswers = Object.assign( + { + mutator: 'typescript', + packageManager: 'yarn', + preset: null, + reporters: ['dimension', 'mars'], + testFramework: 'awesome', + testRunner: 'awesome', + transpilers: ['webpack'] + }, + answerOverrides + ); inquirerPrompt.resolves(answers); } @@ -561,8 +586,10 @@ describe(StrykerInitializer.name, () => { } function expectStrykerConfWritten(expectedRawConfig: string) { - expect(fsWriteFile).calledWithMatch(sinon.match('stryker.conf.js'), sinon.match((actualConf: string) => - normalizeWhitespaces(expectedRawConfig) === normalizeWhitespaces(actualConf))); + expect(fsWriteFile).calledWithMatch( + sinon.match('stryker.conf.js'), + sinon.match((actualConf: string) => normalizeWhitespaces(expectedRawConfig) === normalizeWhitespaces(actualConf)) + ); fsWriteFile.getCall(0); } }); diff --git a/packages/core/test/unit/input/InputFileResolver.spec.ts b/packages/core/test/unit/input/InputFileResolver.spec.ts index 833b075a03..5c24bbdd8c 100644 --- a/packages/core/test/unit/input/InputFileResolver.spec.ts +++ b/packages/core/test/unit/input/InputFileResolver.spec.ts @@ -14,11 +14,8 @@ import BroadcastReporter from '../../../src/reporters/BroadcastReporter'; import * as fileUtils from '../../../src/utils/fileUtils'; import { Mock, mock } from '../../helpers/producers'; -const files = (...namesWithContent: [string, string][]): File[] => - namesWithContent.map((nameAndContent): File => new File( - path.resolve(nameAndContent[0]), - Buffer.from(nameAndContent[1]) - )); +const files = (...namesWithContent: Array<[string, string]>): File[] => + namesWithContent.map((nameAndContent): File => new File(path.resolve(nameAndContent[0]), Buffer.from(nameAndContent[1]))); describe(InputFileResolver.name, () => { let globStub: sinon.SinonStub; @@ -30,14 +27,22 @@ describe(InputFileResolver.name, () => { beforeEach(() => { reporterMock = mock(BroadcastReporter); globStub = sinon.stub(fileUtils, 'glob'); - readFileStub = sinon.stub(fsAsPromised, 'readFile') - .withArgs(sinon.match.string).resolves(Buffer.from('')) // fallback - .withArgs(sinon.match.string).resolves(Buffer.from('')) // fallback - .withArgs(sinon.match('file1')).resolves(Buffer.from('file 1 content')) - .withArgs(sinon.match('file2')).resolves(Buffer.from('file 2 content')) - .withArgs(sinon.match('file3')).resolves(Buffer.from('file 3 content')) - .withArgs(sinon.match('mute1')).resolves(Buffer.from('mutate 1 content')) - .withArgs(sinon.match('mute2')).resolves(Buffer.from('mutate 2 content')); + readFileStub = sinon + .stub(fsAsPromised, 'readFile') + .withArgs(sinon.match.string) + .resolves(Buffer.from('')) // fallback + .withArgs(sinon.match.string) + .resolves(Buffer.from('')) // fallback + .withArgs(sinon.match('file1')) + .resolves(Buffer.from('file 1 content')) + .withArgs(sinon.match('file2')) + .resolves(Buffer.from('file 2 content')) + .withArgs(sinon.match('file3')) + .resolves(Buffer.from('file 3 content')) + .withArgs(sinon.match('mute1')) + .resolves(Buffer.from('mutate 1 content')) + .withArgs(sinon.match('mute2')) + .resolves(Buffer.from('mutate 2 content')); globStub.withArgs('mute*').resolves(['/mute1.js', '/mute2.js']); globStub.withArgs('mute1').resolves(['/mute1.js']); globStub.withArgs('mute2').resolves(['/mute2.js']); @@ -59,8 +64,9 @@ describe(InputFileResolver.name, () => { `) }); const result = await sut.resolve(); - expect(childProcessExecStub).calledWith('git ls-files --others --exclude-standard --cached --exclude /.stryker-tmp/*', - { maxBuffer: 10 * 1000 * 1024 }); + expect(childProcessExecStub).calledWith('git ls-files --others --exclude-standard --cached --exclude /.stryker-tmp/*', { + maxBuffer: 10 * 1000 * 1024 + }); expect(result.files.map(file => file.name)).deep.eq([path.resolve('file1.js'), path.resolve('foo/bar/baz.ts')]); }); @@ -77,24 +83,27 @@ describe(InputFileResolver.name, () => { // Assert expect(childProcessExecStub).calledWith('git ls-files --others --exclude-standard --cached --exclude /foo-bar/*'); - }); + }); it('should reject if there is no `files` array and `git ls-files` command fails', () => { const expectedError = new Error('fatal: Not a git repository (or any of the parent directories): .git'); childProcessExecStub.rejects(expectedError); - return expect(createSut().resolve()) - .rejectedWith(`Cannot determine input files. Either specify a \`files\` array in your stryker configuration, or make sure "${process.cwd() - }" is located inside a git repository. Inner error: ${ - errorToString(expectedError) - }`); + return expect(createSut().resolve()).rejectedWith( + `Cannot determine input files. Either specify a \`files\` array in your stryker configuration, or make sure "${process.cwd()}" is located inside a git repository. Inner error: ${errorToString( + expectedError + )}` + ); }); it('should log a warning if no files were resolved', async () => { testInjector.options.files = []; sut = createSut(); await sut.resolve(); - expect(testInjector.logger.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'))); + expect(testInjector.logger.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 () => { @@ -124,23 +133,19 @@ describe(InputFileResolver.name, () => { }); describe('with mutate file expressions', () => { - it('should result in the expected mutate files', async () => { testInjector.options.mutate = ['mute*']; testInjector.options.files = ['file1', 'mute1', 'file2', 'mute2', 'file3']; sut = createSut(); const result = await sut.resolve(); - expect(result.filesToMutate.map(_ => _.name)).to.deep.equal([ - path.resolve('/mute1.js'), - path.resolve('/mute2.js') - ]); + expect(result.filesToMutate.map(_ => _.name)).to.deep.equal([path.resolve('/mute1.js'), path.resolve('/mute2.js')]); expect(result.files.map(file => file.name)).to.deep.equal([ path.resolve('/file1.js'), path.resolve('/mute1.js'), path.resolve('/file2.js'), path.resolve('/mute2.js'), - path.resolve('/file3.js')] - ); + path.resolve('/file3.js') + ]); }); it('should only report a mutate file when it is included in the resolved files', async () => { @@ -148,9 +153,7 @@ describe(InputFileResolver.name, () => { testInjector.options.files = ['file1', 'mute1', 'file2', /*'mute2'*/ 'file3']; sut = createSut(); const result = await sut.resolve(); - expect(result.filesToMutate.map(_ => _.name)).to.deep.equal([ - path.resolve('/mute1.js') - ]); + expect(result.filesToMutate.map(_ => _.name)).to.deep.equal([path.resolve('/mute1.js')]); }); it('should report OnAllSourceFilesRead', async () => { @@ -185,7 +188,6 @@ describe(InputFileResolver.name, () => { }); describe('without mutate files', () => { - beforeEach(() => { testInjector.options.files = ['file1', 'mute1']; sut = createSut(); @@ -193,7 +195,9 @@ describe(InputFileResolver.name, () => { it('should warn about dry-run', async () => { await sut.resolve(); - expect(testInjector.logger.warn).calledWith(sinon.match('No files marked to be mutated, Stryker will perform a dry-run without actually mutating anything.')); + expect(testInjector.logger.warn).calledWith( + sinon.match('No files marked to be mutated, Stryker will perform a dry-run without actually mutating anything.') + ); }); }); @@ -212,7 +216,6 @@ describe(InputFileResolver.name, () => { }); describe('when a globbing expression does not result in a result', () => { - it('should log a warning', async () => { testInjector.options.files = ['file1', 'notExists']; testInjector.options.mutate = ['file1']; @@ -226,7 +229,7 @@ describe(InputFileResolver.name, () => { testInjector.options.files = config.files; testInjector.options.mutate = config.mutate; sut = createSut(); - childProcessExecStub.resolves({ stdout: Buffer.from(`src/foobar.js`) }); + childProcessExecStub.resolves({ stdout: Buffer.from('src/foobar.js') }); globStub.withArgs(config.mutate[0]).returns(['src/foobar.js']); await sut.resolve(); expect(testInjector.logger.warn).not.called; @@ -243,7 +246,6 @@ describe(InputFileResolver.name, () => { }); describe('when excluding files with "!"', () => { - it('should exclude the files that were previously included', async () => { testInjector.options.files = ['file2', 'file1', '!file2']; const sut = createSut(); @@ -267,7 +269,6 @@ describe(InputFileResolver.name, () => { }); describe('when provided duplicate files', () => { - it('should deduplicate files that occur more than once', async () => { testInjector.options.files = ['file2', 'file2']; const result = await createSut().resolve(); @@ -287,7 +288,7 @@ describe(InputFileResolver.name, () => { }); }); - function assertFilesEqual(actual: ReadonlyArray, expected: ReadonlyArray) { + function assertFilesEqual(actual: readonly File[], expected: readonly File[]) { expect(actual).lengthOf(expected.length); for (const index in actual) { expect(actual[index].name).eq(expected[index].name); @@ -296,8 +297,6 @@ describe(InputFileResolver.name, () => { } function createSut() { - return testInjector.injector - .provideValue(coreTokens.reporter, reporterMock) - .injectClass(InputFileResolver); + return testInjector.injector.provideValue(coreTokens.reporter, reporterMock).injectClass(InputFileResolver); } }); diff --git a/packages/core/test/unit/logging/LogConfigurator.spec.ts b/packages/core/test/unit/logging/LogConfigurator.spec.ts index 38313898f1..58c3479a7d 100644 --- a/packages/core/test/unit/logging/LogConfigurator.spec.ts +++ b/packages/core/test/unit/logging/LogConfigurator.spec.ts @@ -7,7 +7,6 @@ import LoggingClientContext from '../../../src/logging/LoggingClientContext'; import * as netUtils from '../../../src/utils/netUtils'; describe('LogConfigurator', () => { - const sut = LogConfigurator; let getFreePortStub: sinon.SinonStub; let log4jsConfigure: sinon.SinonStub; @@ -79,7 +78,6 @@ describe('LogConfigurator', () => { expect(getFreePortStub).called; expect(actualLoggingContext).deep.eq(expectedLoggingContext); }); - }); describe('configureChildProcess', () => { @@ -106,12 +104,17 @@ describe('LogConfigurator', () => { }); }); - function createMasterConfig(consoleLevel: LogLevel, fileLevel: LogLevel, defaultLevel: LogLevel, allowConsoleColors: boolean): log4js.Configuration { + function createMasterConfig( + consoleLevel: LogLevel, + fileLevel: LogLevel, + defaultLevel: LogLevel, + allowConsoleColors: boolean + ): log4js.Configuration { const coloredLayout: log4js.PatternLayout = { pattern: '%[%r (%z) %p %c%] %m', type: 'pattern' }; - const notColoredLayout: log4js.PatternLayout = { + const notColoredLayout: log4js.PatternLayout = { pattern: '%r (%z) %p %c %m', type: 'pattern' }; @@ -122,7 +125,7 @@ describe('LogConfigurator', () => { all: { type: require.resolve('../../../src/logging/MultiAppender'), appenders: ['filteredConsoleLevel', 'filteredFile'] }, console: { type: 'stdout', layout: consoleLayout }, file: { type: 'file', layout: notColoredLayout, filename: 'stryker.log' }, - filteredConsoleCategory: {type: 'categoryFilter', appender: 'console', exclude: 'log4js' }, + filteredConsoleCategory: { type: 'categoryFilter', appender: 'console', exclude: 'log4js' }, filteredConsoleLevel: { type: 'logLevelFilter', appender: 'filteredConsoleCategory', level: consoleLevel }, filteredFile: { type: 'logLevelFilter', appender: 'file', level: fileLevel } }, diff --git a/packages/core/test/unit/mutants/MutantTestMatcher.spec.ts b/packages/core/test/unit/mutants/MutantTestMatcher.spec.ts index 86dfbbcfdc..afd50aa26f 100644 --- a/packages/core/test/unit/mutants/MutantTestMatcher.spec.ts +++ b/packages/core/test/unit/mutants/MutantTestMatcher.spec.ts @@ -19,7 +19,6 @@ import { MappedLocation, PassThroughSourceMapper } from '../../../src/transpiler import { Mock, mock } from '../../helpers/producers'; describe(MutantTestMatcher.name, () => { - let sut: MutantTestMatcher; let mutants: Mutant[]; let initialRunResult: InitialTestRunResult; @@ -34,7 +33,7 @@ describe(MutantTestMatcher.name, () => { coverageMaps: fileCoverageDictionary, overheadTimeMS: 42, runResult: { tests: [], status: RunStatus.Complete }, - sourceMapper: new PassThroughSourceMapper(), + sourceMapper: new PassThroughSourceMapper() }; reporter = mock(BroadcastReporter); input = new InputFileCollection( @@ -45,7 +44,6 @@ describe(MutantTestMatcher.name, () => { }); describe('with coverageAnalysis: "perTest"', () => { - beforeEach(() => { testInjector.options.coverageAnalysis = 'perTest'; sut = createSut(); @@ -63,7 +61,7 @@ describe(MutantTestMatcher.name, () => { fileName: 'fileWithMutantOne', mutatorName: 'myMutator', range: [9, 9], // line 4:5 -> line 4:5 - replacement: '>', + replacement: '>' // location: { start: { line: 4, column: 5 }, end: { line: 4, column: 5 } }, }; @@ -91,7 +89,6 @@ describe(MutantTestMatcher.name, () => { }); describe('without code coverage info', () => { - it('should add both tests to the mutants and report failure', async () => { const expectedTestSelection = [{ id: 0, name: 'test one' }, { id: 1, name: 'test two' }]; @@ -101,7 +98,10 @@ describe(MutantTestMatcher.name, () => { expect(result[1].selectedTests).deep.eq(expectedTestSelection); expect(TestSelectionResult[result[0].testSelectionResult]).eq(TestSelectionResult[TestSelectionResult.FailedButAlreadyReported]); expect(TestSelectionResult[result[1].testSelectionResult]).eq(TestSelectionResult[TestSelectionResult.FailedButAlreadyReported]); - expect(testInjector.logger.warn).calledWith('No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', 'perTest'); + expect(testInjector.logger.warn).calledWith( + 'No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', + 'perTest' + ); }); it('should have both mutants matched', async () => { @@ -131,7 +131,6 @@ describe(MutantTestMatcher.name, () => { }); describe('without the tests having covered the mutants', () => { - beforeEach(() => { const covCollectionPerFile: CoveragePerTestResult = { baseline: {}, @@ -150,7 +149,8 @@ describe(MutantTestMatcher.name, () => { fileCoverageDictionary.anOtherFile = { fnMap: {}, statementMap: { - 1: { // covers but in wrong src file + 1: { + // covers but in wrong src file end: { line: 4, column: 7 }, start: { line: 4, column: 0 } } @@ -167,7 +167,8 @@ describe(MutantTestMatcher.name, () => { end: { line: 4, column: 9 }, start: { line: 4, column: 0 } }, - 3: { // Smallest statement that surrounds the mutant. Differs based on column number + 3: { + // Smallest statement that surrounds the mutant. Differs based on column number end: { line: 4, column: 7 }, start: { line: 4, column: 3 } } @@ -180,7 +181,8 @@ describe(MutantTestMatcher.name, () => { end: { line: 9, column: 4 }, start: { line: 0, column: 0 } }, - 2: { // Smallest statement that surround the mutant. Differs based on line number + 2: { + // Smallest statement that surround the mutant. Differs based on line number end: { line: 9, column: 4 }, start: { line: 8, column: 0 } }, @@ -201,7 +203,6 @@ describe(MutantTestMatcher.name, () => { }); describe('with tests having covered the mutants based on statements', () => { - beforeEach(() => { fileCoverageDictionary.fileWithMutantOne = { fnMap: {}, @@ -232,10 +233,7 @@ describe(MutantTestMatcher.name, () => { }); it('should have added the run results to the mutants', async () => { - const expectedTestSelectionFirstMutant: TestSelection[] = [ - { id: 0, name: 'test one' }, - { id: 1, name: 'test two' } - ]; + const expectedTestSelectionFirstMutant: TestSelection[] = [{ id: 0, name: 'test one' }, { id: 1, name: 'test two' }]; const result = await sut.matchWithMutants(mutants); @@ -256,10 +254,7 @@ describe(MutantTestMatcher.name, () => { }); it('should select all test in the test run but not report the error yet', async () => { - const expectedTestSelection: TestSelection[] = [ - { name: 'test one', id: 0 }, - { name: 'test two', id: 1 } - ]; + const expectedTestSelection: TestSelection[] = [{ name: 'test one', id: 0 }, { name: 'test two', id: 1 }]; const result = await sut.matchWithMutants(mutants); @@ -297,7 +292,6 @@ describe(MutantTestMatcher.name, () => { }); describe('with baseline covering a mutant', () => { - beforeEach(() => { fileCoverageDictionary.fileWithMutantOne = { fnMap: {}, @@ -337,13 +331,101 @@ describe(MutantTestMatcher.name, () => { // Arrange const sourceFile = new SourceFile(new File('', '')); sourceFile.getLocation = () => ({ start: { line: 13, column: 38 }, end: { line: 24, column: 5 } }); - const testableMutant = new TestableMutant('1', mutant({ - fileName: 'juice-shop\\app\\js\\controllers\\SearchResultController.js' - }), sourceFile); - - const coverageResult: CoverageCollection = { 'juice-shop\\app\\js\\controllers\\SearchResultController.js': { s: { 1: 1, 2: 1, 3: 1, 4: 0, 5: 1, 6: 0, 7: 0, 8: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0, 14: 0, 15: 0, 16: 0, 17: 0, 18: 0, 19: 0, 20: 0, 21: 0, 22: 0, 23: 0, 24: 0, 25: 0, 26: 0, 27: 0, 28: 0, 29: 0, 30: 0, 31: 0, 32: 1, 33: 1, 34: 1, 35: 1, 36: 0, 37: 0 }, f: {} } }; + const testableMutant = new TestableMutant( + '1', + mutant({ + fileName: 'juice-shop\\app\\js\\controllers\\SearchResultController.js' + }), + sourceFile + ); + + const coverageResult: CoverageCollection = { + 'juice-shop\\app\\js\\controllers\\SearchResultController.js': { + s: { + 1: 1, + 2: 1, + 3: 1, + 4: 0, + 5: 1, + 6: 0, + 7: 0, + 8: 0, + 9: 0, + 10: 0, + 11: 0, + 12: 0, + 13: 0, + 14: 0, + 15: 0, + 16: 0, + 17: 0, + 18: 0, + 19: 0, + 20: 0, + 21: 0, + 22: 0, + 23: 0, + 24: 0, + 25: 0, + 26: 0, + 27: 0, + 28: 0, + 29: 0, + 30: 0, + 31: 0, + 32: 1, + 33: 1, + 34: 1, + 35: 1, + 36: 0, + 37: 0 + }, + f: {} + } + }; - fileCoverageDictionary['juice-shop\\app\\js\\controllers\\SearchResultController.js'] = { statementMap: { 1: { start: { line: 1, column: 0 }, end: { line: 84, column: 5 } }, 2: { start: { line: 13, column: 4 }, end: { line: 24, column: 5 } }, 3: { start: { line: 14, column: 6 }, end: { line: 23, column: 8 } }, 4: { start: { line: 20, column: 12 }, end: { line: 20, column: 21 } }, 5: { start: { line: 26, column: 4 }, end: { line: 72, column: 5 } }, 6: { start: { line: 27, column: 6 }, end: { line: 71, column: 8 } }, 7: { start: { line: 28, column: 8 }, end: { line: 28, column: 51 } }, 8: { start: { line: 29, column: 8 }, end: { line: 29, column: 25 } }, 9: { start: { line: 30, column: 8 }, end: { line: 53, column: 9 } }, 10: { start: { line: 31, column: 10 }, end: { line: 52, column: 11 } }, 11: { start: { line: 32, column: 12 }, end: { line: 32, column: 24 } }, 12: { start: { line: 33, column: 12 }, end: { line: 50, column: 14 } }, 13: { start: { line: 34, column: 14 }, end: { line: 34, column: 68 } }, 14: { start: { line: 35, column: 14 }, end: { line: 47, column: 16 } }, 15: { start: { line: 36, column: 16 }, end: { line: 44, column: 18 } }, 16: { start: { line: 37, column: 18 }, end: { line: 41, column: 20 } }, 17: { start: { line: 38, column: 20 }, end: { line: 38, column: 62 } }, 18: { start: { line: 40, column: 20 }, end: { line: 40, column: 55 } }, 19: { start: { line: 43, column: 18 }, end: { line: 43, column: 34 } }, 20: { start: { line: 46, column: 16 }, end: { line: 46, column: 32 } }, 21: { start: { line: 49, column: 14 }, end: { line: 49, column: 30 } }, 22: { start: { line: 51, column: 12 }, end: { line: 51, column: 17 } }, 23: { start: { line: 54, column: 8 }, end: { line: 68, column: 9 } }, 24: { start: { line: 55, column: 10 }, end: { line: 67, column: 12 } }, 25: { start: { line: 56, column: 12 }, end: { line: 64, column: 14 } }, 26: { start: { line: 57, column: 14 }, end: { line: 61, column: 16 } }, 27: { start: { line: 58, column: 16 }, end: { line: 58, column: 54 } }, 28: { start: { line: 60, column: 16 }, end: { line: 60, column: 51 } }, 29: { start: { line: 63, column: 14 }, end: { line: 63, column: 30 } }, 30: { start: { line: 66, column: 12 }, end: { line: 66, column: 28 } }, 31: { start: { line: 70, column: 8 }, end: { line: 70, column: 24 } }, 32: { start: { line: 74, column: 4 }, end: { line: 74, column: 63 } }, 33: { start: { line: 76, column: 4 }, end: { line: 83, column: 6 } }, 34: { start: { line: 77, column: 6 }, end: { line: 77, column: 37 } }, 35: { start: { line: 78, column: 6 }, end: { line: 80, column: 7 } }, 36: { start: { line: 79, column: 8 }, end: { line: 79, column: 89 } }, 37: { start: { line: 82, column: 6 }, end: { line: 82, column: 22 } } }, fnMap: {} }; + fileCoverageDictionary['juice-shop\\app\\js\\controllers\\SearchResultController.js'] = { + statementMap: { + 1: { start: { line: 1, column: 0 }, end: { line: 84, column: 5 } }, + 2: { start: { line: 13, column: 4 }, end: { line: 24, column: 5 } }, + 3: { start: { line: 14, column: 6 }, end: { line: 23, column: 8 } }, + 4: { start: { line: 20, column: 12 }, end: { line: 20, column: 21 } }, + 5: { start: { line: 26, column: 4 }, end: { line: 72, column: 5 } }, + 6: { start: { line: 27, column: 6 }, end: { line: 71, column: 8 } }, + 7: { start: { line: 28, column: 8 }, end: { line: 28, column: 51 } }, + 8: { start: { line: 29, column: 8 }, end: { line: 29, column: 25 } }, + 9: { start: { line: 30, column: 8 }, end: { line: 53, column: 9 } }, + 10: { start: { line: 31, column: 10 }, end: { line: 52, column: 11 } }, + 11: { start: { line: 32, column: 12 }, end: { line: 32, column: 24 } }, + 12: { start: { line: 33, column: 12 }, end: { line: 50, column: 14 } }, + 13: { start: { line: 34, column: 14 }, end: { line: 34, column: 68 } }, + 14: { start: { line: 35, column: 14 }, end: { line: 47, column: 16 } }, + 15: { start: { line: 36, column: 16 }, end: { line: 44, column: 18 } }, + 16: { start: { line: 37, column: 18 }, end: { line: 41, column: 20 } }, + 17: { start: { line: 38, column: 20 }, end: { line: 38, column: 62 } }, + 18: { start: { line: 40, column: 20 }, end: { line: 40, column: 55 } }, + 19: { start: { line: 43, column: 18 }, end: { line: 43, column: 34 } }, + 20: { start: { line: 46, column: 16 }, end: { line: 46, column: 32 } }, + 21: { start: { line: 49, column: 14 }, end: { line: 49, column: 30 } }, + 22: { start: { line: 51, column: 12 }, end: { line: 51, column: 17 } }, + 23: { start: { line: 54, column: 8 }, end: { line: 68, column: 9 } }, + 24: { start: { line: 55, column: 10 }, end: { line: 67, column: 12 } }, + 25: { start: { line: 56, column: 12 }, end: { line: 64, column: 14 } }, + 26: { start: { line: 57, column: 14 }, end: { line: 61, column: 16 } }, + 27: { start: { line: 58, column: 16 }, end: { line: 58, column: 54 } }, + 28: { start: { line: 60, column: 16 }, end: { line: 60, column: 51 } }, + 29: { start: { line: 63, column: 14 }, end: { line: 63, column: 30 } }, + 30: { start: { line: 66, column: 12 }, end: { line: 66, column: 28 } }, + 31: { start: { line: 70, column: 8 }, end: { line: 70, column: 24 } }, + 32: { start: { line: 74, column: 4 }, end: { line: 74, column: 63 } }, + 33: { start: { line: 76, column: 4 }, end: { line: 83, column: 6 } }, + 34: { start: { line: 77, column: 6 }, end: { line: 77, column: 37 } }, + 35: { start: { line: 78, column: 6 }, end: { line: 80, column: 7 } }, + 36: { start: { line: 79, column: 8 }, end: { line: 79, column: 89 } }, + 37: { start: { line: 82, column: 6 }, end: { line: 82, column: 22 } } + }, + fnMap: {} + }; initialRunResult.runResult.coverage = { baseline: {}, deviations: { 0: coverageResult } }; initialRunResult.runResult.tests.push({ @@ -357,16 +439,17 @@ describe(MutantTestMatcher.name, () => { await sut.enrichWithCoveredTests(testableMutant); // Assert - expect(testableMutant.selectedTests).deep.eq([{ - id: 0, - name: 'controllers SearchResultController should open a modal dialog with product details' - }]); + expect(testableMutant.selectedTests).deep.eq([ + { + id: 0, + name: 'controllers SearchResultController should open a modal dialog with product details' + } + ]); }); }); }); describe('with coverageAnalysis: "all"', () => { - beforeEach(() => { testInjector.options.coverageAnalysis = 'all'; sut = createSut(); @@ -383,11 +466,13 @@ describe(MutantTestMatcher.name, () => { expect(result[1].selectedTests).deep.eq(expectedTestSelection); expect(result[0].testSelectionResult).deep.eq(TestSelectionResult.FailedButAlreadyReported); expect(result[1].testSelectionResult).deep.eq(TestSelectionResult.FailedButAlreadyReported); - expect(testInjector.logger.warn).to.have.been.calledWith('No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', 'all'); + expect(testInjector.logger.warn).to.have.been.calledWith( + 'No coverage result found, even though coverageAnalysis is "%s". Assuming that all tests cover each mutant. This might have a big impact on the performance.', + 'all' + ); }); describe('when there is coverage data', () => { - beforeEach(() => { initialRunResult.runResult.coverage = { fileWithMutantOne: { s: { 0: 1 }, f: {} } @@ -427,22 +512,23 @@ describe(MutantTestMatcher.name, () => { const result = await sut.matchWithMutants(mutants); // Assert - const expectedTestSelection: TestSelection[] = [{ - id: 0, - name: 'test 1' - }, { - id: 1, - name: 'test 2' - }]; + const expectedTestSelection: TestSelection[] = [ + { + id: 0, + name: 'test 1' + }, + { + id: 1, + name: 'test 2' + } + ]; expect(result).lengthOf(1); expect(result[0].selectedTests).deep.eq(expectedTestSelection); }); }); - }); describe('with coverageAnalysis: "off"', () => { - beforeEach(() => { testInjector.options.coverageAnalysis = 'off'; sut = createSut(); diff --git a/packages/core/test/unit/mutants/MutatorFacade.spec.ts b/packages/core/test/unit/mutants/MutatorFacade.spec.ts index 7b8ad20220..2b2ea2502c 100644 --- a/packages/core/test/unit/mutants/MutatorFacade.spec.ts +++ b/packages/core/test/unit/mutants/MutatorFacade.spec.ts @@ -9,7 +9,6 @@ import { MutatorFacade } from '../../../src/mutants/MutatorFacade'; import { Mock } from '../../helpers/producers'; describe('MutatorFacade', () => { - let mutatorMock: Mock; let pluginCreatorMock: Mock>; @@ -55,7 +54,7 @@ describe('MutatorFacade', () => { factory.mutant({ mutatorName: 'foo' }), factory.mutant({ mutatorName: 'bar' }), factory.mutant({ mutatorName: 'baz' }) - ]); + ]); testInjector.options.mutator = { excludedMutations: ['foo'], name: 'javascript' @@ -75,20 +74,19 @@ describe('MutatorFacade', () => { name: 'javascript' }; createSut().mutate([]); - expect(testInjector.logger.info).calledWith('It\'s a mutant-free world, nothing to test. (3 Mutant(s) excluded)'); + expect(testInjector.logger.info).calledWith("It's a mutant-free world, nothing to test. (3 Mutant(s) excluded)"); }); it('should log the absence of mutants if no mutants were generated', async () => { mutatorMock.mutate.returns([]); createSut().mutate([]); - expect(testInjector.logger.info).calledWith('It\'s a mutant-free world, nothing to test.'); + expect(testInjector.logger.info).calledWith("It's a mutant-free world, nothing to test."); }); - }); function createSut() { return testInjector.injector - .provideValue(coreTokens.pluginCreatorMutator, pluginCreatorMock as unknown as PluginCreator) + .provideValue(coreTokens.pluginCreatorMutator, (pluginCreatorMock as unknown) as PluginCreator) .injectClass(MutatorFacade); } }); diff --git a/packages/core/test/unit/process/InitialTestExecutor.spec.ts b/packages/core/test/unit/process/InitialTestExecutor.spec.ts index a83b0b057c..1060459fa1 100644 --- a/packages/core/test/unit/process/InitialTestExecutor.spec.ts +++ b/packages/core/test/unit/process/InitialTestExecutor.spec.ts @@ -28,7 +28,6 @@ const LOGGING_CONTEXT: LoggingClientContext = Object.freeze({ }); describe('InitialTestExecutor run', () => { - let strykerSandboxMock: producers.Mock; let sut: InitialTestExecutor; let testFrameworkMock: TestFramework | null; @@ -50,8 +49,8 @@ describe('InitialTestExecutor run', () => { .provideValue(coreTokens.loggingContext, LOGGING_CONTEXT) .provideValue(coreTokens.testFramework, testFrameworkMock) .provideValue(coreTokens.transpiler, transpilerMock as Transpiler) - .provideValue(coreTokens.timer, timerMock as unknown as Timer) - .provideValue(coreTokens.temporaryDirectory, temporaryDirectoryMock as unknown as TemporaryDirectory) + .provideValue(coreTokens.timer, (timerMock as unknown) as Timer) + .provideValue(coreTokens.temporaryDirectory, (temporaryDirectoryMock as unknown) as TemporaryDirectory) .injectClass(InitialTestExecutor); } @@ -65,14 +64,8 @@ describe('InitialTestExecutor run', () => { sourceMapperMock = producers.mock(PassThroughSourceMapper); sinon.stub(SourceMapper, 'create').returns(sourceMapperMock); testFrameworkMock = testFramework(); - coverageAnnotatedFiles = [ - new File('cov-annotated-transpiled-file-1.js', ''), - new File('cov-annotated-transpiled-file-2.js', ''), - ]; - transpiledFiles = [ - new File('transpiled-file-1.js', ''), - new File('transpiled-file-2.js', '') - ]; + coverageAnnotatedFiles = [new File('cov-annotated-transpiled-file-1.js', ''), new File('cov-annotated-transpiled-file-2.js', '')]; + transpiledFiles = [new File('transpiled-file-1.js', ''), new File('transpiled-file-2.js', '')]; coverageInstrumenterTranspilerMock.transpile.returns(coverageAnnotatedFiles); transpilerMock.transpile.returns(transpiledFiles); expectedRunResult = runResult(); @@ -80,7 +73,6 @@ describe('InitialTestExecutor run', () => { }); describe('with input files', () => { - beforeEach(() => { inputFiles = new InputFileCollection([new File('mutate.js', ''), new File('mutate.spec.js', '')], ['mutate.js']); }); @@ -165,15 +157,22 @@ describe('InitialTestExecutor run', () => { timerMock.elapsedMs.returns(50); sut = createSut(); await sut.run(); - expect(testInjector.logger.info).to.have.been.calledWith('Initial test run succeeded. Ran %s tests in %s (net %s ms, overhead %s ms).', - 2, '2 seconds', 20, 30); + expect(testInjector.logger.info).to.have.been.calledWith( + 'Initial test run succeeded. Ran %s tests in %s (net %s ms, overhead %s ms).', + 2, + '2 seconds', + 20, + 30 + ); }); it('should log when there were no tests', async () => { while (expectedRunResult.tests.pop()); sut = createSut(); await sut.run(); - expect(testInjector.logger.warn).to.have.been.calledWith('No tests were executed. Stryker will exit prematurely. Please check your configuration.'); + expect(testInjector.logger.warn).to.have.been.calledWith( + 'No tests were executed. Stryker will exit prematurely. Please check your configuration.' + ); }); it('should pass through any rejections', async () => { @@ -207,7 +206,9 @@ describe('InitialTestExecutor run', () => { sinon.stub(coverageHooks, 'coveragePerTestHooks').returns('test hook foobar'); sut = createSut(); await sut.run(); - expect(testInjector.logger.warn).calledWith('Cannot measure coverage results per test, there is no testFramework and thus no way of executing code right before and after each test.'); + expect(testInjector.logger.warn).calledWith( + 'Cannot measure coverage results per test, there is no testFramework and thus no way of executing code right before and after each test.' + ); }); describe('and run has test failures', () => { @@ -222,7 +223,9 @@ describe('InitialTestExecutor run', () => { it('should have logged the errors', async () => { sut = createSut(); await expect(sut.run()).rejected; - expect(testInjector.logger.error).calledWith(`One or more tests failed in the initial test run:${EOL}\texample test${EOL}\t\texpected error${EOL}\t2nd example test`); + expect(testInjector.logger.error).calledWith( + `One or more tests failed in the initial test run:${EOL}\texample test${EOL}\t\texpected error${EOL}\t2nd example test` + ); }); it('should reject with correct message', async () => { sut = createSut(); @@ -231,7 +234,6 @@ describe('InitialTestExecutor run', () => { }); describe('and run has some errors', () => { - beforeEach(() => { expectedRunResult.status = RunStatus.Error; expectedRunResult.errorMessages = ['foobar', 'example']; @@ -251,15 +253,15 @@ describe('InitialTestExecutor run', () => { describe('and run timed out', () => { beforeEach(() => { expectedRunResult.status = RunStatus.Timeout; - expectedRunResult.tests = [ - testResult({ name: 'foobar test' }), - testResult({ name: 'example test', status: TestStatus.Failed })]; + expectedRunResult.tests = [testResult({ name: 'foobar test' }), testResult({ name: 'example test', status: TestStatus.Failed })]; }); it('should have logged the timeout', async () => { sut = createSut(); await expect(sut.run()).rejected; - expect(testInjector.logger.error).calledWith(`Initial test run timed out! Ran following tests before timeout:${EOL}\tfoobar test (Success)${EOL}\texample test (Failed)`); + expect(testInjector.logger.error).calledWith( + `Initial test run timed out! Ran following tests before timeout:${EOL}\tfoobar test (Success)${EOL}\texample test (Failed)` + ); }); it('should reject with correct message', async () => { diff --git a/packages/core/test/unit/process/MutationTestExecutor.spec.ts b/packages/core/test/unit/process/MutationTestExecutor.spec.ts index 9341517f17..5f2d3b8808 100644 --- a/packages/core/test/unit/process/MutationTestExecutor.spec.ts +++ b/packages/core/test/unit/process/MutationTestExecutor.spec.ts @@ -17,7 +17,6 @@ import { MutantTranspileScheduler } from '../../../src/transpiler/MutantTranspil import { Mock, mock, testableMutant, transpiledMutant } from '../../helpers/producers'; describe(MutationTestExecutor.name, () => { - let sandboxPoolMock: Mock; let mutantTranspileSchedulerMock: Mock; let transpiledMutants: Observable; @@ -36,10 +35,7 @@ describe(MutationTestExecutor.name, () => { inputFiles = new InputFileCollection([new File('input.ts', '')], []); mutants = [testableMutant()]; transpiledMutants = of(transpiledMutant('foo.js'), transpiledMutant('bar.js')); - mutantResults = [ - factory.mutantResult({ status: MutantStatus.RuntimeError }), - factory.mutantResult({ status: MutantStatus.Survived }) - ]; + mutantResults = [factory.mutantResult({ status: MutantStatus.RuntimeError }), factory.mutantResult({ status: MutantStatus.Survived })]; mutantTranspileSchedulerMock.scheduleTranspileMutants.returns(transpiledMutants); sandboxPoolMock.runMutants.returns(from(mutantResults)); sut = createSut(); @@ -47,15 +43,14 @@ describe(MutationTestExecutor.name, () => { function createSut(): MutationTestExecutor { return testInjector.injector - .provideValue(coreTokens.sandboxPool, sandboxPoolMock as unknown as SandboxPool) - .provideValue(coreTokens.mutantTranspileScheduler, mutantTranspileSchedulerMock as unknown as MutantTranspileScheduler) + .provideValue(coreTokens.sandboxPool, (sandboxPoolMock as unknown) as SandboxPool) + .provideValue(coreTokens.mutantTranspileScheduler, (mutantTranspileSchedulerMock as unknown) as MutantTranspileScheduler) .provideValue(coreTokens.inputFiles, inputFiles) .provideValue(coreTokens.reporter, reporter) .injectClass(MutationTestExecutor); } describe('run', () => { - it('should have ran the mutants in the sandbox pool', async () => { await sut.run(mutants); expect(mutantTranspileSchedulerMock.scheduleTranspileMutants).calledWith(mutants); @@ -83,6 +78,5 @@ describe(MutationTestExecutor.name, () => { const actualResults = await sut.run(mutants); expect(actualResults).deep.eq(mutantResults); }); - }); }); diff --git a/packages/core/test/unit/reporters/BroadcastReporter.spec.ts b/packages/core/test/unit/reporters/BroadcastReporter.spec.ts index 6bb182bf0d..b2a557e661 100644 --- a/packages/core/test/unit/reporters/BroadcastReporter.spec.ts +++ b/packages/core/test/unit/reporters/BroadcastReporter.spec.ts @@ -9,7 +9,6 @@ import { PluginCreator } from '../../../src/di/PluginCreator'; import BroadcastReporter from '../../../src/reporters/BroadcastReporter'; describe('BroadcastReporter', () => { - let sut: BroadcastReporter; let rep1: sinon.SinonStubbedInstance>; let rep2: sinon.SinonStubbedInstance>; @@ -23,8 +22,10 @@ describe('BroadcastReporter', () => { rep2 = factory.reporter('rep2'); pluginCreatorMock = sinon.createStubInstance(PluginCreator); pluginCreatorMock.create - .withArgs('rep1').returns(rep1) - .withArgs('rep2').returns(rep2); + .withArgs('rep1') + .returns(rep1) + .withArgs('rep2') + .returns(rep2); }); afterEach(() => { @@ -89,11 +90,13 @@ describe('BroadcastReporter', () => { beforeEach(() => { isResolved = false; - rep1.wrapUp.returns(new Promise((resolve, reject) => { - wrapUpResolveFn = resolve; - wrapUpRejectFn = reject; - })); - rep2.wrapUp.returns(new Promise(resolve => wrapUpResolveFn2 = resolve)); + rep1.wrapUp.returns( + new Promise((resolve, reject) => { + wrapUpResolveFn = resolve; + wrapUpRejectFn = reject; + }) + ); + rep2.wrapUp.returns(new Promise(resolve => (wrapUpResolveFn2 = resolve))); result = sut.wrapUp().then(() => void (isResolved = true)); }); @@ -116,7 +119,7 @@ describe('BroadcastReporter', () => { it('should not result in a rejection', () => result); it('should log the error', () => { - expect(testInjector.logger.error).calledWith(`An error occurred during 'wrapUp' on reporter 'rep1'.`, actualError); + expect(testInjector.logger.error).calledWith("An error occurred during 'wrapUp' on reporter 'rep1'.", actualError); }); }); }); @@ -147,12 +150,14 @@ describe('BroadcastReporter', () => { }); it('should log a warning for reporters that implement the onScoreCalculated event', () => { - rep1.onScoreCalculated.returns(() => { }); + rep1.onScoreCalculated.returns(() => {}); (rep2 as any).onScoreCalculated = undefined; sut.onScoreCalculated(scoreResult()); - expect(testInjector.logger.warn).to.have.been.calledWith(`DEPRECATED: The reporter 'rep1' uses 'onScoreCalculated' which is deprecated. Please use 'onMutationTestReportReady' and calculate the score as an alternative.`); + expect(testInjector.logger.warn).to.have.been.calledWith( + "DEPRECATED: The reporter 'rep1' uses 'onScoreCalculated' which is deprecated. Please use 'onMutationTestReportReady' and calculate the score as an alternative." + ); expect(testInjector.logger.warn).to.not.have.been.calledWithMatch('rep2'); }); }); @@ -160,7 +165,7 @@ describe('BroadcastReporter', () => { function createSut() { return testInjector.injector - .provideValue(coreTokens.pluginCreatorReporter, pluginCreatorMock as unknown as PluginCreator) + .provideValue(coreTokens.pluginCreatorReporter, (pluginCreatorMock as unknown) as PluginCreator) .injectClass(BroadcastReporter); } diff --git a/packages/core/test/unit/reporters/ClearTextReporter.spec.ts b/packages/core/test/unit/reporters/ClearTextReporter.spec.ts index 774f30cdac..2df41d3b84 100644 --- a/packages/core/test/unit/reporters/ClearTextReporter.spec.ts +++ b/packages/core/test/unit/reporters/ClearTextReporter.spec.ts @@ -8,11 +8,7 @@ import * as sinon from 'sinon'; import ClearTextReporter from '../../../src/reporters/ClearTextReporter'; const colorizeFileAndPosition = (sourceFilePath: string, line: number, column: number) => { - return [ - chalk.cyan(sourceFilePath), - chalk.yellow(`${line}`), - chalk.yellow(`${column}`), - ].join(':'); + return [chalk.cyan(sourceFilePath), chalk.yellow(`${line}`), chalk.yellow(`${column}`)].join(':'); }; describe(ClearTextReporter.name, () => { @@ -25,7 +21,6 @@ describe(ClearTextReporter.name, () => { }); describe('onMutationTestReportReady', () => { - it('should report the clear text table with correct values', () => { testInjector.options.coverageAnalysis = 'all'; sut = testInjector.injector.injectClass(ClearTextReporter); @@ -85,7 +80,6 @@ describe(ClearTextReporter.name, () => { }); describe('onAllMutantsTested() all mutants except error', () => { - beforeEach(() => { sut.onAllMutantsTested(mutantResults(MutantStatus.Killed, MutantStatus.Survived, MutantStatus.TimedOut, MutantStatus.NoCoverage)); }); @@ -95,9 +89,17 @@ describe(ClearTextReporter.name, () => { }); describe('onAllMutantsTested() with mutants of all kinds', () => { - beforeEach(() => { - sut.onAllMutantsTested(mutantResults(MutantStatus.Killed, MutantStatus.Survived, MutantStatus.TimedOut, MutantStatus.NoCoverage, MutantStatus.RuntimeError, MutantStatus.TranspileError)); + sut.onAllMutantsTested( + mutantResults( + MutantStatus.Killed, + MutantStatus.Survived, + MutantStatus.TimedOut, + MutantStatus.NoCoverage, + MutantStatus.RuntimeError, + MutantStatus.TranspileError + ) + ); }); it('should report on the survived mutant', () => { @@ -111,13 +113,10 @@ describe(ClearTextReporter.name, () => { expect(process.stdout.write).to.have.been.calledWithMatch(sinon.match('Ran all tests for this mutant.')); }); }); - }); describe('when coverageAnalysis: "perTest"', () => { - describe('onAllMutantsTested()', () => { - it('should log source file names with colored text when clearTextReporter is not false', () => { testInjector.options.coverageAnalysis = 'perTest'; @@ -134,7 +133,7 @@ describe(ClearTextReporter.name, () => { sut.onAllMutantsTested(mutantResults(MutantStatus.Killed, MutantStatus.Survived, MutantStatus.TimedOut, MutantStatus.NoCoverage)); - expect(process.stdout.write).to.have.been.calledWithMatch(sinon.match(`sourceFile.ts:1:2`)); + expect(process.stdout.write).to.have.been.calledWithMatch(sinon.match('sourceFile.ts:1:2')); }); it('should not log source file names with colored text when clearTextReporter is false', () => { @@ -155,7 +154,6 @@ describe(ClearTextReporter.name, () => { expect(process.stdout.write).to.not.have.been.calledWithMatch(sinon.match(' a second test')); expect(process.stdout.write).to.not.have.been.calledWithMatch(sinon.match(' a third test')); expect(process.stdout.write).to.not.have.been.calledWithMatch(sinon.match('Ran all tests for this mutant.')); - }); it('should log individual ran tests when logTests is true', () => { @@ -233,8 +231,7 @@ describe(ClearTextReporter.name, () => { }); describe('when coverageAnalysis: "off"', () => { - - beforeEach(() => testInjector.options.coverageAnalysis = 'off'); + beforeEach(() => (testInjector.options.coverageAnalysis = 'off')); describe('onAllMutantsTested()', () => { beforeEach(() => { @@ -247,7 +244,7 @@ describe(ClearTextReporter.name, () => { }); it('should report the average amount of tests ran', () => - expect(process.stdout.write).to.have.been.calledWithMatch(sinon.match(`Ran 3.00 tests per mutant on average.`))); + expect(process.stdout.write).to.have.been.calledWithMatch(sinon.match('Ran 3.00 tests per mutant on average.'))); }); }); @@ -267,5 +264,4 @@ describe(ClearTextReporter.name, () => { return result; }); } - }); diff --git a/packages/core/test/unit/reporters/ClearTextScoreTable.spec.ts b/packages/core/test/unit/reporters/ClearTextScoreTable.spec.ts index 7090360f2e..c983f8137a 100644 --- a/packages/core/test/unit/reporters/ClearTextScoreTable.spec.ts +++ b/packages/core/test/unit/reporters/ClearTextScoreTable.spec.ts @@ -12,17 +12,19 @@ describe(ClearTextScoreTable.name, () => { describe('draw', () => { it('should report the clear text table with correct values', () => { const metricsResult: MetricsResult = { - childResults: [{ - childResults: [ - { - childResults: [], - metrics: metrics({ mutationScore: 59.99 }), - name: 'some/test/for/a/deep/file.js' - } - ], - metrics: metrics({ mutationScore: 60 }), - name: 'child1' - }], + childResults: [ + { + childResults: [ + { + childResults: [], + metrics: metrics({ mutationScore: 59.99 }), + name: 'some/test/for/a/deep/file.js' + } + ], + metrics: metrics({ mutationScore: 60 }), + name: 'child1' + } + ], metrics: metrics({ compileErrors: 7, killed: 1, @@ -72,10 +74,10 @@ describe(ClearTextScoreTable.name, () => { const thresholds: MutationScoreThresholds = { high: 60, low: 50, break: 0 }; const input: MetricsResult = metricsResult({ childResults: [ - metricsResult({ metrics: metrics({ mutationScore: 60.00 }) }), + metricsResult({ metrics: metrics({ mutationScore: 60.0 }) }), metricsResult({ metrics: metrics({ mutationScore: 59.99 }) }), metricsResult({ metrics: metrics({ mutationScore: 50.01 }) }), - metricsResult({ metrics: metrics({ mutationScore: 50.00 }) }), + metricsResult({ metrics: metrics({ mutationScore: 50.0 }) }), metricsResult({ metrics: metrics({ mutationScore: 49.99 }) }) ], metrics: metrics({ mutationScore: 60.01 }) @@ -95,10 +97,7 @@ describe(ClearTextScoreTable.name, () => { it('should color score in red and green if low equals high thresholds', () => { const thresholds: MutationScoreThresholds = { high: 50, low: 50, break: 0 }; const input: MetricsResult = metricsResult({ - childResults: [ - metricsResult({ metrics: metrics({ mutationScore: 50.00 }) }), - metricsResult({ metrics: metrics({ mutationScore: 49.99 }) }) - ], + childResults: [metricsResult({ metrics: metrics({ mutationScore: 50.0 }) }), metricsResult({ metrics: metrics({ mutationScore: 49.99 }) })], metrics: metrics({ mutationScore: 50.01 }) }); const sut = new ClearTextScoreTable(input, thresholds); diff --git a/packages/core/test/unit/reporters/DotsReporter.spec.ts b/packages/core/test/unit/reporters/DotsReporter.spec.ts index b4aa5cfa2b..930aaa1705 100644 --- a/packages/core/test/unit/reporters/DotsReporter.spec.ts +++ b/packages/core/test/unit/reporters/DotsReporter.spec.ts @@ -7,7 +7,6 @@ import * as sinon from 'sinon'; import DotsReporter from '../../../src/reporters/DotsReporter'; describe('DotsReporter', () => { - let sut: DotsReporter; let sandbox: sinon.SinonSandbox; @@ -18,9 +17,7 @@ describe('DotsReporter', () => { }); describe('onMutantTested()', () => { - describe('when status is Killed', () => { - beforeEach(() => { sut.onMutantTested(mutantResult(MutantStatus.Killed)); }); @@ -31,7 +28,6 @@ describe('DotsReporter', () => { }); describe('when status is TimedOut', () => { - beforeEach(() => { sut.onMutantTested(mutantResult(MutantStatus.TimedOut)); }); @@ -42,7 +38,6 @@ describe('DotsReporter', () => { }); describe('when status is Survived', () => { - beforeEach(() => { sut.onMutantTested(mutantResult(MutantStatus.Survived)); }); @@ -77,5 +72,4 @@ describe('DotsReporter', () => { testsRan: [''] }); } - }); diff --git a/packages/core/test/unit/reporters/EventRecorderReporter.spec.ts b/packages/core/test/unit/reporters/EventRecorderReporter.spec.ts index fdf32048bf..b260f850a0 100644 --- a/packages/core/test/unit/reporters/EventRecorderReporter.spec.ts +++ b/packages/core/test/unit/reporters/EventRecorderReporter.spec.ts @@ -9,7 +9,6 @@ import StrictReporter from '../../../src/reporters/StrictReporter'; import * as fileUtils from '../../../src/utils/fileUtils'; describe('EventRecorderReporter', () => { - let sut: StrictReporter; let cleanFolderStub: sinon.SinonStub; let writeFileStub: sinon.SinonStub; @@ -20,7 +19,6 @@ describe('EventRecorderReporter', () => { }); describe('when constructed with empty options', () => { - describe('and cleanFolder resolves correctly', () => { beforeEach(() => { cleanFolderStub.returns(Promise.resolve()); @@ -28,7 +26,9 @@ describe('EventRecorderReporter', () => { }); it('should log about the default baseFolder', () => { - expect(testInjector.logger.debug).to.have.been.calledWith(`No base folder configuration found (using configuration: eventReporter: { baseDir: 'output/folder' }), using default reports/mutation/events`); + expect(testInjector.logger.debug).to.have.been.calledWith( + "No base folder configuration found (using configuration: eventReporter: { baseDir: 'output/folder' }), using default reports/mutation/events" + ); }); it('should clean the baseFolder', () => { @@ -37,15 +37,15 @@ describe('EventRecorderReporter', () => { const arrangeActAssertEvent = (eventName: keyof Reporter) => { describe(`${eventName} event`, () => { - let writeFileRejection: undefined | Error; const expected: any = { some: 'eventData' }; - const arrange = () => beforeEach(() => { - writeFileRejection = undefined; - (sut[eventName] as any)(expected); - return (sut.wrapUp() as Promise).then(() => void 0, error => writeFileRejection = error); - }); + const arrange = () => + beforeEach(() => { + writeFileRejection = undefined; + (sut[eventName] as any)(expected); + return (sut.wrapUp() as Promise).then(() => void 0, error => (writeFileRejection = error)); + }); describe('when writeFile results in a rejection', () => { const expectedError = new Error('some error'); @@ -57,7 +57,8 @@ describe('EventRecorderReporter', () => { describe('when writeFile is successful', () => { arrange(); - it('should writeFile', () => expect(fsAsPromised.writeFile).to.have.been.calledWith(sinon.match(RegExp(`.*0000\\d-${eventName}\\.json`)), JSON.stringify(expected))); + it('should writeFile', () => + expect(fsAsPromised.writeFile).to.have.been.calledWith(sinon.match(RegExp(`.*0000\\d-${eventName}\\.json`)), JSON.stringify(expected))); }); }); }; diff --git a/packages/core/test/unit/reporters/MutationTestReportCalculator.spec.ts b/packages/core/test/unit/reporters/MutationTestReportCalculator.spec.ts index dc80082517..93706a26f6 100644 --- a/packages/core/test/unit/reporters/MutationTestReportCalculator.spec.ts +++ b/packages/core/test/unit/reporters/MutationTestReportCalculator.spec.ts @@ -7,7 +7,6 @@ import InputFileCollection from '../../../src/input/InputFileCollection'; import { MutationTestReportCalculator } from '../../../src/reporters/MutationTestReportCalculator'; describe(MutationTestReportCalculator.name, () => { - let reporterMock: sinon.SinonStubbedInstance>; let sut: MutationTestReportCalculator; let inputFiles: InputFileCollection; @@ -19,7 +18,7 @@ describe(MutationTestReportCalculator.name, () => { inputFiles = { files, filesToMutate: [], - logFiles: () => { } + logFiles: () => {} }; sut = testInjector.injector .provideValue(coreTokens.reporter, reporterMock as Required) @@ -150,7 +149,7 @@ describe(MutationTestReportCalculator.name, () => { expect(testInjector.logger.warn).calledWithMatch('File "not-found.js" not found'); }); - function actReport(input: ReadonlyArray = []): mutationTestReportSchema.MutationTestResult { + function actReport(input: readonly MutantResult[] = []): mutationTestReportSchema.MutationTestResult { sut.report(input); return reporterMock.onMutationTestReportReady.firstCall.args[0]; } diff --git a/packages/core/test/unit/reporters/ProgressAppendOnlyReporter.spec.ts b/packages/core/test/unit/reporters/ProgressAppendOnlyReporter.spec.ts index 27d6e69285..aa75793e10 100644 --- a/packages/core/test/unit/reporters/ProgressAppendOnlyReporter.spec.ts +++ b/packages/core/test/unit/reporters/ProgressAppendOnlyReporter.spec.ts @@ -20,7 +20,6 @@ describe('ProgressAppendOnlyReporter', () => { }); describe('onAllMutantsMatchedWithTests() with 2 mutant to test', () => { - beforeEach(() => { sut.onAllMutantsMatchedWithTests([matchedMutant(1), matchedMutant(1)]); }); @@ -31,8 +30,7 @@ describe('ProgressAppendOnlyReporter', () => { it('should log zero progress after ten seconds without completed tests', () => { sinon.clock.tick(TEN_SECONDS); - expect(process.stdout.write).to.have.been.calledWith(`Mutation testing 0% (ETC n/a) ` + - `0/2 tested (0 survived)${os.EOL}`); + expect(process.stdout.write).to.have.been.calledWith('Mutation testing 0% (ETC n/a) ' + `0/2 tested (0 survived)${os.EOL}`); }); it('should log 50% with 10s ETC after ten seconds with 1 completed test', () => { diff --git a/packages/core/test/unit/reporters/ProgressReporter.spec.ts b/packages/core/test/unit/reporters/ProgressReporter.spec.ts index e01e77d2c9..1bb046877e 100644 --- a/packages/core/test/unit/reporters/ProgressReporter.spec.ts +++ b/packages/core/test/unit/reporters/ProgressReporter.spec.ts @@ -14,11 +14,10 @@ const TEN_THOUSAND_SECONDS = SECOND * 10000; const ONE_HOUR = SECOND * 3600; describe('ProgressReporter', () => { - let sut: ProgressReporter; let matchedMutants: MatchedMutant[]; let progressBar: Mock; - const progressBarContent = `Mutation testing [:bar] :percent (ETC :etc) :tested/:total tested (:survived survived)`; + const progressBarContent = 'Mutation testing [:bar] :percent (ETC :etc) :tested/:total tested (:survived survived)'; beforeEach(() => { sinon.useFakeTimers(); @@ -41,7 +40,7 @@ describe('ProgressReporter', () => { expect(progressBarModule.default).to.have.been.calledWithMatch(progressBarContent, { total: 3 }); }); }); - describe('when there are 2 MatchedMutants that all contain Tests and 1 MatchMutant that doesn\'t have tests', () => { + describe("when there are 2 MatchedMutants that all contain Tests and 1 MatchMutant that doesn't have tests", () => { beforeEach(() => { matchedMutants = [matchedMutant(1), matchedMutant(0), matchedMutant(2)]; @@ -68,7 +67,7 @@ describe('ProgressReporter', () => { expect(progressBar.tick).to.have.been.calledWithMatch(progressBarTickTokens); }); - it('should not tick the ProgressBar if the result was for a mutant that wasn\'t matched to any tests', () => { + it("should not tick the ProgressBar if the result was for a mutant that wasn't matched to any tests", () => { // mutant 0 isn't matched to any tests sut.onMutantTested(mutantResult({ id: '0', status: MutantStatus.TranspileError })); progressBarTickTokens = { total: 3, tested: 0, survived: 0 }; diff --git a/packages/core/test/unit/reporters/ci/CircleProvider.spec.ts b/packages/core/test/unit/reporters/ci/CircleProvider.spec.ts index 7936f32322..6b9b1830d4 100644 --- a/packages/core/test/unit/reporters/ci/CircleProvider.spec.ts +++ b/packages/core/test/unit/reporters/ci/CircleProvider.spec.ts @@ -75,5 +75,4 @@ describe('CircleCI Provider', () => { expect(result).to.equal('(unknown)'); }); }); - }); diff --git a/packages/core/test/unit/reporters/ci/Provider.spec.ts b/packages/core/test/unit/reporters/ci/Provider.spec.ts index 723aee28b3..922505453a 100644 --- a/packages/core/test/unit/reporters/ci/Provider.spec.ts +++ b/packages/core/test/unit/reporters/ci/Provider.spec.ts @@ -21,7 +21,7 @@ describe('determineCiProvider()', () => { }); }); - describe('When HAS_JOSH_K_SEAL_OF_APPROVAL is \'true\'', () => { + describe("When HAS_JOSH_K_SEAL_OF_APPROVAL is 'true'", () => { it('should provide a CI Provider implementation', () => { getEnvironmentVariables.withArgs('HAS_JOSH_K_SEAL_OF_APPROVAL').returns(true); @@ -31,7 +31,7 @@ describe('determineCiProvider()', () => { }); }); - describe('When CIRCLECI is \'true\'', () => { + describe("When CIRCLECI is 'true'", () => { it('should provide a CI Provider implementation', () => { getEnvironmentVariables.withArgs('CIRCLECI').returns(true); diff --git a/packages/core/test/unit/reporters/ci/TravisProvider.spec.ts b/packages/core/test/unit/reporters/ci/TravisProvider.spec.ts index 427ad0d21e..cf67b6111a 100644 --- a/packages/core/test/unit/reporters/ci/TravisProvider.spec.ts +++ b/packages/core/test/unit/reporters/ci/TravisProvider.spec.ts @@ -70,5 +70,4 @@ describe('Travis Provider', () => { expect(result).to.equal('(unknown)'); }); }); - }); diff --git a/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporter.spec.ts b/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporter.spec.ts index 2921e67560..9c5a2d619c 100644 --- a/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporter.spec.ts +++ b/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporter.spec.ts @@ -5,7 +5,11 @@ import { expect } from 'chai'; import * as sinon from 'sinon'; import * as ciProvider from '../../../../src/reporters/ci/Provider'; import DashboardReporter from '../../../../src/reporters/dashboard-reporter/DashboardReporter'; -import { default as DashboardReporterClient, default as StrykerDashboardClient, StrykerDashboardReport } from '../../../../src/reporters/dashboard-reporter/DashboardReporterClient'; +import { + default as DashboardReporterClient, + default as StrykerDashboardClient, + StrykerDashboardReport +} from '../../../../src/reporters/dashboard-reporter/DashboardReporterClient'; import { dashboardReporterTokens } from '../../../../src/reporters/dashboard-reporter/tokens'; import * as environmentVariables from '../../../../src/utils/objectUtils'; import { mock, Mock } from '../../../helpers/producers'; @@ -27,20 +31,17 @@ describe(DashboardReporter.name, () => { determineCiProvider = sinon.stub(ciProvider, 'determineCIProvider'); }); - function setupEnvironmentVariables(env?: { - ci?: boolean - pullRequest?: boolean; - repository?: string; - branch?: string; - apiKey?: string; - }) { - const { ci, pullRequest, repository, branch, apiKey } = Object.assign({ - apiKey: '12345', - branch: 'master', - ci: true, - pullRequest: false, - repository: 'stryker-mutator/stryker' - }, env); + function setupEnvironmentVariables(env?: { ci?: boolean; pullRequest?: boolean; repository?: string; branch?: string; apiKey?: string }) { + const { ci, pullRequest, repository, branch, apiKey } = Object.assign( + { + apiKey: '12345', + branch: 'master', + ci: true, + pullRequest: false, + repository: 'stryker-mutator/stryker' + }, + env + ); if (ci) { determineCiProvider.returns({ @@ -54,7 +55,7 @@ describe(DashboardReporter.name, () => { getEnvironmentVariables.withArgs('STRYKER_DASHBOARD_API_KEY').returns(apiKey); sut = testInjector.injector - .provideValue(dashboardReporterTokens.dashboardReporterClient, dashboardClientMock as unknown as DashboardReporterClient) + .provideValue(dashboardReporterTokens.dashboardReporterClient, (dashboardClientMock as unknown) as DashboardReporterClient) .injectClass(DashboardReporter); } diff --git a/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporterClient.spec.ts b/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporterClient.spec.ts index c8b45d5851..0337c13a27 100644 --- a/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporterClient.spec.ts +++ b/packages/core/test/unit/reporters/dashboard-reporter/DashboardReporterClient.spec.ts @@ -7,7 +7,6 @@ import { dashboardReporterTokens } from '../../../../src/reporters/dashboard-rep import { Mock, mock } from '../../../helpers/producers'; describe('DashboardReporterClient', () => { - let sut: StrykerDashboardClient; let dashboardClient: Mock; @@ -23,30 +22,30 @@ describe('DashboardReporterClient', () => { beforeEach(() => { dashboardClient = mock(HttpClient); sut = testInjector.injector - .provideValue(dashboardReporterTokens.httpClient, dashboardClient as unknown as HttpClient) + .provideValue(dashboardReporterTokens.httpClient, (dashboardClient as unknown) as HttpClient) .injectClass(DashboardReporterClient); }); it('report mutations score to dashboard server', async () => { - // Arrange - dashboardClient.post.resolves({ - message: { - statusCode: 201 - } - }); + // Arrange + dashboardClient.post.resolves({ + message: { + statusCode: 201 + } + }); - // Act - await sut.postStrykerDashboardReport(dashboardReport); + // Act + await sut.postStrykerDashboardReport(dashboardReport); - // Assert - const report = JSON.stringify(dashboardReport); - const contentType = { - ['Content-Type']: 'application/json' - }; + // Assert + const report = JSON.stringify(dashboardReport); + const contentType = { + ['Content-Type']: 'application/json' + }; - expect(testInjector.logger.info).have.been.calledWithMatch(`Posting report to ${url}`); - expect(dashboardClient.post).have.been.calledWith(url, report, contentType); - expect(testInjector.logger.error).have.not.been.called; + expect(testInjector.logger.info).have.been.calledWithMatch(`Posting report to ${url}`); + expect(dashboardClient.post).have.been.calledWith(url, report, contentType); + expect(testInjector.logger.error).have.not.been.called; }); it('when the server returns a invalid status code an error will be logged ', async () => { @@ -64,7 +63,7 @@ describe('DashboardReporterClient', () => { expect(testInjector.logger.error).have.been.calledWithMatch(`Post to ${url} resulted in http status code: 500`); }); - it('when the server doesn\'t respond an error will be logged', async () => { + it("when the server doesn't respond an error will be logged", async () => { // Arrange dashboardClient.post.rejects(); diff --git a/packages/core/test/unit/test-runner/ChildProcessTestRunnerDecorator.spec.ts b/packages/core/test/unit/test-runner/ChildProcessTestRunnerDecorator.spec.ts index 15d39abeca..755ed5cd68 100644 --- a/packages/core/test/unit/test-runner/ChildProcessTestRunnerDecorator.spec.ts +++ b/packages/core/test/unit/test-runner/ChildProcessTestRunnerDecorator.spec.ts @@ -64,12 +64,10 @@ describe(ChildProcessTestRunnerDecorator.name, () => { }); describe('dispose', () => { - it('should dispose the test runner before disposing the child process itself on `dispose`', async () => { childProcessProxyMock.proxy.dispose.resolves(); await sut.dispose(); - expect(childProcessProxyMock.proxy.dispose) - .calledBefore(childProcessProxyMock.dispose); + expect(childProcessProxyMock.proxy.dispose).calledBefore(childProcessProxyMock.dispose); }); it('should not reject when the child process is down', async () => { @@ -87,6 +85,5 @@ describe(ChildProcessTestRunnerDecorator.name, () => { expect(childProcessProxyMock.dispose).called; testRunnerDisposeTask.resolve(undefined); }); - }); }); diff --git a/packages/core/test/unit/test-runner/CommandTestRunner.spec.ts b/packages/core/test/unit/test-runner/CommandTestRunner.spec.ts index 550a6938a1..11452009eb 100644 --- a/packages/core/test/unit/test-runner/CommandTestRunner.spec.ts +++ b/packages/core/test/unit/test-runner/CommandTestRunner.spec.ts @@ -12,7 +12,6 @@ import ChildProcessMock from '../../helpers/ChildProcessMock'; import { Mock, mock } from '../../helpers/producers'; describe(CommandTestRunner.name, () => { - let childProcessMock: ChildProcessMock; let killStub: sinon.SinonStub; let timerMock: Mock; @@ -41,9 +40,7 @@ describe(CommandTestRunner.name, () => { const result = await actRun(); const expectedResult: RunResult = { status: RunStatus.Complete, - tests: [ - { name: 'All tests', status: TestStatus.Success, timeSpentMs: 42 } - ] + tests: [{ name: 'All tests', status: TestStatus.Success, timeSpentMs: 42 }] }; expect(result).deep.eq(expectedResult); }); @@ -59,9 +56,7 @@ describe(CommandTestRunner.name, () => { const result = await resultPromise; const expectedResult: RunResult = { status: RunStatus.Complete, - tests: [ - { name: 'All tests', status: TestStatus.Failed, timeSpentMs: 42, failureMessages: [`x Test 1 failed${os.EOL}1 != 2`] } - ] + tests: [{ name: 'All tests', status: TestStatus.Failed, timeSpentMs: 42, failureMessages: [`x Test 1 failed${os.EOL}1 != 2`] }] }; expect(result).deep.eq(expectedResult); }); @@ -92,7 +87,6 @@ describe(CommandTestRunner.name, () => { }); describe('dispose', () => { - it('should kill any running process', async () => { killStub.resolves(); const sut = createSut(); @@ -137,5 +131,4 @@ describe(CommandTestRunner.name, () => { function tick(): Promise { return new Promise(res => setTimeout(res, 0)); } - }); diff --git a/packages/core/test/unit/test-runner/RetryDecorator.spec.ts b/packages/core/test/unit/test-runner/RetryDecorator.spec.ts index 2236239eae..024614e58a 100644 --- a/packages/core/test/unit/test-runner/RetryDecorator.spec.ts +++ b/packages/core/test/unit/test-runner/RetryDecorator.spec.ts @@ -60,7 +60,10 @@ describe('RetryDecorator', () => { testRunner1.run.rejects(new OutOfMemoryError(123, 123)); testRunner2.run.resolves(expectedResult); await expect(sut.run(options)).to.eventually.eq(expectedResult); - expect(logMock.info).calledWith('Test runner process [%s] ran out of memory. You probably have a memory leak in your tests. Don\'t worry, Stryker will restart the process, but you might want to investigate this later, because this decreases performance.', 123); + expect(logMock.info).calledWith( + "Test runner process [%s] ran out of memory. You probably have a memory leak in your tests. Don't worry, Stryker will restart the process, but you might want to investigate this later, because this decreases performance.", + 123 + ); }); it('should dispose a test runner when it rejected, before creating a new one', async () => { @@ -78,8 +81,9 @@ describe('RetryDecorator', () => { const runResult = await sut.run(options); expect(runResult.status).to.be.eq(RunStatus.Error); - expect(runResult.errorMessages).to.be.deep.eq(['Test runner crashed. Tried twice to restart it without any luck. Last time the error message was: ' - + errorToString(finalError)]); + expect(runResult.errorMessages).to.be.deep.eq([ + `Test runner crashed. Tried twice to restart it without any luck. Last time the error message was: ${errorToString(finalError)}` + ]); expect(availableTestRunners).to.have.lengthOf(0); }); }); diff --git a/packages/core/test/unit/test-runner/TimeoutDecorator.spec.ts b/packages/core/test/unit/test-runner/TimeoutDecorator.spec.ts index 55e85301fe..88e5735b52 100644 --- a/packages/core/test/unit/test-runner/TimeoutDecorator.spec.ts +++ b/packages/core/test/unit/test-runner/TimeoutDecorator.spec.ts @@ -26,7 +26,6 @@ describe('TimeoutDecorator', () => { afterEach(() => sandbox.restore()); function itShouldProxyRequests(action: () => Promise, methodName: 'init' | 'dispose' | 'run') { - it('should proxy the request', () => { testRunner1[methodName].resolves('str'); const promise = action(); @@ -63,8 +62,8 @@ describe('TimeoutDecorator', () => { itShouldProxyRequests(() => sut.run({ timeout: 20 }), 'run'); it('should not handle timeouts premature', () => { - let resolve: (result: string) => void = () => { }; - testRunner1.run.returns(new Promise(res => resolve = res)); + let resolve: (result: string) => void = () => {}; + testRunner1.run.returns(new Promise(res => (resolve = res))); const runPromise = sut.run({ timeout: 20 }); clock.tick(19); resolve('expectedResult'); @@ -72,15 +71,17 @@ describe('TimeoutDecorator', () => { }); it('should handle timeouts', () => { - testRunner1.run.returns(new Promise(() => { })); + testRunner1.run.returns(new Promise(() => {})); const runPromise = sut.run({ timeout: 20 }); clock.tick(20); - return expect(runPromise.then(result => { - expect(availableTestRunners).to.have.lengthOf(0); - expect(testRunner1.dispose).to.have.been.called; - expect(testRunner2.init).to.have.been.called; - return result; - })).to.eventually.be.deep.equal({ status: RunStatus.Timeout, tests: [] }); + return expect( + runPromise.then(result => { + expect(availableTestRunners).to.have.lengthOf(0); + expect(testRunner1.dispose).to.have.been.called; + expect(testRunner2.init).to.have.been.called; + return result; + }) + ).to.eventually.be.deep.equal({ status: RunStatus.Timeout, tests: [] }); }); }); }); diff --git a/packages/core/test/unit/transpiler/CoverageInstrumenterTranspiler.spec.ts b/packages/core/test/unit/transpiler/CoverageInstrumenterTranspiler.spec.ts index 90b9752caa..cee341cab5 100644 --- a/packages/core/test/unit/transpiler/CoverageInstrumenterTranspiler.spec.ts +++ b/packages/core/test/unit/transpiler/CoverageInstrumenterTranspiler.spec.ts @@ -20,36 +20,29 @@ describe('CoverageInstrumenterTranspiler', () => { }); describe('when coverage analysis is "all"', () => { - beforeEach(() => { config.coverageAnalysis = 'all'; sut = new CoverageInstrumenterTranspiler(config, ['mutate.js']); }); it('should instrument code of mutated files', async () => { - const input = [ - new File('mutate.js', 'function something() {}'), - new File('spec.js', '') - ]; + const input = [new File('mutate.js', 'function something() {}'), new File('spec.js', '')]; const outputFiles = await sut.transpile(input); const instrumentedContent = outputFiles[0].textContent; - expect(instrumentedContent).to.contain('function something(){cov_').and.contain('.f[0]++'); + expect(instrumentedContent) + .to.contain('function something(){cov_') + .and.contain('.f[0]++'); }); it('should preserve source map comments', async () => { - const input = [ - new File('mutate.js', 'function something() {} // # sourceMappingUrl="something.map.js"'), - ]; + const input = [new File('mutate.js', 'function something() {} // # sourceMappingUrl="something.map.js"')]; const outputFiles = await sut.transpile(input); const instrumentedContent = outputFiles[0].textContent; expect(instrumentedContent).to.contain('sourceMappingUrl="something.map.js"'); }); it('should create a statement map for mutated files', () => { - const input = [ - new File('mutate.js', 'function something () {}'), - new File('foobar.js', 'console.log("foobar");') - ]; + const input = [new File('mutate.js', 'function something () {}'), new File('foobar.js', 'console.log("foobar");')]; sut.transpile(input); expect(sut.fileCoverageMaps['mutate.js'].statementMap).deep.eq({}); expect(sut.fileCoverageMaps['mutate.js'].fnMap[0]).deep.eq({ start: { line: 0, column: 22 }, end: { line: 0, column: 24 } }); @@ -59,7 +52,9 @@ describe('CoverageInstrumenterTranspiler', () => { it('should fill error message and not transpile input when the file contains a parse error', async () => { const invalidJavascriptFile = new File('mutate.js', 'function something {}'); - return expect(sut.transpile([invalidJavascriptFile])).rejectedWith('Could not instrument "mutate.js" for code coverage. Inner error: SyntaxError: Unexpected token'); + return expect(sut.transpile([invalidJavascriptFile])).rejectedWith( + 'Could not instrument "mutate.js" for code coverage. Inner error: SyntaxError: Unexpected token' + ); }); }); }); diff --git a/packages/core/test/unit/transpiler/MutantTranspileScheduler.spec.ts b/packages/core/test/unit/transpiler/MutantTranspileScheduler.spec.ts index ffc682ac68..d43b0f7c9c 100644 --- a/packages/core/test/unit/transpiler/MutantTranspileScheduler.spec.ts +++ b/packages/core/test/unit/transpiler/MutantTranspileScheduler.spec.ts @@ -31,7 +31,7 @@ describe(MutantTranspileScheduler.name, () => { transpiledFilesTwo = [new File('secondResult.js', 'second result')]; sut = testInjector.injector .provideValue(coreTokens.transpiledFiles, initialTranspiledFiles) - .provideValue(coreTokens.transpiler, transpilerMock as unknown as Transpiler & Disposable) + .provideValue(coreTokens.transpiler, (transpilerMock as unknown) as Transpiler & Disposable) .injectClass(MutantTranspileScheduler); }); @@ -39,15 +39,18 @@ describe(MutantTranspileScheduler.name, () => { // Arrange const mutants = [testableMutant(), testableMutant()]; transpilerMock.transpile - .onFirstCall().resolves(transpiledFilesOne) - .onSecondCall().resolves(transpiledFilesTwo); + .onFirstCall() + .resolves(transpiledFilesOne) + .onSecondCall() + .resolves(transpiledFilesTwo); const expected: TranspiledMutant[] = [ { mutant: mutants[0], transpileResult: { error: null, outputFiles: transpiledFilesOne }, changedAnyTranspiledFiles: true }, { mutant: mutants[1], transpileResult: { error: null, outputFiles: transpiledFilesTwo }, changedAnyTranspiledFiles: true } ]; // Act - const actualResult = await sut.scheduleTranspileMutants(mutants) + const actualResult = await sut + .scheduleTranspileMutants(mutants) .pipe(toArray()) .toPromise(); @@ -62,13 +65,14 @@ describe(MutantTranspileScheduler.name, () => { const mutant = testableMutant(); // Act - const actualResult = await sut.scheduleTranspileMutants([mutant]) + const actualResult = await sut + .scheduleTranspileMutants([mutant]) .pipe(toArray()) .toPromise(); // Assert const expected: TranspiledMutant[] = [ - { mutant, transpileResult: { error: errorToString(error), outputFiles: [] }, changedAnyTranspiledFiles: false }, + { mutant, transpileResult: { error: errorToString(error), outputFiles: [] }, changedAnyTranspiledFiles: false } ]; expect(actualResult).deep.eq(expected); }); @@ -79,7 +83,8 @@ describe(MutantTranspileScheduler.name, () => { const mutants = [testableMutant()]; // Act - const actual = await sut.scheduleTranspileMutants(mutants) + const actual = await sut + .scheduleTranspileMutants(mutants) .pipe(toArray()) .toPromise(); @@ -92,15 +97,18 @@ describe(MutantTranspileScheduler.name, () => { it('should transpile mutants one by one in sequence', async () => { // Arrange - let resolveFirst: (files: ReadonlyArray) => void = () => { }; - let resolveSecond: (files: ReadonlyArray) => void = () => { }; + let resolveFirst: (files: readonly File[]) => void = () => {}; + let resolveSecond: (files: readonly File[]) => void = () => {}; transpilerMock.transpile - .onFirstCall().returns(new Promise>(res => resolveFirst = res)) - .onSecondCall().returns(new Promise>(res => resolveSecond = res)); + .onFirstCall() + .returns(new Promise(res => (resolveFirst = res))) + .onSecondCall() + .returns(new Promise(res => (resolveSecond = res))); const actualResults: TranspileResult[] = []; // Act - sut.scheduleTranspileMutants([testableMutant('one'), testableMutant('two')]) + sut + .scheduleTranspileMutants([testableMutant('one'), testableMutant('two')]) .subscribe(transpiledMutant => actualResults.push(transpiledMutant.transpileResult)); // Assert: only first time called @@ -114,20 +122,20 @@ describe(MutantTranspileScheduler.name, () => { resolveSecond(transpiledFilesTwo); // Assert: all results are in await sleep(); - const expectedResults: TranspileResult[] = [ - { error: null, outputFiles: transpiledFilesOne }, - { error: null, outputFiles: transpiledFilesTwo } - ]; + const expectedResults: TranspileResult[] = [{ error: null, outputFiles: transpiledFilesOne }, { error: null, outputFiles: transpiledFilesTwo }]; expect(actualResults).deep.eq(expectedResults); }); const MAX_CONCURRENCY = 100; - it(`should transpile ${MAX_CONCURRENCY} mutants at a time and not transpile more until \`scheduleNext\` is called`, async () => { // Arrange + it(`should transpile ${MAX_CONCURRENCY} mutants at a time and not transpile more until \`scheduleNext\` is called`, async () => { + // Arrange transpilerMock.transpile.resolves(initialTranspiledFiles); - const input = await range(0, MAX_CONCURRENCY + 1).pipe( - map(i => testableMutant(i.toString())), - toArray() - ).toPromise(); + const input = await range(0, MAX_CONCURRENCY + 1) + .pipe( + map(i => testableMutant(i.toString())), + toArray() + ) + .toPromise(); const actualResult: TranspiledMutant[] = []; const subscription = sut.scheduleTranspileMutants(input).subscribe(mutant => actualResult.push(mutant)); diff --git a/packages/core/test/unit/transpiler/SourceMapper.spec.ts b/packages/core/test/unit/transpiler/SourceMapper.spec.ts index 7bd666a683..f197f12a4a 100644 --- a/packages/core/test/unit/transpiler/SourceMapper.spec.ts +++ b/packages/core/test/unit/transpiler/SourceMapper.spec.ts @@ -14,7 +14,8 @@ function base64Encode(input: string) { return Buffer.from(input).toString('base64'); } -const ERROR_POSTFIX = '. Cannot analyse code coverage. Setting `coverageAnalysis: "off"` in your stryker.conf.js will prevent this error, but forces Stryker to run each test for each mutant.'; +const ERROR_POSTFIX = + '. Cannot analyse code coverage. Setting `coverageAnalysis: "off"` in your stryker.conf.js will prevent this error, but forces Stryker to run each test for each mutant.'; describe('SourceMapper', () => { let sut: SourceMapper; @@ -28,10 +29,12 @@ describe('SourceMapper', () => { // For some reason, `generatedPositionFor` is not defined on the `SourceMapConsumer` prototype // Define it here by hand sourceMapConsumerMock.generatedPositionFor = sinon.stub(); - sourceMapConsumerMock.generatedPositionFor.returns(Promise.resolve({ - column: 2, - line: 1 - })); + sourceMapConsumerMock.generatedPositionFor.returns( + Promise.resolve({ + column: 2, + line: 1 + }) + ); sinon.stub(sourceMapModule, 'SourceMapConsumer').returns(sourceMapConsumerMock); // Restore the static values, removed by the stub @@ -79,7 +82,9 @@ describe('SourceMapper', () => { const expectedMapFile2 = { sources: ['file2.ts'] }; transpiledFiles.push(new File('file1.js', '// # sourceMappingURL=file1.js.map')); transpiledFiles.push(new File('file1.js.map', JSON.stringify(expectedMapFile1))); - transpiledFiles.push(new File('file2.js', `// # sourceMappingURL=data:application/json;base64,${base64Encode(JSON.stringify(expectedMapFile2))}`)); + transpiledFiles.push( + new File('file2.js', `// # sourceMappingURL=data:application/json;base64,${base64Encode(JSON.stringify(expectedMapFile2))}`) + ); // Act await sut.transpiledLocationFor(mappedLocation({ fileName: 'file1.ts' })); @@ -93,7 +98,9 @@ describe('SourceMapper', () => { it('should cache source maps for future use when `transpiledLocationFor` is called', async () => { // Arrange const expectedMapFile1 = { sources: ['file1.ts'] }; - transpiledFiles.push(new File('file1.js', `// # sourceMappingURL=data:application/json;base64,${base64Encode(JSON.stringify(expectedMapFile1))}`)); + transpiledFiles.push( + new File('file1.js', `// # sourceMappingURL=data:application/json;base64,${base64Encode(JSON.stringify(expectedMapFile1))}`) + ); // Act await sut.transpiledLocationFor(mappedLocation({ fileName: 'file1.ts' })); @@ -104,42 +111,57 @@ describe('SourceMapper', () => { }); it('should throw an error when the requested source map could not be found', async () => { - await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))).to.be.rejectedWith(SourceMapError, 'Source map not found for "foobar"' + ERROR_POSTFIX); + await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))).to.be.rejectedWith( + SourceMapError, + 'Source map not found for "foobar"' + ERROR_POSTFIX + ); }); it('should throw an error if source map file is a binary file', async () => { transpiledFiles.push(new File('file.js', '// # sourceMappingURL=file1.js.map')); transpiledFiles.push(new File('file1.js.map', Buffer.from(PNG_BASE64_ENCODED, 'base64'))); - await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))) - .to.be.rejectedWith(SourceMapError, /^Source map file "file1.js.map" could not be parsed as json. Cannot analyse code coverage. Setting `coverageAnalysis: "off"` in your stryker.conf.js will prevent this error/); + await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))).to.be.rejectedWith( + SourceMapError, + /^Source map file "file1.js.map" could not be parsed as json. Cannot analyse code coverage. Setting `coverageAnalysis: "off"` in your stryker.conf.js will prevent this error/ + ); }); it('should throw an error if source map data url is not supported', async () => { const expectedMapFile1 = { sources: ['file1.ts'] }; - transpiledFiles.push(new File('file1.js', `// # sourceMappingURL=data:application/xml;base64,${base64Encode(JSON.stringify(expectedMapFile1))}`)); - - await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))) - .to.be.rejectedWith(SourceMapError, `Source map file for "file1.js" cannot be read. Data url "data:application/xml;base64" found, where "data:application/json;base64" was expected${ERROR_POSTFIX}`); + transpiledFiles.push( + new File('file1.js', `// # sourceMappingURL=data:application/xml;base64,${base64Encode(JSON.stringify(expectedMapFile1))}`) + ); + + await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))).to.be.rejectedWith( + SourceMapError, + `Source map file for "file1.js" cannot be read. Data url "data:application/xml;base64" found, where "data:application/json;base64" was expected${ERROR_POSTFIX}` + ); }); it('should throw an error if source map file cannot be found', async () => { transpiledFiles.push(new File('file1.js', '// # sourceMappingURL=file1.js.map')); - await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))) - .to.be.rejectedWith(SourceMapError, `Source map file "file1.js.map" (referenced by "file1.js") cannot be found in list of transpiled files${ERROR_POSTFIX}`); + await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))).to.be.rejectedWith( + SourceMapError, + `Source map file "file1.js.map" (referenced by "file1.js") cannot be found in list of transpiled files${ERROR_POSTFIX}` + ); }); it('should throw an error if source map file url is not declared in a transpiled file', async () => { - transpiledFiles.push(new File('file1.js', `// # sourceMapping%%%=file1.js.map`)); + transpiledFiles.push(new File('file1.js', '// # sourceMapping%%%=file1.js.map')); - await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))) - .to.be.rejectedWith(SourceMapError, `Source map not found for "foobar"${ERROR_POSTFIX}`); + await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'foobar' }))).to.be.rejectedWith( + SourceMapError, + `Source map not found for "foobar"${ERROR_POSTFIX}` + ); }); it('should not throw an error if one of the files is a binary file', async () => { const expectedMapFile1 = { sources: ['file1.ts'] }; - transpiledFiles.push(new File('file1.js', `// # sourceMappingURL=data:application/json;base64,${base64Encode(JSON.stringify(expectedMapFile1))}`)); + transpiledFiles.push( + new File('file1.js', `// # sourceMappingURL=data:application/json;base64,${base64Encode(JSON.stringify(expectedMapFile1))}`) + ); transpiledFiles.push(new File('foo.png', Buffer.from(PNG_BASE64_ENCODED, 'base64'))); await expect(sut.transpiledLocationFor(mappedLocation({ fileName: 'file1.ts' }))).to.eventually.deep.eq({ diff --git a/packages/core/test/unit/transpiler/TranspilerFacade.spec.ts b/packages/core/test/unit/transpiler/TranspilerFacade.spec.ts index b683c1431e..e2dc7ff04a 100644 --- a/packages/core/test/unit/transpiler/TranspilerFacade.spec.ts +++ b/packages/core/test/unit/transpiler/TranspilerFacade.spec.ts @@ -14,7 +14,6 @@ describe(TranspilerFacade.name, () => { let pluginCreatorMock: sinon.SinonStubbedInstance>; describe('when there are no transpilers', () => { - beforeEach(() => { pluginCreatorMock = sinon.createStubInstance(PluginCreator); sut = createSut(); @@ -29,11 +28,10 @@ describe(TranspilerFacade.name, () => { }); describe('with 2 transpilers', () => { - let transpilerOne: Mock; let transpilerTwo: Mock; - let resultFilesOne: ReadonlyArray; - let resultFilesTwo: ReadonlyArray; + let resultFilesOne: readonly File[]; + let resultFilesTwo: readonly File[]; beforeEach(() => { testInjector.options.transpilers = ['transpiler-one', 'transpiler-two']; transpilerOne = mock(TranspilerFacade); @@ -42,8 +40,10 @@ describe(TranspilerFacade.name, () => { resultFilesTwo = [new File('result-2', '')]; pluginCreatorMock = sinon.createStubInstance(PluginCreator); pluginCreatorMock.create - .withArgs('transpiler-one').returns(transpilerOne) - .withArgs('transpiler-two').returns(transpilerTwo); + .withArgs('transpiler-one') + .returns(transpilerOne) + .withArgs('transpiler-two') + .returns(transpilerTwo); transpilerOne.transpile.resolves(resultFilesOne); transpilerTwo.transpile.resolves(resultFilesTwo); sut = createSut(); @@ -74,17 +74,16 @@ describe(TranspilerFacade.name, () => { const transpilePromise = sut.transpile(input); // Assert - await (expect(transpilePromise).rejectedWith('An error occurred in transpiler "transpiler-one". Inner error: Error: an error')); + await expect(transpilePromise).rejectedWith('An error occurred in transpiler "transpiler-one". Inner error: Error: an error'); // Assert expect(transpilerOne.transpile).calledWith(input); expect(transpilerTwo.transpile).not.called; }); - }); function createSut() { return testInjector.injector - .provideValue(coreTokens.pluginCreatorTranspiler, pluginCreatorMock as unknown as PluginCreator) + .provideValue(coreTokens.pluginCreatorTranspiler, (pluginCreatorMock as unknown) as PluginCreator) .injectClass(TranspilerFacade); } }); diff --git a/packages/core/test/unit/transpiler/coverageHooks.spec.ts b/packages/core/test/unit/transpiler/coverageHooks.spec.ts index bc137371ad..eb160ed920 100644 --- a/packages/core/test/unit/transpiler/coverageHooks.spec.ts +++ b/packages/core/test/unit/transpiler/coverageHooks.spec.ts @@ -3,7 +3,6 @@ import { expect } from 'chai'; import * as coverageHooks from '../../../src/transpiler/coverageHooks'; describe('coveragePerTestHooks', () => { - it('should use the coverage variable "__strykerCoverageCurrentTest__"', () => { const actual = coverageHooks.coveragePerTestHooks(testFramework()); expect(actual).to.contain('__strykerCoverageCurrentTest__'); @@ -18,6 +17,6 @@ describe('coveragePerTestHooks', () => { it('should wrap all in a closure', () => { const actual = coverageHooks.coveragePerTestHooks(testFramework()); expect(actual).to.contain('(function (window) {'); - expect(actual).to.contain('})((Function(\'return this\'))());'); + expect(actual).to.contain("})((Function('return this'))());"); }); }); diff --git a/packages/core/test/unit/transpiler/transpiler.spec.ts b/packages/core/test/unit/transpiler/transpiler.spec.ts index dd137c42bc..3fc2668aa4 100644 --- a/packages/core/test/unit/transpiler/transpiler.spec.ts +++ b/packages/core/test/unit/transpiler/transpiler.spec.ts @@ -8,7 +8,6 @@ import { transpilerFactory } from '../../../src/transpiler'; import { ChildProcessTranspiler } from '../../../src/transpiler/ChildProcessTranspiler'; describe(transpilerFactory.name, () => { - let injectorMock: sinon.SinonStubbedInstance>; let childProcessTranspiler: sinon.SinonStubbedInstance; @@ -35,5 +34,4 @@ describe(transpilerFactory.name, () => { expect(actual).not.eq(childProcessTranspiler); expect(actual.transpile(input)).eventually.eq(input); }); - }); diff --git a/packages/core/test/unit/utils/StringBuilder.spec.ts b/packages/core/test/unit/utils/StringBuilder.spec.ts index 5cfc6967fc..959191841e 100644 --- a/packages/core/test/unit/utils/StringBuilder.spec.ts +++ b/packages/core/test/unit/utils/StringBuilder.spec.ts @@ -3,15 +3,13 @@ import { EOL } from 'os'; import StringBuilder from '../../../src/utils/StringBuilder'; describe(StringBuilder.name, () => { - describe('toString', () => { - it('should append strings without separator when `toString()` is called', () => { const sut = new StringBuilder(); sut.append('1'); sut.append('2'); sut.append('3'); - expect(sut.toString()).eq(`123`); + expect(sut.toString()).eq('123'); }); const DEFAULT_MAX_CHARACTERS = 2048; @@ -37,21 +35,14 @@ describe(StringBuilder.name, () => { describe('concat', () => { it('should concatenate multiple string builders with new lines', () => { - const stringBuilders = [ - new StringBuilder(), - new StringBuilder() - ]; + const stringBuilders = [new StringBuilder(), new StringBuilder()]; stringBuilders[0].append('foo'); stringBuilders[0].append('bar'); stringBuilders[1].append('baz'); expect(StringBuilder.concat(...stringBuilders)).eq(`foobar${EOL}baz`); }); it('should remove empty builders', () => { - const stringBuilders = [ - new StringBuilder(), - new StringBuilder(), - new StringBuilder() - ]; + const stringBuilders = [new StringBuilder(), new StringBuilder(), new StringBuilder()]; stringBuilders[0].append('foo'); stringBuilders[0].append('bar'); stringBuilders[2].append('baz'); diff --git a/packages/core/test/unit/utils/TemporaryDirectory.spec.ts b/packages/core/test/unit/utils/TemporaryDirectory.spec.ts index 5f72d349ac..7a7e41ac4e 100644 --- a/packages/core/test/unit/utils/TemporaryDirectory.spec.ts +++ b/packages/core/test/unit/utils/TemporaryDirectory.spec.ts @@ -76,5 +76,4 @@ describe(TemporaryDirectory.name, () => { }); }); }); - }); diff --git a/packages/core/test/unit/utils/Timer.spec.ts b/packages/core/test/unit/utils/Timer.spec.ts index 5ae01946ef..d39b4482e8 100644 --- a/packages/core/test/unit/utils/Timer.spec.ts +++ b/packages/core/test/unit/utils/Timer.spec.ts @@ -17,8 +17,7 @@ describe('Timer', () => { describe(`after ${expectedTimeLabel}`, () => { beforeEach(() => clock.tick(elapsedMs)); - it(`should show "${expectedTimeLabel}" when humanReadableElapsed()`, - () => expect(sut.humanReadableElapsed()).to.be.eq(expectedTimeLabel)); + it(`should show "${expectedTimeLabel}" when humanReadableElapsed()`, () => expect(sut.humanReadableElapsed()).to.be.eq(expectedTimeLabel)); }); }; diff --git a/packages/core/test/unit/utils/fileUtils.spec.ts b/packages/core/test/unit/utils/fileUtils.spec.ts index 0d41a1664d..835cca9632 100644 --- a/packages/core/test/unit/utils/fileUtils.spec.ts +++ b/packages/core/test/unit/utils/fileUtils.spec.ts @@ -5,7 +5,6 @@ import * as sinon from 'sinon'; import * as fileUtils from '../../../src/utils/fileUtils'; describe('fileUtils', () => { - let existsStub: sinon.SinonStub; beforeEach(() => { @@ -37,11 +36,13 @@ describe('fileUtils', () => { expect(actual).eq(expectedNodeModules); }); - it('should return node_modules located in parent directory of `basePath` if it didn\'t exist in base path', async () => { + it("should return node_modules located in parent directory of `basePath` if it didn't exist in base path", async () => { const basePath = path.resolve('a', 'b', 'c'); const expectedNodeModules = path.resolve('a', 'node_modules'); - existsStub.resolves(false) // default - .withArgs(expectedNodeModules).resolves(true); + existsStub + .resolves(false) // default + .withArgs(expectedNodeModules) + .resolves(true); const actual = await fileUtils.findNodeModules(basePath); expect(actual).eq(expectedNodeModules); }); diff --git a/packages/grunt-stryker/.eslintrc b/packages/grunt-stryker/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/grunt-stryker/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/html-reporter/.eslintrc b/packages/html-reporter/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/html-reporter/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/html-reporter/src/HtmlReporter.ts b/packages/html-reporter/src/HtmlReporter.ts index adfa79dbf7..34a94dd913 100644 --- a/packages/html-reporter/src/HtmlReporter.ts +++ b/packages/html-reporter/src/HtmlReporter.ts @@ -14,8 +14,7 @@ export default class HtmlReporter implements Reporter { private _baseDir!: string; private mainPromise: Promise | undefined; - constructor(private readonly options: StrykerOptions, private readonly log: Logger) { - } + constructor(private readonly options: StrykerOptions, private readonly log: Logger) {} public static readonly inject = tokens(commonTokens.options, commonTokens.logger); @@ -31,7 +30,10 @@ export default class HtmlReporter implements Reporter { const indexFileName = path.resolve(this.baseDir, 'index.html'); await this.cleanBaseFolder(); await Promise.all([ - util.copyFile(require.resolve('mutation-testing-elements/dist/mutation-test-elements.js'), path.resolve(this.baseDir, 'mutation-test-elements.js')), + util.copyFile( + require.resolve('mutation-testing-elements/dist/mutation-test-elements.js'), + path.resolve(this.baseDir, 'mutation-test-elements.js') + ), util.copyFile(path.resolve(__dirname, 'templates', 'stryker-80x80.png'), path.resolve(this.baseDir, 'stryker-80x80.png')), util.copyFile(path.resolve(__dirname, 'templates', 'index.html'), path.resolve(this.baseDir, 'index.html')), util.writeFile(path.resolve(this.baseDir, 'bind-mutation-test-report.js'), bindMutationTestReport(report)) @@ -45,7 +47,9 @@ export default class HtmlReporter implements Reporter { this._baseDir = this.options.htmlReporter.baseDir; this.log.debug(`Using configured output folder ${this._baseDir}`); } else { - this.log.debug(`No base folder configuration found (using configuration: htmlReporter: { baseDir: 'output/folder' }), using default ${DEFAULT_BASE_FOLDER}`); + this.log.debug( + `No base folder configuration found (using configuration: htmlReporter: { baseDir: 'output/folder' }), using default ${DEFAULT_BASE_FOLDER}` + ); this._baseDir = DEFAULT_BASE_FOLDER; } } @@ -56,5 +60,4 @@ export default class HtmlReporter implements Reporter { await util.deleteDir(this.baseDir); await util.mkdir(this.baseDir); } - } diff --git a/packages/html-reporter/src/index.ts b/packages/html-reporter/src/index.ts index ba48afe51e..d18fc5854e 100644 --- a/packages/html-reporter/src/index.ts +++ b/packages/html-reporter/src/index.ts @@ -1,6 +1,4 @@ import { declareClassPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import HtmlReporter from './HtmlReporter'; -export const strykerPlugins = [ - declareClassPlugin(PluginKind.Reporter, 'html', HtmlReporter) -]; +export const strykerPlugins = [declareClassPlugin(PluginKind.Reporter, 'html', HtmlReporter)]; diff --git a/packages/html-reporter/test/unit/HtmlReporter.spec.ts b/packages/html-reporter/test/unit/HtmlReporter.spec.ts index 958daaf9c3..620ae8b900 100644 --- a/packages/html-reporter/test/unit/HtmlReporter.spec.ts +++ b/packages/html-reporter/test/unit/HtmlReporter.spec.ts @@ -23,7 +23,6 @@ describe(HtmlReporter.name, () => { }); describe('onMutationTestReportReady', () => { - it('should use configured base directory', async () => { testInjector.options.htmlReporter = { baseDir: 'foo/bar' }; actReportReady(); @@ -36,7 +35,9 @@ describe(HtmlReporter.name, () => { const expectedBaseDir = path.normalize('reports/mutation/html'); actReportReady(); await sut.wrapUp(); - expect(testInjector.logger.debug).calledWith(`No base folder configuration found (using configuration: htmlReporter: { baseDir: 'output/folder' }), using default ${expectedBaseDir}`); + expect(testInjector.logger.debug).calledWith( + `No base folder configuration found (using configuration: htmlReporter: { baseDir: 'output/folder' }), using default ${expectedBaseDir}` + ); expect(deleteDirStub).calledWith(expectedBaseDir); }); @@ -72,15 +73,11 @@ describe(HtmlReporter.name, () => { }; sut.onMutationTestReportReady(report); await sut.wrapUp(); - expect(writeFileStub).calledWith( - path.resolve('reports', 'mutation', 'html', 'bind-mutation-test-report.js'), - bindMutationTestReport(report) - ); + expect(writeFileStub).calledWith(path.resolve('reports', 'mutation', 'html', 'bind-mutation-test-report.js'), bindMutationTestReport(report)); }); }); describe('wrapUp', () => { - it('should resolve when everything is OK', () => { actReportReady(); return expect(sut.wrapUp()).eventually.undefined; diff --git a/packages/jasmine-framework/.eslintrc b/packages/jasmine-framework/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/jasmine-framework/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/jasmine-framework/src/JasmineTestFramework.ts b/packages/jasmine-framework/src/JasmineTestFramework.ts index ffc4abf959..55ea7049a8 100644 --- a/packages/jasmine-framework/src/JasmineTestFramework.ts +++ b/packages/jasmine-framework/src/JasmineTestFramework.ts @@ -1,15 +1,12 @@ import { TestFramework, TestSelection } from '@stryker-mutator/api/test_framework'; export default class JasmineTestFramework implements TestFramework { - - constructor() { } - /** * Creates a code fragment which, if included in a test run, * is run before a particular test is run. */ public beforeEach(codeFragment: string): string { - return ` + return ` jasmine.getEnv().addReporter({ specStarted: function () { ${codeFragment} @@ -22,7 +19,7 @@ export default class JasmineTestFramework implements TestFramework { * is run before a particular test is run. */ public afterEach(codeFragment: string): string { - return ` + return ` jasmine.getEnv().addReporter({ specDone: function () { ${codeFragment} diff --git a/packages/jasmine-framework/src/index.ts b/packages/jasmine-framework/src/index.ts index 58abb0cb3a..0fd4653cce 100644 --- a/packages/jasmine-framework/src/index.ts +++ b/packages/jasmine-framework/src/index.ts @@ -1,6 +1,4 @@ import { declareClassPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import JasmineTestFramework from './JasmineTestFramework'; -export const strykerPlugins = [ - declareClassPlugin(PluginKind.TestFramework, 'jasmine', JasmineTestFramework) -]; +export const strykerPlugins = [declareClassPlugin(PluginKind.TestFramework, 'jasmine', JasmineTestFramework)]; diff --git a/packages/jasmine-framework/test/integration/nestedSuite.it.spec.ts b/packages/jasmine-framework/test/integration/nestedSuite.it.spec.ts index 0fcefa9d86..21a43d07b1 100644 --- a/packages/jasmine-framework/test/integration/nestedSuite.it.spec.ts +++ b/packages/jasmine-framework/test/integration/nestedSuite.it.spec.ts @@ -16,7 +16,6 @@ interface JasmineTest { } describe('Selecting tests with nested suites', () => { - let sut: JasmineTestFramework; let testSelections: TestSelection[]; const jsonReporterFile = path.resolve(__dirname, '..', '..', 'testResources', 'json-reporter.js'); @@ -25,11 +24,7 @@ describe('Selecting tests with nested suites', () => { beforeEach(() => { sut = new JasmineTestFramework(); - testSelections = [ - { id: 0, name: 'outer test 1' }, - { id: 1, name: 'outer inner test 2' }, - { id: 2, name: 'outer test 3' } - ]; + testSelections = [{ id: 0, name: 'outer test 1' }, { id: 1, name: 'outer inner test 2' }, { id: 2, name: 'outer test 3' }]; }); afterEach(() => { diff --git a/packages/jasmine-framework/test/unit/JasmineTestFramework.spec.ts b/packages/jasmine-framework/test/unit/JasmineTestFramework.spec.ts index 6d5870bd91..610265db4b 100644 --- a/packages/jasmine-framework/test/unit/JasmineTestFramework.spec.ts +++ b/packages/jasmine-framework/test/unit/JasmineTestFramework.spec.ts @@ -3,20 +3,24 @@ import JasmineTestFramework from '../../src/JasmineTestFramework'; describe('JasmineTestFramework', () => { let sut: JasmineTestFramework; - beforeEach(() => sut = new JasmineTestFramework()); + beforeEach(() => (sut = new JasmineTestFramework())); describe('beforeEach()', () => { it('should result in a specStarted reporter hook', () => - expect(sut.beforeEach('fragment')).to.contain('fragment').and.to.contain('specStarted: function () {')); + expect(sut.beforeEach('fragment')) + .to.contain('fragment') + .and.to.contain('specStarted: function () {')); }); describe('afterEach()', () => { it('should result in an specDone reporter hook', () => - expect(sut.afterEach('fragment')).to.contain('fragment').and.to.contain('specDone: function () {')); + expect(sut.afterEach('fragment')) + .to.contain('fragment') + .and.to.contain('specDone: function () {')); }); describe('filter()', () => { - it('should result in a specFilter of jasmine it\'s', () => + it("should result in a specFilter of jasmine it's", () => expect(sut.filter([{ id: 5, name: 'test five' }, { id: 8, name: 'test eight' }])) .to.contain('jasmine.getEnv().specFilter = function (spec)') .and.to.contain('return ["test five","test eight"].indexOf(spec.getFullName()) !== -1;')); diff --git a/packages/jasmine-framework/test/unit/index.spec.ts b/packages/jasmine-framework/test/unit/index.spec.ts index 0a4b8ea3de..3478734bc3 100644 --- a/packages/jasmine-framework/test/unit/index.spec.ts +++ b/packages/jasmine-framework/test/unit/index.spec.ts @@ -3,7 +3,6 @@ import { strykerPlugins } from '../..'; import JasmineTestFramework from '../../src/JasmineTestFramework'; describe('index', () => { - it('should export strykerPlugins', () => { expect(strykerPlugins[0].injectableClass).eq(JasmineTestFramework); }); diff --git a/packages/jasmine-runner/.eslintrc b/packages/jasmine-runner/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/jasmine-runner/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/jasmine-runner/src/JasmineTestRunner.ts b/packages/jasmine-runner/src/JasmineTestRunner.ts index 4abc23fece..7441df2ba2 100644 --- a/packages/jasmine-runner/src/JasmineTestRunner.ts +++ b/packages/jasmine-runner/src/JasmineTestRunner.ts @@ -6,12 +6,11 @@ import { EOL } from 'os'; import { evalGlobal, Jasmine, toStrykerTestResult } from './helpers'; export default class JasmineTestRunner implements TestRunner { - private readonly jasmineConfigFile: string | undefined; private readonly Date: typeof Date = Date; // take Date prototype now we still can (user might choose to mock it away) public static inject = tokens(commonTokens.sandboxFileNames, commonTokens.options); - constructor(private readonly fileNames: ReadonlyArray, options: StrykerOptions) { + constructor(private readonly fileNames: readonly string[], options: StrykerOptions) { this.jasmineConfigFile = options.jasmineConfigFile; } @@ -45,7 +44,7 @@ export default class JasmineTestRunner implements TestRunner { jasmine.addReporter(reporter); jasmine.execute(); }).catch(error => ({ - errorMessages: ['An error occurred while loading your jasmine specs' + EOL + errorToString(error)], + errorMessages: [`An error occurred while loading your jasmine specs${EOL}${errorToString(error)}`], status: RunStatus.Error, tests: [] })); @@ -57,7 +56,9 @@ export default class JasmineTestRunner implements TestRunner { jasmine.loadConfigFile(this.jasmineConfigFile); jasmine.stopSpecOnExpectationFailure(true); jasmine.env.throwOnExpectationFailure(true); - jasmine.exit = () => { }; + console.log('test'); + + jasmine.exit = () => {}; jasmine.clearReporters(); jasmine.randomizeTests(false); return jasmine; diff --git a/packages/jasmine-runner/src/index.ts b/packages/jasmine-runner/src/index.ts index 176a018f30..1d5562f3e4 100644 --- a/packages/jasmine-runner/src/index.ts +++ b/packages/jasmine-runner/src/index.ts @@ -1,6 +1,4 @@ import { declareClassPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import JasmineTestRunner from './JasmineTestRunner'; -export const strykerPlugins = [ - declareClassPlugin(PluginKind.TestRunner, 'jasmine', JasmineTestRunner) -]; +export const strykerPlugins = [declareClassPlugin(PluginKind.TestRunner, 'jasmine', JasmineTestRunner)]; diff --git a/packages/jasmine-runner/test/helpers/assertions.ts b/packages/jasmine-runner/test/helpers/assertions.ts index bfa8e01dd1..a7c4b5a44f 100644 --- a/packages/jasmine-runner/test/helpers/assertions.ts +++ b/packages/jasmine-runner/test/helpers/assertions.ts @@ -1,15 +1,26 @@ import { TestResult, TestStatus } from '@stryker-mutator/api/test_runner'; import { expect } from 'chai'; -export function expectTestResultsToEqual(actualTestResults: TestResult[], expectedResults: ReadonlyArray<{ name: string, status: TestStatus, failureMessages: string[] | undefined }>) { - expect(actualTestResults).lengthOf(expectedResults.length, `Expected ${JSON.stringify(actualTestResults, null, 2)} to equal ${JSON.stringify(expectedResults, null, 2)}`); +export function expectTestResultsToEqual( + actualTestResults: TestResult[], + expectedResults: ReadonlyArray<{ name: string; status: TestStatus; failureMessages: string[] | undefined }> +) { + expect(actualTestResults).lengthOf( + expectedResults.length, + `Expected ${JSON.stringify(actualTestResults, null, 2)} to equal ${JSON.stringify(expectedResults, null, 2)}` + ); expectedResults.forEach(expectedResult => { const actualTestResult = actualTestResults.find(testResult => testResult.name === expectedResult.name); if (actualTestResult) { - expect({ name: actualTestResult.name, status: actualTestResult.status, failureMessages: actualTestResult.failureMessages }) - .deep.equal(expectedResult); + expect({ name: actualTestResult.name, status: actualTestResult.status, failureMessages: actualTestResult.failureMessages }).deep.equal( + expectedResult + ); } else { - expect.fail(undefined, undefined, `Could not find test result "${expectedResult.name}" in ${JSON.stringify(actualTestResults.map(_ => _.name))}`); + expect.fail( + undefined, + undefined, + `Could not find test result "${expectedResult.name}" in ${JSON.stringify(actualTestResults.map(_ => _.name))}` + ); } }); } diff --git a/packages/jasmine-runner/test/integration/JasmineRunner.it.spec.ts b/packages/jasmine-runner/test/integration/JasmineRunner.it.spec.ts index 2192fd84ed..22c5541934 100644 --- a/packages/jasmine-runner/test/integration/JasmineRunner.it.spec.ts +++ b/packages/jasmine-runner/test/integration/JasmineRunner.it.spec.ts @@ -14,44 +14,51 @@ function wrapInClosure(codeFragment: string) { } describe('JasmineRunner integration', () => { - let sut: JasmineTestRunner; afterEach(() => { process.chdir(path.resolve(__dirname, '../../..')); }); describe('using the jasmine-init project', () => { - - const expectedJasmineInitResults = Object.freeze([Object.freeze({ - failureMessages: undefined, - name: 'Player should be able to play a Song', - status: TestStatus.Success - }), Object.freeze({ - failureMessages: undefined, - name: 'Player when song has been paused should indicate that the song is currently paused', - status: TestStatus.Success - }), Object.freeze({ - failureMessages: undefined, - name: 'Player when song has been paused should be possible to resume', - status: TestStatus.Success - }), Object.freeze({ - failureMessages: undefined, - name: 'Player tells the current song if the user has made it a favorite', - status: TestStatus.Success - }), Object.freeze({ - failureMessages: undefined, - name: 'Player #resume should throw an exception if song is already playing', - status: TestStatus.Success - })]); + const expectedJasmineInitResults = Object.freeze([ + Object.freeze({ + failureMessages: undefined, + name: 'Player should be able to play a Song', + status: TestStatus.Success + }), + Object.freeze({ + failureMessages: undefined, + name: 'Player when song has been paused should indicate that the song is currently paused', + status: TestStatus.Success + }), + Object.freeze({ + failureMessages: undefined, + name: 'Player when song has been paused should be possible to resume', + status: TestStatus.Success + }), + Object.freeze({ + failureMessages: undefined, + name: 'Player tells the current song if the user has made it a favorite', + status: TestStatus.Success + }), + Object.freeze({ + failureMessages: undefined, + name: 'Player #resume should throw an exception if song is already playing', + status: TestStatus.Success + }) + ]); beforeEach(() => { process.chdir(path.resolve(__dirname, '../../testResources/jasmine-init')); - sut = new JasmineTestRunner([ + sut = new JasmineTestRunner( + [ path.resolve('lib', 'jasmine_examples', 'Player.js'), path.resolve('lib', 'jasmine_examples', 'Song.js'), path.resolve('spec', 'helpers', 'jasmine_examples', 'SpecHelper.js'), path.resolve('spec', 'jasmine_examples', 'PlayerSpec.js') - ], factory.strykerOptions({ jasmineConfigFile: 'spec/support/jasmine.json' })); + ], + factory.strykerOptions({ jasmineConfigFile: 'spec/support/jasmine.json' }) + ); }); it('should run the specs', async () => { const runResult = await sut.run({}); @@ -69,13 +76,18 @@ describe('JasmineRunner integration', () => { it('should be able to filter tests', async () => { // Arrange const testFramework = new JasmineTestFramework(); - const testHooks = wrapInClosure(testFramework.filter([{ - id: 1, - name: expectedJasmineInitResults[1].name - }, { - id: 3, - name: expectedJasmineInitResults[3].name - }])); + const testHooks = wrapInClosure( + testFramework.filter([ + { + id: 1, + name: expectedJasmineInitResults[1].name + }, + { + id: 3, + name: expectedJasmineInitResults[3].name + } + ]) + ); // Act const runResult = await sut.run({ testHooks }); @@ -88,14 +100,22 @@ describe('JasmineRunner integration', () => { it('should be able to filter tests in quick succession', async () => { // Arrange const testFramework = new JasmineTestFramework(); - const testHooks1 = wrapInClosure(testFramework.filter([{ - id: 1, - name: expectedJasmineInitResults[1].name - }])); - const testHooks2 = wrapInClosure(testFramework.filter([{ - id: 2, - name: expectedJasmineInitResults[2].name - }])); + const testHooks1 = wrapInClosure( + testFramework.filter([ + { + id: 1, + name: expectedJasmineInitResults[1].name + } + ]) + ); + const testHooks2 = wrapInClosure( + testFramework.filter([ + { + id: 2, + name: expectedJasmineInitResults[2].name + } + ]) + ); // Act const firstResult = await sut.run({ testHooks: testHooks1 }); @@ -107,16 +127,18 @@ describe('JasmineRunner integration', () => { }); function expectTestsFiltered(actualTestResults: TestResult[], ...filteredTestIds: number[]) { - expectTestResultsToEqual(actualTestResults, expectedJasmineInitResults.map((testResult, id) => ({ - failureMessages: testResult.failureMessages, - name: testResult.name, - status: filteredTestIds.indexOf(id) >= 0 ? TestStatus.Success : TestStatus.Skipped - }))); + expectTestResultsToEqual( + actualTestResults, + expectedJasmineInitResults.map((testResult, id) => ({ + failureMessages: testResult.failureMessages, + name: testResult.name, + status: filteredTestIds.includes(id) ? TestStatus.Success : TestStatus.Skipped + })) + ); } }); describe('using a jasmine-project with errors', () => { - beforeEach(() => { process.chdir(path.resolve(__dirname, '../../testResources/errors')); sut = new JasmineTestRunner([path.resolve('lib', 'error.js'), path.resolve('spec', 'errorSpec.js')], factory.strykerOptions()); @@ -136,20 +158,19 @@ describe('JasmineRunner integration', () => { describe('when it includes failed tests', () => { beforeEach(() => { process.chdir(path.resolve(__dirname, '../../testResources/test-failures')); - sut = new JasmineTestRunner([ - path.resolve('lib', 'foo.js'), - path.resolve('spec', 'fooSpec.js') - ], factory.strykerOptions()); + sut = new JasmineTestRunner([path.resolve('lib', 'foo.js'), path.resolve('spec', 'fooSpec.js')], factory.strykerOptions()); }); it('should complete with one test failure', async () => { const result = await sut.run({}); expect(result.status).eq(RunStatus.Complete); - expectTestResultsToEqual(result.tests, [{ - failureMessages: ['Expected \'bar\' to be \'baz\'.'], - name: 'foo should be baz', - status: TestStatus.Failed - }]); + expectTestResultsToEqual(result.tests, [ + { + failureMessages: ["Expected 'bar' to be 'baz'."], + name: 'foo should be baz', + status: TestStatus.Failed + } + ]); }); }); }); diff --git a/packages/jasmine-runner/test/unit/JasmineTestRunner.spec.ts b/packages/jasmine-runner/test/unit/JasmineTestRunner.spec.ts index ba0821bfb4..9ae1211ee9 100644 --- a/packages/jasmine-runner/test/unit/JasmineTestRunner.spec.ts +++ b/packages/jasmine-runner/test/unit/JasmineTestRunner.spec.ts @@ -12,7 +12,6 @@ type SinonStubbedInstance = { }; describe('JasmineTestRunner', () => { - let sandbox: sinon.SinonSandbox; let jasmineStub: SinonStubbedInstance; let evalGlobalStub: sinon.SinonStub; @@ -110,7 +109,8 @@ describe('JasmineTestRunner', () => { const result = await sut.run({}); expect(result.status).eq(RunStatus.Error); expect(result.errorMessages).lengthOf(1); - expect((result.errorMessages || [])[0]).matches(/An error occurred while loading your jasmine specs.*/) + expect((result.errorMessages || [])[0]) + .matches(/An error occurred while loading your jasmine specs.*/) .and.matches(/.*Error: foobar.*/); }); diff --git a/packages/javascript-mutator/.eslintrc b/packages/javascript-mutator/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/javascript-mutator/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/javascript-mutator/src/JavaScriptMutator.ts b/packages/javascript-mutator/src/JavaScriptMutator.ts index 3a32242727..b6b3507618 100644 --- a/packages/javascript-mutator/src/JavaScriptMutator.ts +++ b/packages/javascript-mutator/src/JavaScriptMutator.ts @@ -8,12 +8,8 @@ import copy from './helpers/copy'; import { NODE_MUTATORS_TOKEN, NodeMutator } from './mutators/NodeMutator'; export class JavaScriptMutator implements Mutator { - - public static inject = tokens(commonTokens.logger, NODE_MUTATORS_TOKEN) ; - constructor( - private readonly log: Logger, - private readonly mutators: ReadonlyArray - ) { } + public static inject = tokens(commonTokens.logger, NODE_MUTATORS_TOKEN); + constructor(private readonly log: Logger, private readonly mutators: readonly NodeMutator[]) {} public mutate(inputFiles: File[]): Mutant[] { const mutants: Mutant[] = []; diff --git a/packages/javascript-mutator/src/helpers/BabelHelper.ts b/packages/javascript-mutator/src/helpers/BabelHelper.ts index 6988137f5d..0e614efa10 100644 --- a/packages/javascript-mutator/src/helpers/BabelHelper.ts +++ b/packages/javascript-mutator/src/helpers/BabelHelper.ts @@ -5,7 +5,6 @@ import * as types from '@babel/types'; import { NodeWithParent } from './ParentNode'; export default class BabelHelper { - public static parse(code: string): types.File { return parse(code, this.createOptions()); } @@ -20,7 +19,7 @@ export default class BabelHelper { 'flow', 'jsx', 'objectRestSpread', - ['decorators', { decoratorsBeforeExport: true }] as any, + ['decorators', { decoratorsBeforeExport: true }] as any ], sourceType: 'unambiguous' }; @@ -35,8 +34,7 @@ export default class BabelHelper { node.parent = path.parent as any; Object.freeze(node); nodes.push(node); - }, - + } }); return nodes; @@ -45,5 +43,4 @@ export default class BabelHelper { public static generateCode(ast: types.Node) { return generate(ast).code; } - } diff --git a/packages/javascript-mutator/src/index.ts b/packages/javascript-mutator/src/index.ts index 5d9fe417f4..afeb07b67d 100644 --- a/packages/javascript-mutator/src/index.ts +++ b/packages/javascript-mutator/src/index.ts @@ -3,13 +3,9 @@ import { JavaScriptMutator } from './JavaScriptMutator'; import { nodeMutators } from './mutators'; import { NODE_MUTATORS_TOKEN } from './mutators/NodeMutator'; -export const strykerPlugins = [ - declareFactoryPlugin(PluginKind.Mutator, 'javascript', javaScriptMutatorFactory) -]; +export const strykerPlugins = [declareFactoryPlugin(PluginKind.Mutator, 'javascript', javaScriptMutatorFactory)]; function javaScriptMutatorFactory(injector: Injector): JavaScriptMutator { - return injector - .provideValue(NODE_MUTATORS_TOKEN, nodeMutators) - .injectClass(JavaScriptMutator); + return injector.provideValue(NODE_MUTATORS_TOKEN, nodeMutators).injectClass(JavaScriptMutator); } javaScriptMutatorFactory.inject = tokens(commonTokens.injector); diff --git a/packages/javascript-mutator/src/mutators/ArrayNewExpressionMutator.ts b/packages/javascript-mutator/src/mutators/ArrayNewExpressionMutator.ts index 5122f14be0..7215bba567 100644 --- a/packages/javascript-mutator/src/mutators/ArrayNewExpressionMutator.ts +++ b/packages/javascript-mutator/src/mutators/ArrayNewExpressionMutator.ts @@ -10,7 +10,12 @@ export default class ArrayNewExpressionMutator implements NodeMutator { public mutate(node: types.Node, copy: (obj: T, deep?: boolean) => T): void | types.Node[] { const nodes: types.Node[] = []; - if ((types.isCallExpression(node) || types.isNewExpression(node)) && types.isIdentifier(node.callee) && node.callee.name === 'Array' && node.arguments.length > 0) { + if ( + (types.isCallExpression(node) || types.isNewExpression(node)) && + types.isIdentifier(node.callee) && + node.callee.name === 'Array' && + node.arguments.length > 0 + ) { const mutatedNode = copy(node); mutatedNode.arguments = []; nodes.push(mutatedNode); diff --git a/packages/javascript-mutator/src/mutators/BinaryExpressionMutator.ts b/packages/javascript-mutator/src/mutators/BinaryExpressionMutator.ts index 0a910d682e..cea1a72521 100644 --- a/packages/javascript-mutator/src/mutators/BinaryExpressionMutator.ts +++ b/packages/javascript-mutator/src/mutators/BinaryExpressionMutator.ts @@ -17,12 +17,12 @@ export default class BinaryExpressionMutator implements NodeMutator { '===': '!==', '>': ['>=', '<='], '>=': ['>', '<'], - '||': '&&', + '||': '&&' }; public name = 'BinaryExpression'; - public mutate(node: types.Node, clone: (node: T, deep?: boolean) => T): void | types.Node[] { + public mutate(node: types.Node, clone: (node: T, deep?: boolean) => T): void | types.Node[] { if (types.isBinaryExpression(node) || types.isLogicalExpression(node)) { let mutatedOperators = this.operators[node.operator]; if (mutatedOperators) { diff --git a/packages/javascript-mutator/src/mutators/ConditionalExpressionMutator.ts b/packages/javascript-mutator/src/mutators/ConditionalExpressionMutator.ts index 737610ec80..29a05392f4 100644 --- a/packages/javascript-mutator/src/mutators/ConditionalExpressionMutator.ts +++ b/packages/javascript-mutator/src/mutators/ConditionalExpressionMutator.ts @@ -7,23 +7,10 @@ import { NodeMutator } from './NodeMutator'; * Represents a mutator which can remove the conditional clause from statements. */ export default class ConditionalExpressionMutator implements NodeMutator { - private readonly validOperators: string[] = [ - '!=', - '!==', - '&&', - '<', - '<=', - '==', - '===', - '>', - '>=', - '||', - ]; + private readonly validOperators: string[] = ['!=', '!==', '&&', '<', '<=', '==', '===', '>', '>=', '||']; public name = 'ConditionalExpression'; - constructor() {} - private hasValidParent(node: NodeWithParent): boolean { return ( !node.parent || @@ -37,19 +24,12 @@ export default class ConditionalExpressionMutator implements NodeMutator { } private isValidOperator(operator: string): boolean { - return this.validOperators.indexOf(operator) !== -1; + return this.validOperators.includes(operator); } public mutate(node: types.Node): types.Node[] | void { - if ( - (types.isBinaryExpression(node) || types.isLogicalExpression(node)) && - this.hasValidParent(node) && - this.isValidOperator(node.operator) - ) { - return [ - NodeGenerator.createBooleanLiteralNode(node, false), - NodeGenerator.createBooleanLiteralNode(node, true), - ]; + if ((types.isBinaryExpression(node) || types.isLogicalExpression(node)) && this.hasValidParent(node) && this.isValidOperator(node.operator)) { + return [NodeGenerator.createBooleanLiteralNode(node, false), NodeGenerator.createBooleanLiteralNode(node, true)]; } } } diff --git a/packages/javascript-mutator/src/mutators/DoStatementMutator.ts b/packages/javascript-mutator/src/mutators/DoStatementMutator.ts index 0764e5dcb7..d62907953d 100644 --- a/packages/javascript-mutator/src/mutators/DoStatementMutator.ts +++ b/packages/javascript-mutator/src/mutators/DoStatementMutator.ts @@ -8,11 +8,9 @@ import { NodeMutator } from './NodeMutator'; export default class DoStatementMutator implements NodeMutator { public name = 'DoStatement'; - constructor() { } - public mutate(node: types.Node): types.Node[] | void { if (types.isDoWhileStatement(node)) { - return [NodeGenerator.createBooleanLiteralNode(node.test, false)]; + return [NodeGenerator.createBooleanLiteralNode(node.test, false)]; } } } diff --git a/packages/javascript-mutator/src/mutators/ForStatementMutator.ts b/packages/javascript-mutator/src/mutators/ForStatementMutator.ts index 77909332e2..6839f1bce9 100644 --- a/packages/javascript-mutator/src/mutators/ForStatementMutator.ts +++ b/packages/javascript-mutator/src/mutators/ForStatementMutator.ts @@ -8,12 +8,10 @@ import { NodeMutator } from './NodeMutator'; export default class ForStatementMutator implements NodeMutator { public name = 'ForStatement'; - constructor() { } - public mutate(node: types.Node, copy: (obj: T, deep?: boolean) => T): types.Node[] | void { if (types.isForStatement(node)) { if (!node.test) { - const mutatedNode = copy(node) as types.ForStatement; + const mutatedNode = copy(node); mutatedNode.test = NodeGenerator.createBooleanLiteralNode(node, false); return [mutatedNode]; } else { diff --git a/packages/javascript-mutator/src/mutators/IfStatementMutator.ts b/packages/javascript-mutator/src/mutators/IfStatementMutator.ts index a277fb7782..2712a4868e 100644 --- a/packages/javascript-mutator/src/mutators/IfStatementMutator.ts +++ b/packages/javascript-mutator/src/mutators/IfStatementMutator.ts @@ -8,14 +8,9 @@ import { NodeMutator } from './NodeMutator'; export default class IfStatementMutator implements NodeMutator { public name = 'IfStatement'; - constructor() { } - public mutate(node: types.Node): types.Node[] | void { if (types.isIfStatement(node)) { - return [ - NodeGenerator.createBooleanLiteralNode(node.test, false), - NodeGenerator.createBooleanLiteralNode(node.test, true) - ]; + return [NodeGenerator.createBooleanLiteralNode(node.test, false), NodeGenerator.createBooleanLiteralNode(node.test, true)]; } } } diff --git a/packages/javascript-mutator/src/mutators/NodeMutator.ts b/packages/javascript-mutator/src/mutators/NodeMutator.ts index 4ca795f9a7..9b12b111c6 100644 --- a/packages/javascript-mutator/src/mutators/NodeMutator.ts +++ b/packages/javascript-mutator/src/mutators/NodeMutator.ts @@ -22,5 +22,5 @@ export interface NodeMutator { * @param copy A function to create a copy of an object. * @returns An array of mutated Nodes. */ - mutate(node: NodeWithParent, copy: (obj: T, deep?: boolean) => T): void | types.Node[]; + mutate(node: NodeWithParent, copy: (obj: T, deep?: boolean) => T): void | types.Node[]; } diff --git a/packages/javascript-mutator/src/mutators/StringLiteralMutator.ts b/packages/javascript-mutator/src/mutators/StringLiteralMutator.ts index c16c208c82..e61930be8d 100644 --- a/packages/javascript-mutator/src/mutators/StringLiteralMutator.ts +++ b/packages/javascript-mutator/src/mutators/StringLiteralMutator.ts @@ -25,8 +25,10 @@ export default class StringLiteralMutator implements NodeMutator { } nodes.push(mutatedNode); - } else if ((!node.parent || (!types.isImportDeclaration(node.parent) && !types.isExportDeclaration(node.parent) && !types.isJSXAttribute(node.parent))) - && types.isStringLiteral(node)) { + } else if ( + (!node.parent || (!types.isImportDeclaration(node.parent) && !types.isExportDeclaration(node.parent) && !types.isJSXAttribute(node.parent))) && + types.isStringLiteral(node) + ) { const mutatedNode = copy(node); mutatedNode.value = mutatedNode.value.length === 0 ? 'Stryker was here!' : ''; nodes.push(mutatedNode); diff --git a/packages/javascript-mutator/src/mutators/SwitchCaseMutator.ts b/packages/javascript-mutator/src/mutators/SwitchCaseMutator.ts index 5ac6993ee8..4548eddef6 100644 --- a/packages/javascript-mutator/src/mutators/SwitchCaseMutator.ts +++ b/packages/javascript-mutator/src/mutators/SwitchCaseMutator.ts @@ -8,7 +8,7 @@ import { NodeMutator } from './NodeMutator'; export default class SwitchCaseMutator implements NodeMutator { public name = 'SwitchCase'; - public mutate(node: NodeWithParent, copy: (obj: T, deep?: boolean) => T): void | types.Node[] { + public mutate(node: NodeWithParent, copy: (obj: T, deep?: boolean) => T): void | types.Node[] { if (types.isSwitchCase(node)) { // if not a fallthrough case if (node.consequent.length > 0) { diff --git a/packages/javascript-mutator/src/mutators/WhileStatementMutator.ts b/packages/javascript-mutator/src/mutators/WhileStatementMutator.ts index 8592bac47d..3645c35d36 100644 --- a/packages/javascript-mutator/src/mutators/WhileStatementMutator.ts +++ b/packages/javascript-mutator/src/mutators/WhileStatementMutator.ts @@ -8,8 +8,6 @@ import { NodeMutator } from './NodeMutator'; export default class WhileStatementMutator implements NodeMutator { public name = 'WhileStatement'; - constructor() { } - public mutate(node: types.Node): types.Node[] | void { if (types.isWhileStatement(node)) { return [NodeGenerator.createBooleanLiteralNode(node.test, false)]; diff --git a/packages/javascript-mutator/test/helpers/mutatorAssertions.ts b/packages/javascript-mutator/test/helpers/mutatorAssertions.ts index 0110a6ac14..a1ebc3d134 100644 --- a/packages/javascript-mutator/test/helpers/mutatorAssertions.ts +++ b/packages/javascript-mutator/test/helpers/mutatorAssertions.ts @@ -6,7 +6,7 @@ import { expect } from 'chai'; import { JavaScriptMutator } from '../../src/JavaScriptMutator'; import { NodeMutator } from '../../src/mutators/NodeMutator'; -type MutatorConstructor = new() => NodeMutator; +type MutatorConstructor = new () => NodeMutator; export function verifySpecification(specification: (name: string, expectMutation: ExpectMutation) => void, MutatorClass: MutatorConstructor): void { specification(new MutatorClass().name, (actual: string, ...expected: string[]) => expectMutation(new MutatorClass(), actual, ...expected)); @@ -27,7 +27,5 @@ export function expectMutation(mutator: NodeMutator, sourceText: string, ...expe * @param sourceText */ function mutantToString(mutant: Mutant, sourceText: string) { - return sourceText.substr(0, mutant.range[0]) + - mutant.replacement.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ') + - sourceText.substr(mutant.range[1]); + return sourceText.substr(0, mutant.range[0]) + mutant.replacement.replace(/\s{2,}/g, ' ').replace(/\n/g, ' ') + sourceText.substr(mutant.range[1]); } diff --git a/packages/javascript-mutator/test/unit/JavaScriptMutator.spec.ts b/packages/javascript-mutator/test/unit/JavaScriptMutator.spec.ts index 18178c9710..c4155b28c4 100644 --- a/packages/javascript-mutator/test/unit/JavaScriptMutator.spec.ts +++ b/packages/javascript-mutator/test/unit/JavaScriptMutator.spec.ts @@ -6,17 +6,14 @@ import { nodeMutators } from '../../src/mutators'; import { NODE_MUTATORS_TOKEN, NodeMutator } from '../../src/mutators/NodeMutator'; describe('JavaScriptMutator', () => { - - let selectedMutators: ReadonlyArray; + let selectedMutators: readonly NodeMutator[]; beforeEach(() => { selectedMutators = nodeMutators; }); function createSut() { - return testInjector.injector - .provideValue(NODE_MUTATORS_TOKEN, selectedMutators) - .injectClass(JavaScriptMutator); + return testInjector.injector.provideValue(NODE_MUTATORS_TOKEN, selectedMutators).injectClass(JavaScriptMutator); } it('should generate a correct mutant', () => { @@ -36,7 +33,10 @@ describe('JavaScriptMutator', () => { it('should generate mutant a correct mutant for jsx code', () => { const mutator = createSut(); - const files: File[] = [new File('testFile.jsx', ` + const files: File[] = [ + new File( + 'testFile.jsx', + ` "use strict"; import React from 'react' import { render } from 'react-dom' @@ -51,7 +51,9 @@ describe('JavaScriptMutator', () => { , document.getElementById('appContainer') ) - `)]; + ` + ) + ]; const mutants = mutator.mutate(files); @@ -66,7 +68,10 @@ describe('JavaScriptMutator', () => { it('should not mutate unknown extensions', () => { const mutator = createSut(); - const files: File[] = [new File('testFile.html', ` + const files: File[] = [ + new File( + 'testFile.html', + ` Test @@ -75,7 +80,9 @@ describe('JavaScriptMutator', () => {

Hello World

- `)]; + ` + ) + ]; const mutants = mutator.mutate(files); expect(mutants.length).to.equal(0); @@ -83,7 +90,10 @@ describe('JavaScriptMutator', () => { it('should generate mutants for flow code', () => { const mutator = createSut(); - const files: File[] = [new File('testFile.js', ` + const files: File[] = [ + new File( + 'testFile.js', + ` // @flow import React from 'react' @@ -104,7 +114,9 @@ describe('JavaScriptMutator', () => { } export default App - `)]; + ` + ) + ]; const mutants = mutator.mutate(files); @@ -119,7 +131,10 @@ describe('JavaScriptMutator', () => { it('should generate mutants for js vnext code', () => { const sut = createSut(); - const files: File[] = [new File('testFile.js', ` + const files: File[] = [ + new File( + 'testFile.js', + ` function objectRestSpread(input) { return { ...input, @@ -137,7 +152,9 @@ describe('JavaScriptMutator', () => { function dynamicImport(){ import('./guy').then(a) } - `)]; + ` + ) + ]; const mutants = sut.mutate(files); expect(mutants).lengthOf.above(2); @@ -154,12 +171,17 @@ describe('JavaScriptMutator', () => { it('should generate mutants when file contains a decorator', () => { const sut = createSut(); - const files: File[] = [new File('testFile.js', ` + const files: File[] = [ + new File( + 'testFile.js', + ` @decorator() export class Foo { bar = 'bar'; }; - `)]; + ` + ) + ]; const mutants = sut.mutate(files); expect(mutants).lengthOf.above(0); diff --git a/packages/javascript-mutator/test/unit/helpers/BabelParser.spec.ts b/packages/javascript-mutator/test/unit/helpers/BabelParser.spec.ts index c761b6d9bc..36c67d5edd 100644 --- a/packages/javascript-mutator/test/unit/helpers/BabelParser.spec.ts +++ b/packages/javascript-mutator/test/unit/helpers/BabelParser.spec.ts @@ -2,7 +2,6 @@ import { expect } from 'chai'; import BabelHelper from '../../../src/helpers/BabelHelper'; describe('BabelHelper', () => { - describe('getNodes', () => { it('should get the correct amount of statements', () => { const ast = BabelHelper.parse('"use strict"; var a = 1 + 2;'); @@ -31,13 +30,13 @@ describe('BabelHelper', () => { describe('parse', () => { it('should support scripts', () => { - const ast = BabelHelper.parse(`var fs = require('fs'); var a = 1 + 2;`); + const ast = BabelHelper.parse("var fs = require('fs'); var a = 1 + 2;"); expect(ast).exist; }); it('should support modules', () => { - const ast = BabelHelper.parse(`import fs from 'fs'; var a = 1 + 2;`); + const ast = BabelHelper.parse("import fs from 'fs'; var a = 1 + 2;"); expect(ast).exist; }); diff --git a/packages/javascript-mutator/test/unit/mutators/ArrayLiteralMutator.spec.ts b/packages/javascript-mutator/test/unit/mutators/ArrayLiteralMutator.spec.ts index 4937ec436c..7d8866c79d 100644 --- a/packages/javascript-mutator/test/unit/mutators/ArrayLiteralMutator.spec.ts +++ b/packages/javascript-mutator/test/unit/mutators/ArrayLiteralMutator.spec.ts @@ -2,14 +2,13 @@ import ArrayLiteralMutator from '../../../src/mutators/ArrayLiteralMutator'; import { expectMutation } from '../../helpers/mutatorAssertions'; describe('ArrayLiteralMutator', () => { - it('should mutate filled array literals as empty arrays', () => { expectMutation(new ArrayLiteralMutator(), '[a, 1 + 1]', '[]'); - expectMutation(new ArrayLiteralMutator(), `['val']`, '[]'); + expectMutation(new ArrayLiteralMutator(), "['val']", '[]'); }); it('should not mutate array initializers (leave that for ArrayNewExpressionMutator)', () => { - expectMutation(new ArrayLiteralMutator(), `new Array()`); - expectMutation(new ArrayLiteralMutator(), `new Array(1, 2, 3)`); + expectMutation(new ArrayLiteralMutator(), 'new Array()'); + expectMutation(new ArrayLiteralMutator(), 'new Array(1, 2, 3)'); }); }); diff --git a/packages/javascript-mutator/test/unit/mutators/ArrayNewExpressionMutator.spec.ts b/packages/javascript-mutator/test/unit/mutators/ArrayNewExpressionMutator.spec.ts index c45d0c8383..af0a09bf13 100644 --- a/packages/javascript-mutator/test/unit/mutators/ArrayNewExpressionMutator.spec.ts +++ b/packages/javascript-mutator/test/unit/mutators/ArrayNewExpressionMutator.spec.ts @@ -2,20 +2,18 @@ import ArrayNewExpressionMutator from '../../../src/mutators/ArrayNewExpressionM import { expectMutation } from '../../helpers/mutatorAssertions'; describe('ArrayNewExpressionMutator', () => { - it('should mutate filled array literals as empty arrays', () => { expectMutation(new ArrayNewExpressionMutator(), 'new Array(a, 1 + 1)', 'new Array()'); - expectMutation(new ArrayNewExpressionMutator(), `new Array('val')`, 'new Array()'); + expectMutation(new ArrayNewExpressionMutator(), "new Array('val')", 'new Array()'); }); it('should not mutate array literals (leave that for ArrayLiteralMutator)', () => { - expectMutation(new ArrayNewExpressionMutator(), `[]`); - expectMutation(new ArrayNewExpressionMutator(), `[1, 2 ,3]`); + expectMutation(new ArrayNewExpressionMutator(), '[]'); + expectMutation(new ArrayNewExpressionMutator(), '[1, 2 ,3]'); }); it('should not mutate other new expressions', () => { expectMutation(new ArrayNewExpressionMutator(), 'new Object(21, 2)'); expectMutation(new ArrayNewExpressionMutator(), 'new Arrays(21, 2)'); }); - }); diff --git a/packages/jest-runner/.eslintrc b/packages/jest-runner/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/jest-runner/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/jest-runner/src/JestConfigEditor.ts b/packages/jest-runner/src/JestConfigEditor.ts index 1cbf4948c2..65055944b9 100644 --- a/packages/jest-runner/src/JestConfigEditor.ts +++ b/packages/jest-runner/src/JestConfigEditor.ts @@ -9,7 +9,6 @@ import JEST_OVERRIDE_OPTIONS from './jestOverrideOptions'; const DEFAULT_PROJECT_NAME = 'custom'; export default class JestConfigEditor implements ConfigEditor { - public edit(strykerConfig: Config): void { // If there is no Jest property on the Stryker config create it strykerConfig.jest = strykerConfig.jest || {}; diff --git a/packages/jest-runner/src/JestTestRunner.ts b/packages/jest-runner/src/JestTestRunner.ts index d031a4da90..8f7927a74c 100644 --- a/packages/jest-runner/src/JestTestRunner.ts +++ b/packages/jest-runner/src/JestTestRunner.ts @@ -24,7 +24,12 @@ export default class JestTestRunner implements TestRunner { private readonly enableFindRelatedTests: boolean; public static inject = tokens(commonTokens.logger, commonTokens.options, PROCESS_ENV_TOKEN, JEST_TEST_ADAPTER_TOKEN); - public constructor(private readonly log: Logger, options: StrykerOptions, private readonly processEnvRef: NodeJS.ProcessEnv, private readonly jestTestAdapter: JestTestAdapter) { + constructor( + private readonly log: Logger, + options: StrykerOptions, + private readonly processEnvRef: NodeJS.ProcessEnv, + private readonly jestTestAdapter: JestTestAdapter + ) { // Get jest configuration from stryker options and assign it to jestConfig this.jestConfig = options.jest.config; @@ -37,7 +42,9 @@ export default class JestTestRunner implements TestRunner { 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.'); + 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 @@ -49,14 +56,20 @@ export default class JestTestRunner implements TestRunner { public async run(options: RunOptions): Promise { this.setNodeEnv(); - const { results } = await this.jestTestAdapter.run(this.jestConfig, process.cwd(), this.enableFindRelatedTests ? options.mutatedFileName : undefined); + const { results } = await this.jestTestAdapter.run( + this.jestConfig, + process.cwd(), + this.enableFindRelatedTests ? options.mutatedFileName : undefined + ); // Get the non-empty errorMessages from the jest RunResult, it's safe to cast to Array here because we filter the empty error messages - const errorMessages = results.testResults.map((testSuite: jest.TestResult) => testSuite.failureMessage).filter(errorMessage => (errorMessage)) as string[]; + const errorMessages = results.testResults + .map((testSuite: jest.TestResult) => testSuite.failureMessage) + .filter(errorMessage => errorMessage) as string[]; return { errorMessages, - status: (results.numRuntimeErrorTestSuites > 0) ? RunStatus.Error : RunStatus.Complete, + status: results.numRuntimeErrorTestSuites > 0 ? RunStatus.Error : RunStatus.Complete, tests: this.processTestResults(results.testResults) }; } diff --git a/packages/jest-runner/src/configLoaders/CustomJestConfigLoader.ts b/packages/jest-runner/src/configLoaders/CustomJestConfigLoader.ts index 25b1db22ed..90ca849d57 100644 --- a/packages/jest-runner/src/configLoaders/CustomJestConfigLoader.ts +++ b/packages/jest-runner/src/configLoaders/CustomJestConfigLoader.ts @@ -21,12 +21,16 @@ export default class CustomJestConfigLoader implements JestConfigLoader { private readConfigFromJestConfigFile() { try { return this._loader(path.join(this._projectRoot, 'jest.config.js')); - } catch { /* Don't return anything (implicitly return undefined) */ } + } catch { + /* Don't return anything (implicitly return undefined) */ + } } private readConfigFromPackageJson() { try { return JSON.parse(fs.readFileSync(path.join(this._projectRoot, 'package.json'), 'utf8')).jest; - } catch { /* Don't return anything (implicitly return undefined) */ } + } catch { + /* Don't return anything (implicitly return undefined) */ + } } } diff --git a/packages/jest-runner/src/configLoaders/ReactScriptsJestConfigLoader.ts b/packages/jest-runner/src/configLoaders/ReactScriptsJestConfigLoader.ts index 3bfc77d87a..a7de1f5796 100644 --- a/packages/jest-runner/src/configLoaders/ReactScriptsJestConfigLoader.ts +++ b/packages/jest-runner/src/configLoaders/ReactScriptsJestConfigLoader.ts @@ -6,7 +6,7 @@ import JestConfigLoader from './JestConfigLoader'; export default class ReactScriptsJestConfigLoader implements JestConfigLoader { private readonly projectRoot: string; - public constructor(projectRoot: string, private readonly resolve: RequireResolve = require.resolve) { + constructor(projectRoot: string, private readonly resolve: RequireResolve = require.resolve) { this.projectRoot = projectRoot; } @@ -22,11 +22,9 @@ export default class ReactScriptsJestConfigLoader implements JestConfigLoader { jestConfiguration.testEnvironment = 'jsdom'; return jestConfiguration; - } - catch (e) { + } catch (e) { if (this.isNodeErrnoException(e) && e.code === 'MODULE_NOT_FOUND') { - throw Error('Unable to locate package react-scripts. ' + - 'This package is required when projectType is set to "react".'); + throw Error('Unable to locate package react-scripts. This package is required when projectType is set to "react".'); } throw e; } @@ -37,10 +35,6 @@ export default class ReactScriptsJestConfigLoader implements JestConfigLoader { } private createJestConfig(reactScriptsLocation: string): jest.Configuration { - return createReactJestConfig( - (relativePath: string): string => path.join(reactScriptsLocation, relativePath), - this.projectRoot, - false - ); + return createReactJestConfig((relativePath: string): string => path.join(reactScriptsLocation, relativePath), this.projectRoot, false); } } diff --git a/packages/jest-runner/src/configLoaders/ReactScriptsTSJestConfigLoader.ts b/packages/jest-runner/src/configLoaders/ReactScriptsTSJestConfigLoader.ts index 31ffd8719e..4353c6a4c5 100644 --- a/packages/jest-runner/src/configLoaders/ReactScriptsTSJestConfigLoader.ts +++ b/packages/jest-runner/src/configLoaders/ReactScriptsTSJestConfigLoader.ts @@ -6,7 +6,7 @@ import JestConfigLoader from './JestConfigLoader'; export default class ReactScriptsTSJestConfigLoader implements JestConfigLoader { private readonly projectRoot: string; - public constructor(projectRoot: string, private readonly resolve = require.resolve) { + constructor(projectRoot: string, private readonly resolve = require.resolve) { this.projectRoot = projectRoot; } @@ -22,11 +22,9 @@ export default class ReactScriptsTSJestConfigLoader implements JestConfigLoader jestConfiguration.testEnvironment = 'jsdom'; return jestConfiguration; - } - catch (e) { + } catch (e) { if (this.isNodeErrnoException(e) && e.code === 'MODULE_NOT_FOUND') { - throw Error('Unable to locate package react-scripts-ts. ' + - 'This package is required when projectType is set to "react-ts".'); + throw Error('Unable to locate package react-scripts-ts. ' + 'This package is required when projectType is set to "react-ts".'); } throw e; } @@ -37,10 +35,6 @@ export default class ReactScriptsTSJestConfigLoader implements JestConfigLoader } private createJestConfig(reactScriptsTsLocation: string): jest.Configuration { - return createReactTsJestConfig( - (relativePath: string): string => path.join(reactScriptsTsLocation, relativePath), - this.projectRoot, - false - ); + return createReactTsJestConfig((relativePath: string): string => path.join(reactScriptsTsLocation, relativePath), this.projectRoot, false); } } diff --git a/packages/jest-runner/src/jestOverrideOptions.ts b/packages/jest-runner/src/jestOverrideOptions.ts index 1da36168de..9af70e2263 100644 --- a/packages/jest-runner/src/jestOverrideOptions.ts +++ b/packages/jest-runner/src/jestOverrideOptions.ts @@ -1,20 +1,20 @@ const JEST_OVERRIDE_OPTIONS = Object.freeze({ - // Prevent the user from using his or her own testResultProcessor because this might - // Mess with the way Stryker gets the results - testResultsProcessor: undefined, + // Prevent the user from using his or her own testResultProcessor because this might + // Mess with the way Stryker gets the results + testResultsProcessor: undefined, - // Disable code coverage, it is not used in Stryker and will only slow down the test runs - collectCoverage: false, + // Disable code coverage, it is not used in Stryker and will only slow down the test runs + collectCoverage: false, - // Disable verbose logging, this will only slow down Stryker test runs - verbose: false, + // Disable verbose logging, this will only slow down Stryker test runs + verbose: false, - // Disable bail so the jest process does not quit with a non-zero exit code - bail: false, + // Disable bail so the jest process does not quit with a non-zero exit code + bail: false, - // Disable notifications for test results, this will otherwise show a notification about - // the results each time Stryker runs the tests - notify: false + // Disable notifications for test results, this will otherwise show a notification about + // the results each time Stryker runs the tests + notify: false }); export default JEST_OVERRIDE_OPTIONS; diff --git a/packages/jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts b/packages/jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts index 21b15949e5..c3bc529b2b 100644 --- a/packages/jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts +++ b/packages/jest-runner/src/jestTestAdapters/JestPromiseTestAdapter.ts @@ -4,7 +4,6 @@ import jest from 'jest'; import JestTestAdapter from './JestTestAdapter'; export default class JestPromiseTestAdapter implements JestTestAdapter { - public static inject = tokens(commonTokens.logger); constructor(private readonly log: Logger) {} @@ -16,11 +15,14 @@ export default class JestPromiseTestAdapter implements JestTestAdapter { this.log.trace(`Only running tests related to ${fileNameUnderTest}`); } - return jest.runCLI({ - ...(fileNameUnderTest && { _: [fileNameUnderTest], findRelatedTests: true}), - config, - runInBand: true, - silent: true - }, [projectRoot]); + return jest.runCLI( + { + ...(fileNameUnderTest && { _: [fileNameUnderTest], findRelatedTests: true }), + config, + runInBand: true, + silent: true + }, + [projectRoot] + ); } } diff --git a/packages/jest-runner/src/jestTestAdapters/index.ts b/packages/jest-runner/src/jestTestAdapters/index.ts index 9ff88350d7..b7f5d463fb 100644 --- a/packages/jest-runner/src/jestTestAdapters/index.ts +++ b/packages/jest-runner/src/jestTestAdapters/index.ts @@ -16,6 +16,4 @@ export function jestTestAdapterFactory(log: Logger, jestVersion: string, injecto } jestTestAdapterFactory.inject = tokens(commonTokens.logger, JEST_VERSION_TOKEN, commonTokens.injector); -export { - JestTestAdapter -}; +export { JestTestAdapter }; diff --git a/packages/jest-runner/test/helpers/testResultProducer.ts b/packages/jest-runner/test/helpers/testResultProducer.ts index 5e0b26ae7c..96c763c5a4 100644 --- a/packages/jest-runner/test/helpers/testResultProducer.ts +++ b/packages/jest-runner/test/helpers/testResultProducer.ts @@ -25,10 +25,7 @@ export const createFailResult = () => ({ { ancestorTitles: ['App'], duration: 2, - failureMessages: [ - 'Fail message 1', - 'Fail message 2' - ], + failureMessages: ['Fail message 1', 'Fail message 2'], fullName: 'App render renders without crashing', numPassingAsserts: 0, status: 'failed', @@ -37,10 +34,7 @@ export const createFailResult = () => ({ { ancestorTitles: ['App'], duration: 0, - failureMessages: [ - 'Fail message 3', - 'Fail message 4' - ], + failureMessages: ['Fail message 3', 'Fail message 4'], fullName: 'App render renders without crashing', numPassingAsserts: 0, status: 'failed', @@ -187,7 +181,7 @@ export const createTodoResult = () => ({ sourceMaps: {}, testResults: [ { - ancestorTitles: [ 'App' ], + ancestorTitles: ['App'], duration: 4, failureMessages: [], fullName: 'App renders without crashing', @@ -197,7 +191,7 @@ export const createTodoResult = () => ({ title: 'renders without crashing' }, { - ancestorTitles: [ 'App' ], + ancestorTitles: ['App'], duration: 0, failureMessages: [], fullName: 'App renders without crashing with children', @@ -206,7 +200,7 @@ export const createTodoResult = () => ({ status: 'todo', title: 'renders without crashing with children' } - ], + ] } ], wasInterrupted: false diff --git a/packages/jest-runner/test/integration/JestConfigEditor.it.spec.ts b/packages/jest-runner/test/integration/JestConfigEditor.it.spec.ts index ba0c5207c5..250cc5403b 100644 --- a/packages/jest-runner/test/integration/JestConfigEditor.it.spec.ts +++ b/packages/jest-runner/test/integration/JestConfigEditor.it.spec.ts @@ -29,22 +29,8 @@ describe('Integration test for Jest ConfigEditor', () => { const expectedResult = { bail: false, collectCoverage: false, - collectCoverageFrom: [ - '!src/**/*.d.ts', - 'src/**/*.{js,jsx,ts,tsx}' - ], - moduleFileExtensions: [ - 'js', - 'json', - 'jsx', - 'node', - 'ts', - 'tsx', - 'web.js', - 'web.jsx', - 'web.ts', - 'web.tsx' - ], + collectCoverageFrom: ['!src/**/*.d.ts', 'src/**/*.{js,jsx,ts,tsx}'], + moduleFileExtensions: ['js', 'json', 'jsx', 'node', 'ts', 'tsx', 'web.js', 'web.jsx', 'web.ts', 'web.tsx'], moduleNameMapper: { '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', '^react-native$': 'react-native-web' @@ -54,20 +40,14 @@ describe('Integration test for Jest ConfigEditor', () => { setupFiles: [path.join(projectRoot, 'node_modules', 'react-app-polyfill', 'jsdom.js')], setupTestFrameworkScriptFile: undefined, testEnvironment: 'jsdom', - testMatch: [ - '/src/**/__tests__/**/*.{js,jsx,ts,tsx}', - '/src/**/*.{spec,test}.{js,jsx,ts,tsx}' - ], + testMatch: ['/src/**/__tests__/**/*.{js,jsx,ts,tsx}', '/src/**/*.{spec,test}.{js,jsx,ts,tsx}'], testResultsProcessor: undefined, transform: { '^(?!.*\\.(js|jsx|ts|tsx|css|json)$)': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'fileTransform.js'), '^.+\\.(js|jsx|ts|tsx)$': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'babelTransform.js'), - '^.+\\\.css$': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'cssTransform.js'), + '^.+\\.css$': path.join(projectRoot, 'node_modules', 'react-scripts', 'config', 'jest', 'cssTransform.js') }, - transformIgnorePatterns: [ - '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$', - '^.+\\.module\\.(css|sass|scss)$' - ], + transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|ts|tsx)$', '^.+\\.module\\.(css|sass|scss)$'], verbose: false }; @@ -82,28 +62,13 @@ describe('Integration test for Jest ConfigEditor', () => { const expectedResult = { bail: false, collectCoverage: false, - collectCoverageFrom: [ - '!**/*.d.ts', - 'src/**/*.{js,jsx,ts,tsx}' - ], + collectCoverageFrom: ['!**/*.d.ts', 'src/**/*.{js,jsx,ts,tsx}'], globals: { 'ts-jest': { - tsConfigFile: path.join(projectRoot, 'testResources', 'reactTsProject', 'tsconfig.test.json'), - }, + tsConfigFile: path.join(projectRoot, 'testResources', 'reactTsProject', 'tsconfig.test.json') + } }, - moduleFileExtensions: [ - 'web.ts', - 'ts', - 'web.tsx', - 'tsx', - 'web.js', - 'js', - 'web.jsx', - 'jsx', - 'json', - 'node', - 'mjs' - ], + moduleFileExtensions: ['web.ts', 'ts', 'web.tsx', 'tsx', 'web.js', 'js', 'web.jsx', 'jsx', 'json', 'node', 'mjs'], moduleNameMapper: { '^react-native$': 'react-native-web' }, @@ -112,21 +77,16 @@ describe('Integration test for Jest ConfigEditor', () => { setupFiles: [path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'polyfills.js')], setupTestFrameworkScriptFile: undefined, testEnvironment: 'jsdom', - testMatch: [ - '/src/**/__tests__/**/*.(j|t)s?(x)', - '/src/**/?(*.)(spec|test).(j|t)s?(x)' - ], + testMatch: ['/src/**/__tests__/**/*.(j|t)s?(x)', '/src/**/?(*.)(spec|test).(j|t)s?(x)'], testResultsProcessor: undefined, testURL: 'http://localhost', transform: { '^(?!.*\\.(js|jsx|mjs|css|json)$)': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'fileTransform.js'), '^.+\\.(js|jsx|mjs)$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'babelTransform.js'), - '^.+\\\.css$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'cssTransform.js'), - '^.+\\.tsx?$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'typescriptTransform.js'), + '^.+\\.css$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'cssTransform.js'), + '^.+\\.tsx?$': path.join(projectRoot, 'node_modules', 'react-scripts-ts', 'config', 'jest', 'typescriptTransform.js') }, - transformIgnorePatterns: [ - '[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|ts|tsx)$' - ], + transformIgnorePatterns: ['[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|ts|tsx)$'], verbose: false }; @@ -183,7 +143,7 @@ describe('Integration test for Jest ConfigEditor', () => { collectCoverage: false, notify: false, testResultsProcessor: undefined, - verbose: false, + verbose: false }); }); diff --git a/packages/jest-runner/test/integration/StrykerJestRunner.it.spec.ts b/packages/jest-runner/test/integration/StrykerJestRunner.it.spec.ts index 9945244fd4..09c42f5369 100644 --- a/packages/jest-runner/test/integration/StrykerJestRunner.it.spec.ts +++ b/packages/jest-runner/test/integration/StrykerJestRunner.it.spec.ts @@ -45,9 +45,7 @@ describe('Integration test for Strykers Jest runner', () => { config.jest = jestConfig; } jestConfigEditor.edit(config); - return testInjector.injector - .provideValue(commonTokens.options, config) - .injectFunction(jestTestRunnerFactory); + return testInjector.injector.provideValue(commonTokens.options, config).injectFunction(jestTestRunnerFactory); } it('should run tests on the example React + TypeScript project', async () => { @@ -73,7 +71,9 @@ describe('Integration test for Strykers Jest runner', () => { 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); + expect(result.tests) + .to.be.an('array') + .with.length(testNames.length); for (const test of result.tests) { expect(testNames).to.include(test.name); @@ -94,7 +94,9 @@ describe('Integration test for Strykers Jest runner', () => { 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); + expect(result.tests) + .to.be.an('array') + .with.length(testNames.length); for (const test of result.tests) { expect(testNames).to.include(test.name); diff --git a/packages/jest-runner/test/unit/JestConfigEditor.spec.ts b/packages/jest-runner/test/unit/JestConfigEditor.spec.ts index 443df5bcec..d7662b8f94 100644 --- a/packages/jest-runner/test/unit/JestConfigEditor.spec.ts +++ b/packages/jest-runner/test/unit/JestConfigEditor.spec.ts @@ -24,7 +24,12 @@ describe('JestConfigEditor', () => { sinon.stub(reactScriptsJestConfigLoader, 'default').returns(reactScriptsJestConfigLoaderStub); sinon.stub(reactScriptsTSJestConfigLoader, 'default').returns(reactScriptsTSJestConfigLoaderStub); - const defaultOptions: Partial = { collectCoverage: true, verbose: true, bail: false, testResultsProcessor: 'someResultProcessor' }; + const defaultOptions: Partial = { + collectCoverage: true, + verbose: true, + bail: false, + testResultsProcessor: 'someResultProcessor' + }; customConfigLoaderStub.loadConfig.returns(defaultOptions); reactScriptsJestConfigLoaderStub.loadConfig.returns(defaultOptions); reactScriptsTSJestConfigLoaderStub.loadConfig.returns(defaultOptions); @@ -40,7 +45,7 @@ describe('JestConfigEditor', () => { assert(customConfigLoaderStub.loadConfig.calledOnce, 'CustomConfigLoader loadConfig not called'); }); - it('should call the ReactScriptsJestConfigLoader loadConfig method when \'react\' is defined as projectType', () => { + it("should call the ReactScriptsJestConfigLoader loadConfig method when 'react' is defined as projectType", () => { config.set({ jest: { projectType: 'react' } }); sut.edit(config); @@ -48,7 +53,7 @@ describe('JestConfigEditor', () => { assert(reactScriptsJestConfigLoaderStub.loadConfig.calledOnce, 'ReactScriptsJestConfigLoader loadConfig not called'); }); - it('should call the ReactScriptsTSJestConfigLoader loadConfig method when \'react-ts\' is defined as projectType', () => { + it("should call the ReactScriptsTSJestConfigLoader loadConfig method when 'react-ts' is defined as projectType", () => { config.set({ jest: { projectType: 'react-ts' } }); sut.edit(config); diff --git a/packages/jest-runner/test/unit/JestTestRunner.spec.ts b/packages/jest-runner/test/unit/JestTestRunner.spec.ts index dfd77f5a4a..3c7b20d0b8 100644 --- a/packages/jest-runner/test/unit/JestTestRunner.spec.ts +++ b/packages/jest-runner/test/unit/JestTestRunner.spec.ts @@ -27,7 +27,7 @@ describe('JestTestRunner', () => { jestTestRunner = testInjector.injector .provideValue(PROCESS_ENV_TOKEN, processEnvMock) - .provideValue(JEST_TEST_ADAPTER_TOKEN, jestTestAdapterMock as unknown as JestTestAdapter) + .provideValue(JEST_TEST_ADAPTER_TOKEN, (jestTestAdapterMock as unknown) as JestTestAdapter) .injectClass(JestTestRunner); }); @@ -112,30 +112,26 @@ describe('JestTestRunner', () => { expect(result).to.deep.equal({ errorMessages: ['test failed - App.test.js'], status: RunStatus.Complete, - tests: [{ - failureMessages: [ - 'Fail message 1', - 'Fail message 2' - ], - name: 'App render renders without crashing', - status: TestStatus.Failed, - timeSpentMs: 2 - }, - { - failureMessages: [ - 'Fail message 3', - 'Fail message 4' - ], - name: 'App render renders without crashing', - status: TestStatus.Failed, - timeSpentMs: 0 - }, - { - failureMessages: [], - name: 'App renders without crashing', - status: TestStatus.Success, - timeSpentMs: 23 - }] + tests: [ + { + failureMessages: ['Fail message 1', 'Fail message 2'], + name: 'App render renders without crashing', + status: TestStatus.Failed, + timeSpentMs: 2 + }, + { + failureMessages: ['Fail message 3', 'Fail message 4'], + name: 'App render renders without crashing', + status: TestStatus.Failed, + timeSpentMs: 0 + }, + { + failureMessages: [], + name: 'App renders without crashing', + status: TestStatus.Success, + timeSpentMs: 23 + } + ] }); }); @@ -151,7 +147,7 @@ describe('JestTestRunner', () => { }); }); - it('should set process.env.NODE_ENV to \'test\' when process.env.NODE_ENV is null', async () => { + it("should set process.env.NODE_ENV to 'test' when process.env.NODE_ENV is null", async () => { await jestTestRunner.run(runOptions); expect(processEnvMock.NODE_ENV).to.equal('test'); diff --git a/packages/jest-runner/test/unit/configLoaders/DefaultConfigLoader.spec.ts b/packages/jest-runner/test/unit/configLoaders/DefaultConfigLoader.spec.ts index 09d3d4c107..06aaa3acd4 100644 --- a/packages/jest-runner/test/unit/configLoaders/DefaultConfigLoader.spec.ts +++ b/packages/jest-runner/test/unit/configLoaders/DefaultConfigLoader.spec.ts @@ -33,7 +33,10 @@ describe(`${CustomJestConfigLoader.name} integration`, () => { requireStub.throws(Error('ENOENT: no such file or directory, open package.json')); const config = sut.loadConfig(); - assert(fsStub.readFileSync.calledWith(path.join(projectRoot, 'package.json'), 'utf8'), `readFileSync not called with ${projectRoot}/package.json`); + assert( + fsStub.readFileSync.calledWith(path.join(projectRoot, 'package.json'), 'utf8'), + `readFileSync not called with ${projectRoot}/package.json` + ); expect(config).to.deep.equal({ exampleProperty: 'examplePackageJsonValue' }); diff --git a/packages/jest-runner/test/unit/configLoaders/ReactScriptsJestConfigLoader.spec.ts b/packages/jest-runner/test/unit/configLoaders/ReactScriptsJestConfigLoader.spec.ts index 67641de968..1e57f1f8f3 100644 --- a/packages/jest-runner/test/unit/configLoaders/ReactScriptsJestConfigLoader.spec.ts +++ b/packages/jest-runner/test/unit/configLoaders/ReactScriptsJestConfigLoader.spec.ts @@ -23,7 +23,7 @@ describe(ReactScriptsJestConfigLoader.name, () => { requireResolveStub = sinon.stub(); requireResolveStub.returns(reactScriptsPackagePath); - sut = new ReactScriptsJestConfigLoader(projectRoot, requireResolveStub as unknown as RequireResolve); + sut = new ReactScriptsJestConfigLoader(projectRoot, (requireResolveStub as unknown) as RequireResolve); }); it('should load the configuration via the createJestConfig method provided by react-scripts', () => { @@ -50,7 +50,6 @@ describe(ReactScriptsJestConfigLoader.name, () => { requireResolveStub.throws(error); // Act & Assert - expect(() => sut.loadConfig()) - .throws('Unable to locate package react-scripts. This package is required when projectType is set to "react".'); + expect(() => sut.loadConfig()).throws('Unable to locate package react-scripts. This package is required when projectType is set to "react".'); }); }); diff --git a/packages/jest-runner/test/unit/configLoaders/ReactScriptsTsJestConfigLoader.spec.ts b/packages/jest-runner/test/unit/configLoaders/ReactScriptsTsJestConfigLoader.spec.ts index 3f942646ea..0362c7b7bb 100644 --- a/packages/jest-runner/test/unit/configLoaders/ReactScriptsTsJestConfigLoader.spec.ts +++ b/packages/jest-runner/test/unit/configLoaders/ReactScriptsTsJestConfigLoader.spec.ts @@ -23,7 +23,7 @@ describe(ReactScriptsTSJestConfigLoader.name, () => { requireResolveStub = sinon.stub(); requireResolveStub.returns(reactScriptsTsPackagePath); - sut = new ReactScriptsTSJestConfigLoader(projectRoot, requireResolveStub as unknown as RequireResolve); + sut = new ReactScriptsTSJestConfigLoader(projectRoot, (requireResolveStub as unknown) as RequireResolve); }); it('should load the configuration via the createJestConfig method provided by react-scripts-ts', () => { @@ -50,7 +50,8 @@ describe(ReactScriptsTSJestConfigLoader.name, () => { requireResolveStub.throws(error); // Act & Assert - expect(() => sut.loadConfig()) - .throws('Unable to locate package react-scripts-ts. This package is required when projectType is set to "react-ts".'); + expect(() => sut.loadConfig()).throws( + 'Unable to locate package react-scripts-ts. This package is required when projectType is set to "react-ts".' + ); }); }); diff --git a/packages/jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapter.spec.ts b/packages/jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapter.spec.ts index 43ff08f7b7..e87232907e 100644 --- a/packages/jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapter.spec.ts +++ b/packages/jest-runner/test/unit/jestTestAdapters/JestPromiseTestAdapter.spec.ts @@ -14,10 +14,12 @@ describe(JestPromiseTestAdapter.name, () => { beforeEach(() => { runCLIStub = sinon.stub(jest, 'runCLI'); - runCLIStub.callsFake((config: object) => Promise.resolve({ - config, - result: 'testResult' - })); + runCLIStub.callsFake((config: object) => + Promise.resolve({ + config, + result: 'testResult' + }) + ); sut = testInjector.injector.injectClass(JestPromiseTestAdapter); }); @@ -31,23 +33,33 @@ describe(JestPromiseTestAdapter.name, () => { it('should call the runCLI method with the correct projectRoot', async () => { await sut.run(jestConfig, projectRoot); - assert(runCLIStub.calledWith({ - config: JSON.stringify({ rootDir: projectRoot, reporters: [] }), - runInBand: true, - silent: true - }, [projectRoot])); + assert( + runCLIStub.calledWith( + { + config: JSON.stringify({ rootDir: projectRoot, reporters: [] }), + runInBand: true, + silent: true + }, + [projectRoot] + ) + ); }); it('should call the runCLI method with the --findRelatedTests flag', async () => { await sut.run(jestConfig, projectRoot, fileNameUnderTest); - assert(runCLIStub.calledWith({ - _: [fileNameUnderTest], - config: JSON.stringify({ rootDir: projectRoot, reporters: [] }), - findRelatedTests: true, - runInBand: true, - silent: true - }, [projectRoot])); + assert( + runCLIStub.calledWith( + { + _: [fileNameUnderTest], + config: JSON.stringify({ rootDir: projectRoot, reporters: [] }), + findRelatedTests: true, + runInBand: true, + silent: true + }, + [projectRoot] + ) + ); }); it('should call the runCLI method and return the test result', async () => { diff --git a/packages/jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactory.spec.ts b/packages/jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactory.spec.ts index d5d0446898..ca12a588df 100644 --- a/packages/jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactory.spec.ts +++ b/packages/jest-runner/test/unit/jestTestAdapters/JestTestAdapterFactory.spec.ts @@ -7,9 +7,7 @@ describe(jestTestAdapterFactory.name, () => { let jestVersion: string; function act(): JestTestAdapter { - return testInjector.injector - .provideValue(JEST_VERSION_TOKEN, jestVersion) - .injectFunction(jestTestAdapterFactory); + return testInjector.injector.provideValue(JEST_VERSION_TOKEN, jestVersion).injectFunction(jestTestAdapterFactory); } it('should return a Promise-based adapter when the Jest version is higher or equal to 22.0.0', () => { @@ -25,5 +23,4 @@ describe(jestTestAdapterFactory.name, () => { expect(() => act()).to.throw(Error, 'You need Jest version >= 22.0.0 to use Stryker'); expect(testInjector.logger.debug).calledWith('Detected Jest below 22.0.0: %s', jestVersion); }); - }); diff --git a/packages/karma-runner/.eslintrc b/packages/karma-runner/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/karma-runner/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/karma-runner/src/KarmaTestRunner.ts b/packages/karma-runner/src/KarmaTestRunner.ts index 8b09c1c12b..d359fe7f93 100644 --- a/packages/karma-runner/src/KarmaTestRunner.ts +++ b/packages/karma-runner/src/KarmaTestRunner.ts @@ -38,8 +38,11 @@ export default class KarmaTestRunner implements TestRunner { public init(): Promise { return new Promise((res, rej) => { StrykerReporter.instance.once('browsers_ready', res); - this.starter.start() - .then(() => { /*noop*/ }) + this.starter + .start() + .then(() => { + /*noop*/ + }) .catch(rej); }); } @@ -91,7 +94,7 @@ export default class KarmaTestRunner implements TestRunner { StrykerReporter.instance.on('server_start', (port: number) => { this.port = port; }); - StrykerReporter.instance.on('server_start', () => { }); + StrykerReporter.instance.on('server_start', () => {}); } private listenToCoverage() { @@ -137,9 +140,7 @@ export default class KarmaTestRunner implements TestRunner { private determineRunState() { // Karma will report an Error if no tests had executed. // This is not an "error" in Stryker terms - if (this.currentRunStatus === RunStatus.Error && - !this.currentErrorMessages.length && - !this.currentTestResults.length) { + if (this.currentRunStatus === RunStatus.Error && !this.currentErrorMessages.length && !this.currentTestResults.length) { return RunStatus.Complete; } else if (this.currentErrorMessages.length) { // Karma will return Complete when there are runtime errors diff --git a/packages/karma-runner/src/StrykerReporter.ts b/packages/karma-runner/src/StrykerReporter.ts index 3328c1c87d..fc40e89485 100644 --- a/packages/karma-runner/src/StrykerReporter.ts +++ b/packages/karma-runner/src/StrykerReporter.ts @@ -21,16 +21,14 @@ export interface KarmaSpec { * i.e. use `public readonly onFoo = () => {}` instead of `onFoo() { }`. */ export default class StrykerReporter extends EventEmitter implements karma.Reporter { - public adapters: any[] = []; private constructor() { - super(); } private static _instance = new StrykerReporter(); - static get instance(): StrykerReporter { + public static get instance(): StrykerReporter { if (!this._instance) { this._instance = new StrykerReporter(); } @@ -39,7 +37,7 @@ export default class StrykerReporter extends EventEmitter implements karma.Repor public readonly onListening = (port: number) => { this.emit('server_start', port); - } + }; public readonly onSpecComplete = (_browser: any, spec: KarmaSpec) => { const name = spec.suite.reduce((name, suite) => name + suite + ' ', '') + spec.description; @@ -56,23 +54,23 @@ export default class StrykerReporter extends EventEmitter implements karma.Repor timeSpentMs: spec.time }; this.emit('test_result', testResult); - } + }; public readonly onRunComplete = (runResult: karma.TestResults) => { this.emit('run_complete', this.collectRunState(runResult)); - } + }; public readonly onLoadError = (...args: any[]) => { this.emit('load_error', ...args); - } + }; public readonly onBrowserComplete = (_browser: any, result: { coverage: CoverageCollection | CoverageCollectionPerTest }) => { this.emit('coverage_report', result.coverage); - } + }; public readonly onBrowsersReady = () => { this.emit('browsers_ready'); - } + }; public readonly onBrowserError = (_browser: any, error: any) => { // Karma 2.0 has different error messages @@ -81,13 +79,13 @@ export default class StrykerReporter extends EventEmitter implements karma.Repor } else { this.emit('browser_error', error.toString()); } - } + }; public readonly onCompileError = (errors: string[]) => { // This is called from angular cli logic // https://github.com/angular/angular-cli/blob/012672161087a05ae5ecffbed5d1ee307ce1e0ad/packages/angular_devkit/build_angular/src/angular-cli-files/plugins/karma.ts#L96 this.emit('compile_error', errors); - } + }; private collectRunState(runResult: karma.TestResults): RunStatus { if (runResult.disconnected) { @@ -98,5 +96,4 @@ export default class StrykerReporter extends EventEmitter implements karma.Repor return RunStatus.Complete; } } - } diff --git a/packages/karma-runner/src/TestHooksMiddleware.ts b/packages/karma-runner/src/TestHooksMiddleware.ts index 417f77ef7a..fa9d17a4f4 100644 --- a/packages/karma-runner/src/TestHooksMiddleware.ts +++ b/packages/karma-runner/src/TestHooksMiddleware.ts @@ -5,14 +5,13 @@ import * as url from 'url'; export const TEST_HOOKS_FILE_NAME = require.resolve('./testHooksForStryker'); export default class TestHooksMiddleware { - private constructor() { // This `.bind` call is important! The `handler` will be executed with `.apply` (or friends) and otherwise the `this` won't point to this instance! this.handler = this.handler.bind(this); } private static _instance: TestHooksMiddleware; - static get instance(): TestHooksMiddleware { + public static get instance(): TestHooksMiddleware { if (!this._instance) { this._instance = new TestHooksMiddleware(); } @@ -32,5 +31,5 @@ export default class TestHooksMiddleware { } else { next(); } - } + }; } diff --git a/packages/karma-runner/src/index.ts b/packages/karma-runner/src/index.ts index 91a9def635..147aed4869 100644 --- a/packages/karma-runner/src/index.ts +++ b/packages/karma-runner/src/index.ts @@ -1,6 +1,4 @@ import { declareClassPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import KarmaTestRunner from './KarmaTestRunner'; -export const strykerPlugins = [ - declareClassPlugin(PluginKind.TestRunner, 'karma', KarmaTestRunner) -]; +export const strykerPlugins = [declareClassPlugin(PluginKind.TestRunner, 'karma', KarmaTestRunner)]; diff --git a/packages/karma-runner/src/starters/angularStarter.ts b/packages/karma-runner/src/starters/angularStarter.ts index 63868bb663..70681e5d89 100644 --- a/packages/karma-runner/src/starters/angularStarter.ts +++ b/packages/karma-runner/src/starters/angularStarter.ts @@ -16,11 +16,7 @@ export async function start(getLogger: LoggerFactoryMethod, ngConfig?: NgConfigO if ('default' in cli) { cli = cli.default; } - const cliArgs = [ - 'test', - '--progress=false', - `--karma-config=${require.resolve('./stryker-karma.conf')}` - ]; + const cliArgs = ['test', '--progress=false', `--karma-config=${require.resolve('./stryker-karma.conf')}`]; if (ngConfig && ngConfig.testArguments) { const testArguments: NgTestArguments = ngConfig.testArguments; @@ -41,7 +37,9 @@ export async function start(getLogger: LoggerFactoryMethod, ngConfig?: NgConfigO outputStream: process.stdout }).then((exitCode: number) => { if (exitCode > 0) { - throw new Error(`\`ng test\` command failed with exit code ${exitCode}. Please run with logLevel 'trace' to see the angular-cli console output (actual command was ${actualCommand})`); + throw new Error( + `\`ng test\` command failed with exit code ${exitCode}. Please run with logLevel 'trace' to see the angular-cli console output (actual command was ${actualCommand})` + ); } }); } @@ -49,18 +47,12 @@ export async function start(getLogger: LoggerFactoryMethod, ngConfig?: NgConfigO function verifyAngularCliVersion() { const version = semver.coerce(requireModule('@angular/cli/package').version); if (!version || semver.lt(version, MIN_ANGULAR_CLI_VERSION)) { - throw new Error( - `Your @angular/cli version (${version}) is not supported. Please install ${MIN_ANGULAR_CLI_VERSION} or higher` - ); + throw new Error(`Your @angular/cli version (${version}) is not supported. Please install ${MIN_ANGULAR_CLI_VERSION} or higher`); } } function verifyNgTestArguments(ngTestArguments: string[]) { - const prefixedArguments = ngTestArguments.filter(key => - key.trim().startsWith('-') - ); + const prefixedArguments = ngTestArguments.filter(key => key.trim().startsWith('-')); if (prefixedArguments.length > 0) { - throw new Error( - `Don't prefix arguments with dashes ('-'). Stryker will do this automatically. Problematic arguments are ${prefixedArguments}.` - ); + throw new Error(`Don't prefix arguments with dashes ('-'). Stryker will do this automatically. Problematic arguments are ${prefixedArguments}.`); } } diff --git a/packages/karma-runner/src/starters/karmaStarter.ts b/packages/karma-runner/src/starters/karmaStarter.ts index e361935cab..ba623979a9 100644 --- a/packages/karma-runner/src/starters/karmaStarter.ts +++ b/packages/karma-runner/src/starters/karmaStarter.ts @@ -4,6 +4,6 @@ export async function start(): Promise { // Make sure require karma from inside this function, that way it won't break if karma isn't installed and this file is required. const karma = requireModule('karma'); await new karma.Server({ - configFile: require.resolve('./stryker-karma.conf'), + configFile: require.resolve('./stryker-karma.conf') }).start(); } diff --git a/packages/karma-runner/src/starters/stryker-karma.conf.ts b/packages/karma-runner/src/starters/stryker-karma.conf.ts index 0bedd5bd4d..7bc2c92a46 100644 --- a/packages/karma-runner/src/starters/stryker-karma.conf.ts +++ b/packages/karma-runner/src/starters/stryker-karma.conf.ts @@ -22,7 +22,9 @@ function setUserKarmaConfigFile(config: Config, log: Logger) { config.configFile = configFileName; // override config to ensure karma is as user-like as possible } catch (error) { if (error.code === 'MODULE_NOT_FOUND') { - log.error(`Unable to find karma config at "${globalSettings.karmaConfigFile}" (tried to load from ${configFileName}). Please check your stryker config. You might need to make sure the file is included in the sandbox directory.`); + log.error( + `Unable to find karma config at "${globalSettings.karmaConfigFile}" (tried to load from ${configFileName}). Please check your stryker config. You might need to make sure the file is included in the sandbox directory.` + ); } else { log.error(`Could not read karma configuration from ${globalSettings.karmaConfigFile}.`, error); } @@ -93,18 +95,42 @@ function configureStrykerReporter(config: Config) { } const noopLogger = { - isTraceEnabled() { return false; }, - isDebugEnabled() { return false; }, - isInfoEnabled() { return false; }, - isWarnEnabled() { return false; }, - isErrorEnabled() { return false; }, - isFatalEnabled() { return false; }, - trace() { }, - debug() { }, - info() { }, - warn() { }, - error() { }, - fatal() { } + isTraceEnabled() { + return false; + }, + isDebugEnabled() { + return false; + }, + isInfoEnabled() { + return false; + }, + isWarnEnabled() { + return false; + }, + isErrorEnabled() { + return false; + }, + isFatalEnabled() { + return false; + }, + trace() { + // noop + }, + debug() { + // noop + }, + info() { + // noop + }, + warn() { + // noop + }, + error() { + // noop + }, + fatal() { + // noop + } }; const globalSettings: { @@ -117,16 +143,18 @@ const globalSettings: { } }; -export = Object.assign((config: Config) => { - const log = globalSettings.getLogger(path.basename(__filename)); - setDefaultOptions(config); - setUserKarmaConfigFile(config, log); - setUserKarmaConfig(config); - setBasePath(config); - setLifeCycleOptions(config); - configureTestHooksMiddleware(config); - configureStrykerReporter(config); -}, { +export = Object.assign( + (config: Config) => { + const log = globalSettings.getLogger(path.basename(__filename)); + setDefaultOptions(config); + setUserKarmaConfigFile(config, log); + setUserKarmaConfig(config); + setBasePath(config); + setLifeCycleOptions(config); + configureTestHooksMiddleware(config); + configureStrykerReporter(config); + }, + { /** * Provide global settings for next configuration * This is the only way we can pass through any values between the `KarmaTestRunner` and the stryker-karma.conf file. @@ -137,4 +165,5 @@ export = Object.assign((config: Config) => { globalSettings.karmaConfigFile = globals.karmaConfigFile; globalSettings.getLogger = globals.getLogger || (() => noopLogger); } - }); + } +); diff --git a/packages/karma-runner/test/helpers/assertions.ts b/packages/karma-runner/test/helpers/assertions.ts index 137743e6e5..0543787545 100644 --- a/packages/karma-runner/test/helpers/assertions.ts +++ b/packages/karma-runner/test/helpers/assertions.ts @@ -1,7 +1,7 @@ import { RunResult, TestStatus } from '@stryker-mutator/api/test_runner'; import { expect } from 'chai'; -export function expectTestResults(result: RunResult, expectedTestResults: { name: string, status: TestStatus }[]) { +export function expectTestResults(result: RunResult, expectedTestResults: Array<{ name: string; status: TestStatus }>) { const actualTestResults = result.tests.map(test => ({ name: test.name, status: test.status })); expect(actualTestResults).to.have.length(expectedTestResults.length); expectedTestResults.forEach(expectedTestResult => { diff --git a/packages/karma-runner/test/integration/KarmaTestRunner.it.spec.ts b/packages/karma-runner/test/integration/KarmaTestRunner.it.spec.ts index db8ab80bfa..3c3fe2ba2b 100644 --- a/packages/karma-runner/test/integration/KarmaTestRunner.it.spec.ts +++ b/packages/karma-runner/test/integration/KarmaTestRunner.it.spec.ts @@ -31,7 +31,6 @@ function createSut() { } describe('KarmaTestRunner', () => { - let sut: KarmaTestRunner; const expectToHaveSuccessfulTests = (result: RunResult, n: number) => { @@ -47,14 +46,9 @@ describe('KarmaTestRunner', () => { }; describe('when all tests succeed', () => { - describe('with simple add function to test', () => { - before(() => { - setOptions([ - 'testResources/sampleProject/src/Add.js', - 'testResources/sampleProject/test/AddSpec.js' - ]); + setOptions(['testResources/sampleProject/src/Add.js', 'testResources/sampleProject/test/AddSpec.js']); sut = createSut(); return sut.init(); }); @@ -68,14 +62,16 @@ describe('KarmaTestRunner', () => { }); }); - it('should be able to run twice in quick succession', - () => expect(sut.run({}).then(() => sut.run({}))).to.eventually.have.property('status', RunStatus.Complete)); + it('should be able to run twice in quick succession', () => + expect(sut.run({}).then(() => sut.run({}))).to.eventually.have.property('status', RunStatus.Complete)); it('should be able to filter tests', async () => { - const testHooks = wrapInClosure(new JasmineTestFramework().filter([ - { id: 0, name: 'Add should be able to add two numbers' }, - { id: 3, name: 'Add should be able to recognize a negative number' } - ])); + const testHooks = wrapInClosure( + new JasmineTestFramework().filter([ + { id: 0, name: 'Add should be able to add two numbers' }, + { id: 3, name: 'Add should be able to recognize a negative number' } + ]) + ); const result = await sut.run({ testHooks }); expectTestResults(result, [ { name: 'Add should be able to add two numbers', status: TestStatus.Success }, @@ -110,7 +106,6 @@ describe('KarmaTestRunner', () => { }); describe('when an error occurs while running tests', () => { - before(() => { setOptions([ 'testResources/sampleProject/src/Add.js', @@ -125,16 +120,13 @@ describe('KarmaTestRunner', () => { const runResult = await sut.run({}); expect(RunStatus[runResult.status]).to.be.eq(RunStatus[RunStatus.Error]); expect((runResult.errorMessages as string[]).length).to.equal(1); - expect((runResult.errorMessages as string[])[0]).include('ReferenceError: Can\'t find variable: someGlobalVariableThatIsNotDeclared'); + expect((runResult.errorMessages as string[])[0]).include("ReferenceError: Can't find variable: someGlobalVariableThatIsNotDeclared"); }); }); describe('when no error occurred and no test is performed', () => { before(() => { - setOptions([ - 'testResources/sampleProject/src/Add.js', - 'testResources/sampleProject/test/EmptySpec.js' - ]); + setOptions(['testResources/sampleProject/src/Add.js', 'testResources/sampleProject/test/EmptySpec.js']); sut = createSut(); return sut.init(); }); @@ -152,7 +144,6 @@ describe('KarmaTestRunner', () => { }); describe('when adding an error file with included: false', () => { - before(() => { setOptions([ { pattern: 'testResources/sampleProject/src/Add.js', included: true }, @@ -172,28 +163,24 @@ describe('KarmaTestRunner', () => { }); describe('when coverage data is available', () => { - before(() => { - setOptions([ - 'testResources/sampleProject/src-instrumented/Add.js', - 'testResources/sampleProject/test/AddSpec.js' - ], 'all'); + setOptions(['testResources/sampleProject/src-instrumented/Add.js', 'testResources/sampleProject/test/AddSpec.js'], 'all'); sut = createSut(); return sut.init(); }); - it('should report coverage data', () => expect(sut.run({})).to.eventually.satisfy((runResult: RunResult) => { - expect(runResult.coverage).to.be.ok; - expect(runResult.status).to.be.eq(RunStatus.Complete); - const files = Object.keys(runResult.coverage || {}); - expect(files).to.have.length(1); - const coverageResult = (runResult.coverage as CoverageCollection)[files[0]]; - expect(coverageResult.s).to.be.ok; - return true; - })); + it('should report coverage data', () => + expect(sut.run({})).to.eventually.satisfy((runResult: RunResult) => { + expect(runResult.coverage).to.be.ok; + expect(runResult.status).to.be.eq(RunStatus.Complete); + const files = Object.keys(runResult.coverage || {}); + expect(files).to.have.length(1); + const coverageResult = (runResult.coverage as CoverageCollection)[files[0]]; + expect(coverageResult.s).to.be.ok; + return true; + })); }); describe('when specified port is not available', () => { - let dummyServer: DummyServer; before(async () => { @@ -223,7 +210,7 @@ class DummyServer { this.httpServer = http.createServer(); } - get port() { + public get port() { const address = this.httpServer.address(); if (typeof address === 'string') { throw new Error(`Address "${address}" was unexpected: https://nodejs.org/dist/latest-v11.x/docs/api/net.html#net_server_address`); diff --git a/packages/karma-runner/test/integration/sample.it.spec.ts b/packages/karma-runner/test/integration/sample.it.spec.ts index cba494ab3c..552f055938 100644 --- a/packages/karma-runner/test/integration/sample.it.spec.ts +++ b/packages/karma-runner/test/integration/sample.it.spec.ts @@ -5,7 +5,6 @@ import KarmaTestRunner from '../../src/KarmaTestRunner'; import { expectTestResults } from '../helpers/assertions'; describe('Sample project', () => { - it('should be able to run karma', async () => { testInjector.options.karma = { configFile: path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'karma.conf.js') }; const runner = testInjector.injector.injectClass(KarmaTestRunner); @@ -43,7 +42,8 @@ describe('Sample project', () => { { name: 'Circle should have a circumference of 2PI when the radius is 1', status: TestStatus.Success - }, { + }, + { name: 'Add this test should fail', status: TestStatus.Failed } diff --git a/packages/karma-runner/test/unit/KarmaTestRunner.spec.ts b/packages/karma-runner/test/unit/KarmaTestRunner.spec.ts index 1981bbdec0..4475293152 100644 --- a/packages/karma-runner/test/unit/KarmaTestRunner.spec.ts +++ b/packages/karma-runner/test/unit/KarmaTestRunner.spec.ts @@ -1,10 +1,6 @@ import { LoggerFactoryMethod } from '@stryker-mutator/api/logging'; import { commonTokens } from '@stryker-mutator/api/plugin'; -import { - RunStatus, - TestResult, - TestStatus -} from '@stryker-mutator/api/test_runner'; +import { RunStatus, TestResult, TestStatus } from '@stryker-mutator/api/test_runner'; import { testInjector } from '@stryker-mutator/test-helpers'; import { expect } from 'chai'; import { EventEmitter } from 'events'; diff --git a/packages/karma-runner/test/unit/StrykerReporter.spec.ts b/packages/karma-runner/test/unit/StrykerReporter.spec.ts index 107b9c5902..777a484d04 100644 --- a/packages/karma-runner/test/unit/StrykerReporter.spec.ts +++ b/packages/karma-runner/test/unit/StrykerReporter.spec.ts @@ -4,7 +4,6 @@ import { TestResults } from 'karma'; import StrykerReporter, { KarmaSpec } from '../../src/StrykerReporter'; describe('StrykerReporter', () => { - let sut: StrykerReporter; beforeEach(() => { @@ -27,12 +26,15 @@ describe('StrykerReporter', () => { events = listenTo('test_result'); }); it('should emit "test_result"', () => { - sut.onSpecComplete(undefined, karmaSpec({ - description: '3', - success: true, - suite: ['1', '2'], - time: 64 - })); + sut.onSpecComplete( + undefined, + karmaSpec({ + description: '3', + success: true, + suite: ['1', '2'], + time: 64 + }) + ); const expectedTestResult: TestResult = { failureMessages: [], name: '1 2 3', @@ -76,16 +78,20 @@ describe('StrykerReporter', () => { }); it('should convert error to RunState.Error', () => { - sut.onRunComplete(testResults({ - error: true - })); + sut.onRunComplete( + testResults({ + error: true + }) + ); expect(events()[0]).eq(RunStatus.Error); }); it('should convert disconnected to RunState.Timeout', () => { - sut.onRunComplete(testResults({ - disconnected: true - })); + sut.onRunComplete( + testResults({ + disconnected: true + }) + ); expect(events()[0]).eq(RunStatus.Timeout); }); }); @@ -155,24 +161,30 @@ describe('StrykerReporter', () => { } function karmaSpec(overrides?: Partial): KarmaSpec { - return Object.assign({ - description: 'baz', - id: '1', - log: [], - skipped: false, - success: true, - suite: ['foo', 'bar'], - time: 42 - }, overrides); + return Object.assign( + { + description: 'baz', + id: '1', + log: [], + skipped: false, + success: true, + suite: ['foo', 'bar'], + time: 42 + }, + overrides + ); } function testResults(overrides?: Partial): TestResults { - return Object.assign({ - disconnected: false, - error: false, - exitCode: 0, - failed: 0, - success: 0 - }, overrides); + return Object.assign( + { + disconnected: false, + error: false, + exitCode: 0, + failed: 0, + success: 0 + }, + overrides + ); } }); diff --git a/packages/karma-runner/test/unit/starters/angularStarter.spec.ts b/packages/karma-runner/test/unit/starters/angularStarter.spec.ts index 2180ec000c..efc47c8a3e 100644 --- a/packages/karma-runner/test/unit/starters/angularStarter.spec.ts +++ b/packages/karma-runner/test/unit/starters/angularStarter.spec.ts @@ -19,9 +19,7 @@ describe('angularStarter', () => { it('should throw an error if angular cli version < 6.1.0', async () => { setAngularVersion('6.0.8'); - await expect(sut.start(getLogger)).rejectedWith( - 'Your @angular/cli version (6.0.8) is not supported. Please install 6.1.0 or higher' - ); + await expect(sut.start(getLogger)).rejectedWith('Your @angular/cli version (6.0.8) is not supported. Please install 6.1.0 or higher'); }); it('should support version 6.1.0 and up inc release candidates', async () => { @@ -44,13 +42,7 @@ describe('angularStarter', () => { setAngularVersion(); await sut.start(getLogger); expect(cliStub).calledWith({ - cliArgs: [ - 'test', - '--progress=false', - `--karma-config=${require.resolve( - '../../../src/starters/stryker-karma.conf' - )}` - ], + cliArgs: ['test', '--progress=false', `--karma-config=${require.resolve('../../../src/starters/stryker-karma.conf')}`], inputStream: process.stdin, outputStream: process.stdout }); @@ -94,12 +86,10 @@ describe('angularStarter', () => { '--project': '@ns/myproj' } }) - ).rejectedWith('Don\'t prefix arguments with dashes (\'-\'). Stryker will do this automatically. Problematic arguments are --project'); + ).rejectedWith("Don't prefix arguments with dashes ('-'). Stryker will do this automatically. Problematic arguments are --project"); }); function setAngularVersion(version = '100') { - requireModuleStub - .withArgs('@angular/cli/package') - .returns({ version }); + requireModuleStub.withArgs('@angular/cli/package').returns({ version }); } }); diff --git a/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts b/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts index 7f0713556c..21a781d19f 100644 --- a/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts +++ b/packages/karma-runner/test/unit/starters/stryker-karma.conf.spec.ts @@ -9,7 +9,6 @@ import TestHooksMiddleware, { TEST_HOOKS_FILE_NAME } from '../../../src/TestHook import * as utils from '../../../src/utils'; describe('stryker-karma.conf.js', () => { - let getLogger: sinon.SinonStub; let requireModuleStub: sinon.SinonStub; let config: Config; @@ -44,9 +43,11 @@ describe('stryker-karma.conf.js', () => { it('should set user configuration from a custom karma.conf.js file', () => { // Arrange - requireModuleStub.returns((conf: Config) => conf.set({ - basePath: 'foobar' - })); + requireModuleStub.returns((conf: Config) => + conf.set({ + basePath: 'foobar' + }) + ); sut.setGlobals({ karmaConfigFile: 'foobar.conf.js' }); // Act @@ -69,7 +70,9 @@ describe('stryker-karma.conf.js', () => { sut(config); // Assert - expect(testInjector.logger.error).calledWithMatch(`Unable to find karma config at "foobar.conf.js" (tried to load from ${path.resolve(expectedKarmaConfigFile)})`); + expect(testInjector.logger.error).calledWithMatch( + `Unable to find karma config at "foobar.conf.js" (tried to load from ${path.resolve(expectedKarmaConfigFile)})` + ); expect(requireModuleStub).calledWith(path.resolve(expectedKarmaConfigFile)); }); @@ -79,7 +82,7 @@ describe('stryker-karma.conf.js', () => { expect(config).deep.include({ basePath: 'foobar' }); }); - it('should force some options that relate to karma\'s life cycle', () => { + it("should force some options that relate to karma's life cycle", () => { config.set({ browserNoActivityTimeout: 1, autoWatch: true, singleRun: true, detached: true }); sut(config); expect(config).deep.include({ @@ -114,7 +117,9 @@ describe('stryker-karma.conf.js', () => { it('should set basePath to location of karma.conf.js', () => { sut.setGlobals({ karmaConfigFile: '../foobar.conf.js' }); - requireModuleStub.returns(() => { /* noop */ }); + requireModuleStub.returns(() => { + /* noop */ + }); sut(config); expect(config).deep.include({ basePath: path.resolve('../'), diff --git a/packages/mocha-framework/.eslintrc b/packages/mocha-framework/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/mocha-framework/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/mocha-framework/src/MochaTestFramework.ts b/packages/mocha-framework/src/MochaTestFramework.ts index 7ba4fa8d31..e83840d9e8 100644 --- a/packages/mocha-framework/src/MochaTestFramework.ts +++ b/packages/mocha-framework/src/MochaTestFramework.ts @@ -1,7 +1,6 @@ import { TestFramework, TestSelection } from '@stryker-mutator/api/test_framework'; export default class MochaTestFramework implements TestFramework { - public beforeEach(codeFragment: string): string { return `beforeEach(function() { ${codeFragment} diff --git a/packages/mocha-framework/src/index.ts b/packages/mocha-framework/src/index.ts index 881fd7e83e..7971365b77 100644 --- a/packages/mocha-framework/src/index.ts +++ b/packages/mocha-framework/src/index.ts @@ -1,7 +1,4 @@ - import { declareClassPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import MochaTestFramework from './MochaTestFramework'; -export const strykerPlugins = [ - declareClassPlugin(PluginKind.TestFramework, 'mocha', MochaTestFramework) -]; +export const strykerPlugins = [declareClassPlugin(PluginKind.TestFramework, 'mocha', MochaTestFramework)]; diff --git a/packages/mocha-framework/test/integration/nestedSuite.it.spec.ts b/packages/mocha-framework/test/integration/nestedSuite.it.spec.ts index f1d4fac442..b48fdde64e 100644 --- a/packages/mocha-framework/test/integration/nestedSuite.it.spec.ts +++ b/packages/mocha-framework/test/integration/nestedSuite.it.spec.ts @@ -20,7 +20,6 @@ interface MochaTest { // See https://github.com/stryker-mutator/stryker/issues/249 describe('Selecting tests with nested suites', () => { - let sut: MochaTestFramework; const nestedSuiteFile = path.resolve(__dirname, '..', '..', 'testResources', 'nested-suite.js'); const selectTestFile = path.join(__dirname, '..', '..', 'testResources', '__filterSpecs.js'); diff --git a/packages/mocha-framework/test/unit/MochaTestFramework.spec.ts b/packages/mocha-framework/test/unit/MochaTestFramework.spec.ts index df67fa46f1..cc93ba3ea0 100644 --- a/packages/mocha-framework/test/unit/MochaTestFramework.spec.ts +++ b/packages/mocha-framework/test/unit/MochaTestFramework.spec.ts @@ -3,20 +3,24 @@ import MochaTestFramework from '../../src/MochaTestFramework'; describe('MochaTestFramework', () => { let sut: MochaTestFramework; - beforeEach(() => sut = new MochaTestFramework()); + beforeEach(() => (sut = new MochaTestFramework())); describe('beforeEach()', () => { it('should result in a beforeEach mocha hook', () => - expect(sut.beforeEach('fragment')).to.contain('fragment').and.to.contain('beforeEach')); + expect(sut.beforeEach('fragment')) + .to.contain('fragment') + .and.to.contain('beforeEach')); }); describe('afterEach()', () => { it('should result in an afterEach mocha hook', () => - expect(sut.afterEach('fragment')).to.contain('fragment').and.to.contain('afterEach')); + expect(sut.afterEach('fragment')) + .to.contain('fragment') + .and.to.contain('afterEach')); }); describe('filter()', () => { - it('should result in a filtering of mocha it\'s', () => { + it("should result in a filtering of mocha it's", () => { expect(sut.filter([{ id: 5, name: 'test five' }, { id: 8, name: 'test eight' }])) .to.contain('var realAddTest = Mocha.Suite.prototype.addTest;') .and.to.contain('selectedTestNames = ["test five","test eight"];') diff --git a/packages/mocha-runner/.eslintrc b/packages/mocha-runner/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/mocha-runner/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/mocha-runner/src/LibWrapper.ts b/packages/mocha-runner/src/LibWrapper.ts index 5796b65b30..14111b4628 100644 --- a/packages/mocha-runner/src/LibWrapper.ts +++ b/packages/mocha-runner/src/LibWrapper.ts @@ -7,10 +7,10 @@ let handleFiles: undefined | ((options: MochaOptions) => string[]); try { /* - * If read, object containing parsed arguments - * @since 6.0.0' - * @see https://mochajs.org/api/module-lib_cli_options.html#.loadOptions - */ + * If read, object containing parsed arguments + * @since 6.0.0' + * @see https://mochajs.org/api/module-lib_cli_options.html#.loadOptions + */ loadOptions = require('mocha/lib/cli/options').loadOptions; } catch { // Mocha < 6 doesn't support `loadOptions` diff --git a/packages/mocha-runner/src/MochaConfigEditor.ts b/packages/mocha-runner/src/MochaConfigEditor.ts index 8628528a9a..f5c07dbb44 100644 --- a/packages/mocha-runner/src/MochaConfigEditor.ts +++ b/packages/mocha-runner/src/MochaConfigEditor.ts @@ -4,7 +4,6 @@ import MochaOptionsLoader from './MochaOptionsLoader'; import { mochaOptionsKey } from './utils'; export default class MochaConfigEditor implements ConfigEditor { - public static inject = tokens('loader'); constructor(private readonly loader: MochaOptionsLoader) {} diff --git a/packages/mocha-runner/src/MochaOptionsLoader.ts b/packages/mocha-runner/src/MochaOptionsLoader.ts index f567497161..ad5241588f 100644 --- a/packages/mocha-runner/src/MochaOptionsLoader.ts +++ b/packages/mocha-runner/src/MochaOptionsLoader.ts @@ -22,18 +22,17 @@ export const DEFAULT_MOCHA_OPTIONS = Object.freeze({ }); export default class MochaOptionsLoader { - public static inject = tokens(commonTokens.logger); - constructor(private readonly log: Logger) { } + constructor(private readonly log: Logger) {} public load(strykerOptions: StrykerOptions): MochaOptions { const mochaOptions = { ...strykerOptions[mochaOptionsKey] } as MochaOptions; - return { ...DEFAULT_MOCHA_OPTIONS, ... this.loadMochaOptions(mochaOptions), ...mochaOptions }; + return { ...DEFAULT_MOCHA_OPTIONS, ...this.loadMochaOptions(mochaOptions), ...mochaOptions }; } private loadMochaOptions(overrides: MochaOptions) { if (LibWrapper.loadOptions) { - this.log.debug('Mocha >= 6 detected. Using mocha\'s `%s` to load mocha options', LibWrapper.loadOptions.name); + this.log.debug("Mocha >= 6 detected. Using mocha's `%s` to load mocha options", LibWrapper.loadOptions.name); return this.loadMocha6Options(overrides); } else { this.log.warn('DEPRECATED: Mocha < 6 detected. Please upgrade to at least Mocha version 6.'); diff --git a/packages/mocha-runner/src/MochaTestRunner.ts b/packages/mocha-runner/src/MochaTestRunner.ts index 432a6cb05b..6bd4283995 100644 --- a/packages/mocha-runner/src/MochaTestRunner.ts +++ b/packages/mocha-runner/src/MochaTestRunner.ts @@ -11,12 +11,11 @@ import { evalGlobal, mochaOptionsKey } from './utils'; const DEFAULT_TEST_PATTERN = 'test/**/*.js'; export default class MochaTestRunner implements TestRunner { - private testFileNames: string[]; private readonly mochaOptions: MochaOptions; public static inject = tokens(commonTokens.logger, commonTokens.sandboxFileNames, commonTokens.options); - constructor(private readonly log: Logger, private readonly allFileNames: ReadonlyArray, options: StrykerOptions) { + constructor(private readonly log: Logger, private readonly allFileNames: readonly string[], options: StrykerOptions) { this.mochaOptions = options[mochaOptionsKey]; this.additionalRequires(); StrykerMochaReporter.log = log; @@ -24,7 +23,7 @@ export default class MochaTestRunner implements TestRunner { public init(): void { if (LibWrapper.handleFiles) { - this.log.debug('Mocha >= 6 detected. Using mocha\'s `handleFiles` to load files'); + this.log.debug("Mocha >= 6 detected. Using mocha's `handleFiles` to load files"); this.testFileNames = this.mocha6DiscoverFiles(LibWrapper.handleFiles); } else { this.log.debug('Mocha < 6 detected. Using custom logic to discover files'); @@ -36,11 +35,10 @@ export default class MochaTestRunner implements TestRunner { const originalProcessExit = process.exit; try { // process.exit unfortunate side effect: https://github.com/mochajs/mocha/blob/07ea8763c663bdd3fe1f8446cdb62dae233f4916/lib/cli/run-helpers.js#L174 - (process as any).exit = () => { }; + (process as any).exit = () => {}; const files = handleFiles(this.mochaOptions); return files; - } - finally { + } finally { process.exit = originalProcessExit; } } @@ -53,7 +51,13 @@ export default class MochaTestRunner implements TestRunner { this.log.debug(`Using files: ${JSON.stringify(fileNames, null, 2)}`); } else { this.log.debug(`Tried ${JSON.stringify(globPatternsAbsolute, null, 2)} on files: ${JSON.stringify(this.allFileNames, null, 2)}.`); - throw new Error(`[${MochaTestRunner.name}] No files discovered (tried pattern(s) ${JSON.stringify(globPatterns, null, 2)}). Please specify the files (glob patterns) containing your tests in ${mochaOptionsKey}.files in your stryker.conf.js file.`); + throw new Error( + `[${MochaTestRunner.name}] No files discovered (tried pattern(s) ${JSON.stringify( + globPatterns, + null, + 2 + )}). Please specify the files (glob patterns) containing your tests in ${mochaOptionsKey}.files in your stryker.conf.js file.` + ); } return fileNames; } @@ -64,7 +68,8 @@ export default class MochaTestRunner implements TestRunner { globPatterns.push(...this.mochaOptions.spec); } - if (typeof this.mochaOptions.files === 'string') { // `files` if for backward compat + if (typeof this.mochaOptions.files === 'string') { + // `files` if for backward compat globPatterns.push(this.mochaOptions.files); } else if (this.mochaOptions.files) { globPatterns.push(...this.mochaOptions.files); @@ -151,8 +156,7 @@ export default class MochaTestRunner implements TestRunner { private additionalRequires() { if (this.mochaOptions.require) { - const modulesToRequire = this.mochaOptions.require - .map(moduleName => moduleName.startsWith('.') ? path.resolve(moduleName) : moduleName); + const modulesToRequire = this.mochaOptions.require.map(moduleName => (moduleName.startsWith('.') ? path.resolve(moduleName) : moduleName)); modulesToRequire.forEach(LibWrapper.require); } } diff --git a/packages/mocha-runner/src/StrykerMochaReporter.ts b/packages/mocha-runner/src/StrykerMochaReporter.ts index e5c0d4e679..cd51256782 100644 --- a/packages/mocha-runner/src/StrykerMochaReporter.ts +++ b/packages/mocha-runner/src/StrykerMochaReporter.ts @@ -3,7 +3,6 @@ import { RunResult, RunStatus, TestStatus } from '@stryker-mutator/api/test_runn import Timer from './Timer'; export class StrykerMochaReporter { - /* * The stryker logger instance injected into this plugin * Needs to be set from 'the outside' because mocha doesn't really have a nice way of providing diff --git a/packages/mocha-runner/src/index.ts b/packages/mocha-runner/src/index.ts index cc6853c350..173c5d841f 100644 --- a/packages/mocha-runner/src/index.ts +++ b/packages/mocha-runner/src/index.ts @@ -11,7 +11,5 @@ export const strykerPlugins = [ mochaConfigEditorFactory.inject = tokens(commonTokens.injector); function mochaConfigEditorFactory(injector: Injector): MochaConfigEditor { - return injector - .provideClass('loader', MochaOptionsLoader) - .injectClass(MochaConfigEditor); + return injector.provideClass('loader', MochaOptionsLoader).injectClass(MochaConfigEditor); } diff --git a/packages/mocha-runner/src/utils.ts b/packages/mocha-runner/src/utils.ts index 6eb5b0a743..a0f1d5c99b 100644 --- a/packages/mocha-runner/src/utils.ts +++ b/packages/mocha-runner/src/utils.ts @@ -25,18 +25,7 @@ export function serializeArguments(mochaOptions: MochaOptions) { export const mochaOptionsKey = 'mochaOptions'; -const SUPPORTED_MOCHA_OPTIONS = Object.freeze([ - 'extension', - 'require', - 'timeout', - 'async-only', - 'ui', - 'grep', - 'exclude', - 'ignore', - 'spec', - 'file' -]); +const SUPPORTED_MOCHA_OPTIONS = Object.freeze(['extension', 'require', 'timeout', 'async-only', 'ui', 'grep', 'exclude', 'ignore', 'spec', 'file']); /** * Filter out those config values that are actually useful to run mocha with Stryker @@ -46,7 +35,7 @@ export function filterConfig(rawConfig: { [key: string]: any }): MochaOptions { const options: MochaOptions = {}; Object.keys(rawConfig) .filter(rawOption => SUPPORTED_MOCHA_OPTIONS.some(supportedOption => rawOption === supportedOption)) - .forEach(option => (options as any)[option] = rawConfig[option]); + .forEach(option => ((options as any)[option] = rawConfig[option])); // Config file can also contain positional arguments. They are provided under the `_` key // For example: diff --git a/packages/mocha-runner/test/integration/MochaFileResolving.spec.ts b/packages/mocha-runner/test/integration/MochaFileResolving.spec.ts index 83d2c4bb85..76d64165d3 100644 --- a/packages/mocha-runner/test/integration/MochaFileResolving.spec.ts +++ b/packages/mocha-runner/test/integration/MochaFileResolving.spec.ts @@ -7,7 +7,6 @@ import MochaTestRunner from '../../src/MochaTestRunner'; import { mochaOptionsKey } from '../../src/utils'; describe('Mocha 6 file resolving integration', () => { - const cwd = process.cwd(); afterEach(() => { @@ -29,14 +28,11 @@ describe('Mocha 6 file resolving integration', () => { }); function createConfigLoader() { - return testInjector.injector - .injectClass(MochaOptionsLoader); + return testInjector.injector.injectClass(MochaOptionsLoader); } function createTestRunner() { - return testInjector.injector - .provideValue(commonTokens.sandboxFileNames, []) - .injectClass(MochaTestRunner); + return testInjector.injector.provideValue(commonTokens.sandboxFileNames, []).injectClass(MochaTestRunner); } function resolveTestDir(fileName = '.') { diff --git a/packages/mocha-runner/test/integration/MochaFramework.it.spec.ts b/packages/mocha-runner/test/integration/MochaFramework.it.spec.ts index 4a1620d092..c089c95328 100644 --- a/packages/mocha-runner/test/integration/MochaFramework.it.spec.ts +++ b/packages/mocha-runner/test/integration/MochaFramework.it.spec.ts @@ -22,7 +22,6 @@ export function wrapInClosure(codeFragment: string) { } describe('Integration with stryker-mocha-framework', () => { - let testFramework: MochaTestFramework; beforeEach(() => { @@ -32,19 +31,21 @@ describe('Integration with stryker-mocha-framework', () => { it('should be able to select only test 0 and 3 to run', async () => { const testHooks = wrapInClosure(testFramework.filter([test0, test3])); const actualProcessOutput = await actRun(testHooks); - expect(actualProcessOutput.result.tests.map(test => ({ name: test.name, status: test.status }))).deep.eq([{ - name: 'MyMath should be able to add two numbers', - status: TestStatus.Success - }, { - name: 'MyMath should be able to recognize a negative number', - status: TestStatus.Success - }]); + expect(actualProcessOutput.result.tests.map(test => ({ name: test.name, status: test.status }))).deep.eq([ + { + name: 'MyMath should be able to add two numbers', + status: TestStatus.Success + }, + { + name: 'MyMath should be able to recognize a negative number', + status: TestStatus.Success + } + ]); }); it('should be able to run beforeEach and afterEach', async () => { const testHooks = wrapInClosure( - testFramework.beforeEach('console.log("beforeEach from hook");') + - testFramework.afterEach('console.log("afterEach from hook");') + testFramework.beforeEach('console.log("beforeEach from hook");') + testFramework.afterEach('console.log("afterEach from hook");') ); const actualProcessOutput = await actRun(testHooks); expect(actualProcessOutput.result.status).eq(RunStatus.Complete); @@ -52,14 +53,14 @@ describe('Integration with stryker-mocha-framework', () => { expect(actualProcessOutput.stdout).includes('afterEach from hook'); }); - function actRun(testHooks: string | undefined): Promise<{ result: RunResult, stdout: string }> { - return new Promise<{ result: RunResult, stdout: string }>((resolve, reject) => { + function actRun(testHooks: string | undefined): Promise<{ result: RunResult; stdout: string }> { + return new Promise<{ result: RunResult; stdout: string }>((resolve, reject) => { const sutProxy = fork(require.resolve('./MochaTestFrameworkIntegrationTestWorker'), [AUTO_START_ARGUMENT], { execArgv: [], silent: true }); let stdout: string = ''; - sutProxy.stdout.on('data', chunk => stdout += chunk.toString()); + sutProxy.stdout.on('data', chunk => (stdout += chunk.toString())); const message: RunMessage = { kind: 'run', testHooks }; sutProxy.send(message, (error: Error) => { if (error) { diff --git a/packages/mocha-runner/test/integration/MochaOptionsLoader.it.spec.ts b/packages/mocha-runner/test/integration/MochaOptionsLoader.it.spec.ts index 90b27ebf4d..072555572f 100644 --- a/packages/mocha-runner/test/integration/MochaOptionsLoader.it.spec.ts +++ b/packages/mocha-runner/test/integration/MochaOptionsLoader.it.spec.ts @@ -60,26 +60,12 @@ describe(`${MochaOptionsLoader.name} integration`, () => { ...DEFAULT_MOCHA_OPTIONS, ['async-only']: false, config: configFile, - exclude: [ - '/path/to/some/excluded/file' - ], - extension: [ - 'yml', - 'js' - ], - file: [ - '/path/to/some/file', - '/path/to/some/other/file' - ], - ignore: [ - '/path/to/some/excluded/file' - ], - require: [ - '@babel/register' - ], - spec: [ - 'test/**/*.spec.js' - ], + exclude: ['/path/to/some/excluded/file'], + extension: ['yml', 'js'], + file: ['/path/to/some/file', '/path/to/some/other/file'], + ignore: ['/path/to/some/excluded/file'], + require: ['@babel/register'], + spec: ['test/**/*.spec.js'], timeout: false, ui: 'bdd' }); @@ -119,10 +105,7 @@ describe(`${MochaOptionsLoader.name} integration`, () => { expect(actualConfig).deep.eq({ ...DEFAULT_MOCHA_OPTIONS, ['async-only']: true, - extension: [ - 'js', - 'json' - ], + extension: ['js', 'json'], timeout: 2000, ui: 'bdd' }); diff --git a/packages/mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts b/packages/mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts index c6f0eeccdb..45e9c7320c 100644 --- a/packages/mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts +++ b/packages/mocha-runner/test/integration/MochaTestFrameworkIntegrationTestWorker.ts @@ -20,9 +20,7 @@ export default class MochaTestFrameworkIntegrationTestWorker { testInjector.options.mochaOptions = { file: [], ignore: [], - spec: [ - path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'MyMathSpec.js') - ], + spec: [path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'MyMathSpec.js')] }; this.sut = testInjector.injector .provideValue(commonTokens.sandboxFileNames, [ @@ -41,7 +39,8 @@ export default class MochaTestFrameworkIntegrationTestWorker { public listenForParentProcess() { process.on('message', (message: ChildMessage) => { - this.sut.run({ testHooks: message.testHooks }) + this.sut + .run({ testHooks: message.testHooks }) .then(result => this.send(result)) .catch(error => this.send(error)); }); @@ -59,6 +58,6 @@ export default class MochaTestFrameworkIntegrationTestWorker { } } -if (process.argv.indexOf(AUTO_START_ARGUMENT) >= 0) { +if (process.argv.includes(AUTO_START_ARGUMENT)) { new MochaTestFrameworkIntegrationTestWorker(); } diff --git a/packages/mocha-runner/test/integration/QUnitSample.it.spec.ts b/packages/mocha-runner/test/integration/QUnitSample.it.spec.ts index f9b43bcdd0..afd6c46d8f 100644 --- a/packages/mocha-runner/test/integration/QUnitSample.it.spec.ts +++ b/packages/mocha-runner/test/integration/QUnitSample.it.spec.ts @@ -14,9 +14,7 @@ describe('QUnit sample', () => { }); function createSut() { - return testInjector.injector - .provideValue(commonTokens.sandboxFileNames, files) - .injectClass(MochaTestRunner); + return testInjector.injector.provideValue(commonTokens.sandboxFileNames, files).injectClass(MochaTestRunner); } it('should work when configured with "qunit" ui', async () => { @@ -41,14 +39,9 @@ describe('QUnit sample', () => { }); it('should not run tests when not configured with "qunit" ui', async () => { - files = [ - resolve('./testResources/qunit-sample/MyMathSpec.js'), - resolve('./testResources/qunit-sample/MyMath.js') - ]; + files = [resolve('./testResources/qunit-sample/MyMathSpec.js'), resolve('./testResources/qunit-sample/MyMath.js')]; testInjector.options.mochaOptions = createMochaOptions({ - files: [ - resolve('./testResources/qunit-sample/MyMathSpec.js') - ] + files: [resolve('./testResources/qunit-sample/MyMathSpec.js')] }); const sut = createSut(); await sut.init(); diff --git a/packages/mocha-runner/test/integration/SampleProject.it.spec.ts b/packages/mocha-runner/test/integration/SampleProject.it.spec.ts index f4af739ebe..e23917d645 100644 --- a/packages/mocha-runner/test/integration/SampleProject.it.spec.ts +++ b/packages/mocha-runner/test/integration/SampleProject.it.spec.ts @@ -9,36 +9,26 @@ import { createMochaOptions } from '../helpers/factories'; chai.use(chaiAsPromised); const expect = chai.expect; -const countTests = (runResult: RunResult, predicate: (result: TestResult) => boolean) => - runResult.tests.filter(predicate).length; +const countTests = (runResult: RunResult, predicate: (result: TestResult) => boolean) => runResult.tests.filter(predicate).length; -const countSucceeded = (runResult: RunResult) => - countTests(runResult, t => t.status === TestStatus.Success); -const countFailed = (runResult: RunResult) => - countTests(runResult, t => t.status === TestStatus.Failed); +const countSucceeded = (runResult: RunResult) => countTests(runResult, t => t.status === TestStatus.Success); +const countFailed = (runResult: RunResult) => countTests(runResult, t => t.status === TestStatus.Failed); function resolve(fileName: string) { return path.resolve(__dirname, '..', '..', fileName); } describe('Running a sample project', () => { - let sut: MochaTestRunner; let spec: string[]; function createSut() { - return testInjector.injector - .provideValue(commonTokens.sandboxFileNames, spec) - .injectClass(MochaTestRunner); + return testInjector.injector.provideValue(commonTokens.sandboxFileNames, spec).injectClass(MochaTestRunner); } describe('when tests pass', () => { - beforeEach(() => { - spec = [ - resolve('./testResources/sampleProject/MyMath.js'), - resolve('./testResources/sampleProject/MyMathSpec.js') - ]; + spec = [resolve('./testResources/sampleProject/MyMath.js'), resolve('./testResources/sampleProject/MyMathSpec.js')]; testInjector.options.mochaOptions = createMochaOptions({ spec }); sut = createSut(); return sut.init(); @@ -48,7 +38,11 @@ describe('Running a sample project', () => { const runResult = await sut.run({}); expect(countSucceeded(runResult)).to.be.eq(5, 'Succeeded tests did not match'); expect(countFailed(runResult)).to.be.eq(0, 'Failed tests did not match'); - runResult.tests.forEach(t => expect(t.timeSpentMs).to.be.greaterThan(-1).and.to.be.lessThan(1000)); + runResult.tests.forEach(t => + expect(t.timeSpentMs) + .to.be.greaterThan(-1) + .and.to.be.lessThan(1000) + ); expect(runResult.status).to.be.eq(RunStatus.Complete, 'Test result did not match'); expect(runResult.coverage).to.not.be.ok; }); @@ -62,10 +56,7 @@ describe('Running a sample project', () => { describe('with an error in an un-included input file', () => { beforeEach(() => { - spec = [ - resolve('testResources/sampleProject/MyMath.js'), - resolve('testResources/sampleProject/MyMathSpec.js'), - ]; + spec = [resolve('testResources/sampleProject/MyMath.js'), resolve('testResources/sampleProject/MyMathSpec.js')]; testInjector.options.mochaOptions = createMochaOptions({ files: spec }); @@ -80,12 +71,8 @@ describe('Running a sample project', () => { }); describe('with multiple failed tests', () => { - before(() => { - spec = [ - resolve('testResources/sampleProject/MyMath.js'), - resolve('testResources/sampleProject/MyMathFailedSpec.js') - ]; + spec = [resolve('testResources/sampleProject/MyMath.js'), resolve('testResources/sampleProject/MyMathFailedSpec.js')]; testInjector.options.mochaOptions = createMochaOptions({ spec }); sut = createSut(); return sut.init(); @@ -98,7 +85,6 @@ describe('Running a sample project', () => { }); describe('when no tests are executed', () => { - beforeEach(() => { spec = [resolve('./testResources/sampleProject/MyMath.js')]; testInjector.options.mochaOptions = createMochaOptions({ spec }); @@ -110,7 +96,11 @@ describe('Running a sample project', () => { const runResult = await sut.run({}); expect(countSucceeded(runResult)).to.be.eq(0, 'Succeeded tests did not match'); expect(countFailed(runResult)).to.be.eq(0, 'Failed tests did not match'); - runResult.tests.forEach(t => expect(t.timeSpentMs).to.be.greaterThan(-1).and.to.be.lessThan(1000)); + runResult.tests.forEach(t => + expect(t.timeSpentMs) + .to.be.greaterThan(-1) + .and.to.be.lessThan(1000) + ); expect(runResult.status).to.be.eq(RunStatus.Complete, 'Test result did not match'); expect(runResult.coverage).to.not.be.ok; }); diff --git a/packages/mocha-runner/test/unit/MochaOptionsLoader.spec.ts b/packages/mocha-runner/test/unit/MochaOptionsLoader.spec.ts index ddb93bc8df..a633350116 100644 --- a/packages/mocha-runner/test/unit/MochaOptionsLoader.spec.ts +++ b/packages/mocha-runner/test/unit/MochaOptionsLoader.spec.ts @@ -10,7 +10,6 @@ import MochaOptionsLoader from '../../src/MochaOptionsLoader'; import { mochaOptionsKey } from '../../src/utils'; describe(MochaOptionsLoader.name, () => { - let readFileStub: sinon.SinonStub; let existsFileStub: sinon.SinonStub; let config: Config; @@ -21,7 +20,6 @@ describe(MochaOptionsLoader.name, () => { }); describe('with mocha >= 6', () => { - let rawOptions: { [option: string]: any }; beforeEach(() => { @@ -32,7 +30,8 @@ describe(MochaOptionsLoader.name, () => { it('should log about mocha >= 6', () => { sut.load(testInjector.options); expect(testInjector.logger.debug).calledWith( - 'Mocha >= 6 detected. Using mocha\'s `%s` to load mocha options', LibWrapper.loadOptions && LibWrapper.loadOptions.name + "Mocha >= 6 detected. Using mocha's `%s` to load mocha options", + LibWrapper.loadOptions && LibWrapper.loadOptions.name ); }); @@ -77,7 +76,7 @@ describe(MochaOptionsLoader.name, () => { spec: ['test/**/*.js'], timeout: 'baz', ['async-only']: 'qux', - ui: 'quux', + ui: 'quux' }); }); @@ -89,19 +88,16 @@ describe(MochaOptionsLoader.name, () => { rawOptions.baz = 'qux'; sut.load(testInjector.options); const fnName = LibWrapper.loadOptions && LibWrapper.loadOptions.name; - expect(testInjector.logger.trace).calledWith( - `Mocha: ${fnName}(['--foo','bar']) => {"baz":"qux"}` - ); + expect(testInjector.logger.trace).calledWith(`Mocha: ${fnName}(['--foo','bar']) => {"baz":"qux"}`); }); - it('should respect mocha\'s defaults', () => { + it("should respect mocha's defaults", () => { const options = sut.load(testInjector.options); expect(options).deep.eq(createMochaOptions()); }); }); describe('with mocha < 6', () => { - beforeEach(() => { sinon.stub(LibWrapper, 'loadOptions').value(undefined); readFileStub = sinon.stub(fs, 'readFileSync'); @@ -131,7 +127,7 @@ describe(MochaOptionsLoader.name, () => { expect(fs.readFileSync).calledWith(path.resolve('some/mocha.opts/file')); }); - it('should log an error if specified mocha.opts file doesn\'t exist', () => { + it("should log an error if specified mocha.opts file doesn't exist", () => { readFileStub.returns(''); existsFileStub.returns(false); config.mochaOptions = { @@ -139,7 +135,9 @@ describe(MochaOptionsLoader.name, () => { }; sut.load(config); - expect(testInjector.logger.error).calledWith(`Could not load opts from "${path.resolve('some/mocha.opts/file')}". Please make sure opts file exists.`); + expect(testInjector.logger.error).calledWith( + `Could not load opts from "${path.resolve('some/mocha.opts/file')}". Please make sure opts file exists.` + ); }); it('should load default mocha.opts file if not specified', () => { @@ -149,7 +147,7 @@ describe(MochaOptionsLoader.name, () => { expect(fs.readFileSync).calledWith(path.resolve('./test/mocha.opts')); }); - it('shouldn\'t load anything if mocha.opts = false', () => { + it("shouldn't load anything if mocha.opts = false", () => { config.mochaOptions = { opts: false }; @@ -162,7 +160,10 @@ describe(MochaOptionsLoader.name, () => { existsFileStub.returns(false); const options = sut.load(config); expect(options).deep.eq(createMochaOptions()); - expect(testInjector.logger.debug).calledWith('No mocha opts file found, not loading additional mocha options (%s.opts was not defined).', 'mochaOptions'); + expect(testInjector.logger.debug).calledWith( + 'No mocha opts file found, not loading additional mocha options (%s.opts was not defined).', + 'mochaOptions' + ); }); it('should load `--require` and `-r` properties if specified in mocha.opts file', () => { @@ -173,10 +174,7 @@ describe(MochaOptionsLoader.name, () => { config.mochaOptions = { opts: '.' }; const options = sut.load(config); expect(options).deep.include({ - require: [ - 'src/test/support/setup', - 'babel-require' - ] + require: ['src/test/support/setup', 'babel-require'] }); }); @@ -213,15 +211,17 @@ describe(MochaOptionsLoader.name, () => { ui: 'exports' }; const options = sut.load(config); - expect(options).deep.equal(createMochaOptions({ - asyncOnly: false, - extension: ['js'], - opts: 'path/to/opts/file', - require: ['ts-node/register'], - spec: ['test'], - timeout: 4000, - ui: 'exports' - })); + expect(options).deep.equal( + createMochaOptions({ + asyncOnly: false, + extension: ['js'], + opts: 'path/to/opts/file', + require: ['ts-node/register'], + spec: ['test'], + timeout: 4000, + ui: 'exports' + }) + ); }); it('should ignore additional properties', () => { @@ -230,7 +230,7 @@ describe(MochaOptionsLoader.name, () => { --ignore-leaks `); config.mochaOptions = { - opts: 'some/mocha.opts/file', + opts: 'some/mocha.opts/file' }; const options = sut.load(config); expect(options).not.have.property('reporter'); @@ -245,22 +245,22 @@ describe(MochaOptionsLoader.name, () => { --ui `); config.mochaOptions = { - opts: 'some/mocha.opts/file', + opts: 'some/mocha.opts/file' }; const options = sut.load(config); - expect(options).deep.eq(createMochaOptions({ - extension: ['js'], - opts: 'some/mocha.opts/file', - spec: [ - 'test' - ], - timeout: undefined, - ui: undefined - })); + expect(options).deep.eq( + createMochaOptions({ + extension: ['js'], + opts: 'some/mocha.opts/file', + spec: ['test'], + timeout: undefined, + ui: undefined + }) + ); }); }); - function createMochaOptions(overrides?: Partial) { + function createMochaOptions(overrides?: Partial) { return { extension: ['js'], file: [], @@ -269,8 +269,7 @@ describe(MochaOptionsLoader.name, () => { spec: ['test'], timeout: 2000, ui: 'bdd', - ... overrides + ...overrides }; } - }); diff --git a/packages/mocha-runner/test/unit/MochaTestRunner.spec.ts b/packages/mocha-runner/test/unit/MochaTestRunner.spec.ts index 63c52a8a71..c90d3e3b3d 100644 --- a/packages/mocha-runner/test/unit/MochaTestRunner.spec.ts +++ b/packages/mocha-runner/test/unit/MochaTestRunner.spec.ts @@ -13,7 +13,6 @@ import { StrykerMochaReporter } from '../../src/StrykerMochaReporter'; import * as utils from '../../src/utils'; describe(MochaTestRunner.name, () => { - let MochaStub: sinon.SinonStub; let mocha: sinon.SinonStubbedInstance & { suite: sinon.SinonStubbedInstance }; let sut: MochaTestRunner; @@ -38,7 +37,7 @@ describe(MochaTestRunner.name, () => { delete StrykerMochaReporter.log; }); - function createSut(mochaSettings: Partial<{ fileNames: ReadonlyArray, mochaOptions: MochaOptions }>) { + function createSut(mochaSettings: Partial<{ fileNames: readonly string[]; mochaOptions: MochaOptions }>) { testInjector.options.mochaOptions = mochaSettings.mochaOptions || {}; return testInjector.injector .provideValue(commonTokens.sandboxFileNames, mochaSettings.fileNames || ['src/math.js', 'test/mathSpec.js']) @@ -120,8 +119,9 @@ describe(MochaTestRunner.name, () => { const actFn = () => sut.init(); // Assert - expect(actFn).throws(`[MochaTestRunner] No files discovered (tried pattern(s) ${relativeGlobbing - }). Please specify the files (glob patterns) containing your tests in mochaOptions.files in your stryker.conf.js file.`); + expect(actFn).throws( + `[MochaTestRunner] No files discovered (tried pattern(s) ${relativeGlobbing}). Please specify the files (glob patterns) containing your tests in mochaOptions.files in your stryker.conf.js file.` + ); expect(testInjector.logger.debug).calledWith(`Tried ${absoluteGlobbing} on files: ${filesStringified}.`); }); @@ -145,17 +145,17 @@ describe(MochaTestRunner.name, () => { it('should log about mocha >= 6 detection', async () => { sut = createSut({}); await sut.init(); - expect(testInjector.logger.debug).calledWith('Mocha >= 6 detected. Using mocha\'s `handleFiles` to load files'); + expect(testInjector.logger.debug).calledWith("Mocha >= 6 detected. Using mocha's `handleFiles` to load files"); }); it('should mock away the `process.exit` method when calling the mocha function (unfortunate side effect)', async () => { sut = createSut({}); const originalProcessExit = process.exit; let stubbedProcessExit = process.exit; - handleFilesStub.callsFake(() => stubbedProcessExit = process.exit); + handleFilesStub.callsFake(() => (stubbedProcessExit = process.exit)); await sut.init(); - expect(originalProcessExit, 'Process.exit doesn\'t seem to be stubbed away').not.eq(stubbedProcessExit); - expect(originalProcessExit, 'Process.exit doesn\'t seem to be reset').eq(process.exit); + expect(originalProcessExit, "Process.exit doesn't seem to be stubbed away").not.eq(stubbedProcessExit); + expect(originalProcessExit, "Process.exit doesn't seem to be reset").eq(process.exit); }); it('should pass along supported options to mocha', async () => { @@ -227,12 +227,10 @@ describe(MochaTestRunner.name, () => { expect(require.cache['bar.js']).undefined; expect(require.cache['baz.js']).eq('baz'); }); - }); async function actRun(options: RunOptions = { timeout: 0 }) { mocha.run.callsArg(0); return sut.run(options); } - }); diff --git a/packages/mocha-runner/test/unit/Timer.spec.ts b/packages/mocha-runner/test/unit/Timer.spec.ts index 38553b8f99..1178f2f2bc 100644 --- a/packages/mocha-runner/test/unit/Timer.spec.ts +++ b/packages/mocha-runner/test/unit/Timer.spec.ts @@ -5,17 +5,14 @@ import Timer from '../../src/Timer'; describe('Timer', () => { let sut: Timer; - beforeEach(() => - sut = new Timer()); + beforeEach(() => (sut = new Timer())); describe('when global Date object is mocked', () => { let clock: sinon.SinonFakeTimers; - beforeEach(() => clock = sinon.useFakeTimers()); + beforeEach(() => (clock = sinon.useFakeTimers())); afterEach(() => clock.restore()); - it('should work even when global Date object is mocked', () => - expect(sut.elapsedMs()).to.be.greaterThan(-1)); - + it('should work even when global Date object is mocked', () => expect(sut.elapsedMs()).to.be.greaterThan(-1)); }); }); diff --git a/packages/mutator-specification/.eslintrc b/packages/mutator-specification/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/mutator-specification/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/mutator-specification/src/ArrayLiteralMutatorSpec.ts b/packages/mutator-specification/src/ArrayLiteralMutatorSpec.ts index c374c7dfcb..733a9f0c3d 100644 --- a/packages/mutator-specification/src/ArrayLiteralMutatorSpec.ts +++ b/packages/mutator-specification/src/ArrayLiteralMutatorSpec.ts @@ -2,25 +2,23 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function ArrayLiteralMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('ArrayLiteralMutator', () => { - it('should have name "ArrayLiteral"', () => { expect(name).eq('ArrayLiteral'); }); it('should mutate filled array literals as empty arrays', () => { expectMutation('[a, 1 + 1]', '[]'); - expectMutation(`['val']`, '[]'); + expectMutation("['val']", '[]'); }); it('should not mutate array initializers (leave that for ArrayNewExpressionMutator)', () => { - expectMutation(`new Array()`); - expectMutation(`new Array(1, 2, 3)`); + expectMutation('new Array()'); + expectMutation('new Array(1, 2, 3)'); }); it('should mutate empty array literals as a filled array', () => { - expectMutation('[]', '[\'Stryker was here\']'); + expectMutation('[]', "['Stryker was here']"); }); }); } diff --git a/packages/mutator-specification/src/ArrayNewExpressionMutatorSpec.ts b/packages/mutator-specification/src/ArrayNewExpressionMutatorSpec.ts index 7ee9b877b8..0b3d63743b 100644 --- a/packages/mutator-specification/src/ArrayNewExpressionMutatorSpec.ts +++ b/packages/mutator-specification/src/ArrayNewExpressionMutatorSpec.ts @@ -2,21 +2,19 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function ArrayNewExpressionMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('ArrayNewExpressionMutator', () => { - it('should have name "ArrayNewExpression"', () => { expect(name).eq('ArrayNewExpression'); }); it('should mutate filled array literals as empty arrays', () => { expectMutation('new Array(a, 1 + 1)', 'new Array()'); - expectMutation(`new Array('val')`, 'new Array()'); + expectMutation("new Array('val')", 'new Array()'); }); it('should not mutate array literals (leave that for ArrayLiteralMutator)', () => { - expectMutation(`[]`); - expectMutation(`[1, 2 ,3]`); + expectMutation('[]'); + expectMutation('[1, 2 ,3]'); }); it('should not mutate other new expressions', () => { diff --git a/packages/mutator-specification/src/ArrowFunctionMutatorSpec.ts b/packages/mutator-specification/src/ArrowFunctionMutatorSpec.ts index 668408f82f..9349983c02 100644 --- a/packages/mutator-specification/src/ArrowFunctionMutatorSpec.ts +++ b/packages/mutator-specification/src/ArrowFunctionMutatorSpec.ts @@ -3,7 +3,6 @@ import ExpectMutation from './ExpectMutation'; export default function ArrowFunctionMutatorSpec(name: string, expectMutation: ExpectMutation) { describe('ArrowFunctionMutator', () => { - it('should have name "ArrowFunction"', () => { expect(name).eq('ArrowFunction'); }); diff --git a/packages/mutator-specification/src/BinaryExpressionMutatorSpec.ts b/packages/mutator-specification/src/BinaryExpressionMutatorSpec.ts index 0ff7faf791..7434a8bfc5 100644 --- a/packages/mutator-specification/src/BinaryExpressionMutatorSpec.ts +++ b/packages/mutator-specification/src/BinaryExpressionMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function BinaryExpressionMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('BinaryExpressionMutator', () => { - it('should have name "BinaryExpression"', () => { expect(name).eq('BinaryExpression'); }); @@ -44,6 +42,5 @@ export default function BinaryExpressionMutatorSpec(name: string, expectMutation expectMutation('a && b', 'a || b'); expectMutation('a || b', 'a && b'); }); - }); } diff --git a/packages/mutator-specification/src/BlockMutatorSpec.ts b/packages/mutator-specification/src/BlockMutatorSpec.ts index ad8dafe4f7..1be2b825a1 100644 --- a/packages/mutator-specification/src/BlockMutatorSpec.ts +++ b/packages/mutator-specification/src/BlockMutatorSpec.ts @@ -1,10 +1,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; -export default function BlockMutatorSpec( - name: string, - expectMutation: ExpectMutation -) { +export default function BlockMutatorSpec(name: string, expectMutation: ExpectMutation) { describe('BlockMutator', () => { it('should have name "Block"', () => { expect(name).eq('Block'); @@ -36,10 +33,7 @@ export default function BlockMutatorSpec( }); it('should mutate the body of a case statement if defined as a block', () => { - expectMutation( - 'switch (v) { case 42: { a = "spam"; break; } }', - 'switch (v) { case 42: {} }' - ); + expectMutation('switch (v) { case 42: { a = "spam"; break; } }', 'switch (v) { case 42: {} }'); }); // object tests diff --git a/packages/mutator-specification/src/BooleanSubstitutionMutatorSpec.ts b/packages/mutator-specification/src/BooleanSubstitutionMutatorSpec.ts index 4788847233..6f372a98c4 100644 --- a/packages/mutator-specification/src/BooleanSubstitutionMutatorSpec.ts +++ b/packages/mutator-specification/src/BooleanSubstitutionMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function BooleanSubstitutionMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('BooleanSubstitutionMutator', () => { - it('should have name "BooleanSubstitution"', () => { expect(name).eq('BooleanSubstitution'); }); diff --git a/packages/mutator-specification/src/ConditionalExpressionMutatorSpec.ts b/packages/mutator-specification/src/ConditionalExpressionMutatorSpec.ts index be6b6e9388..7f25b88740 100644 --- a/packages/mutator-specification/src/ConditionalExpressionMutatorSpec.ts +++ b/packages/mutator-specification/src/ConditionalExpressionMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function ConditionalExpressionMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('ConditionalExpressionMutator', () => { - it('should have name "ConditionalExpression"', () => { expect(name).eq('ConditionalExpression'); }); diff --git a/packages/mutator-specification/src/DoStatementMutatorSpec.ts b/packages/mutator-specification/src/DoStatementMutatorSpec.ts index e108f30c69..d43db2de63 100644 --- a/packages/mutator-specification/src/DoStatementMutatorSpec.ts +++ b/packages/mutator-specification/src/DoStatementMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function DoStatementMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('DoStatementMutator', () => { - it('should have name "DoStatement"', () => { expect(name).eq('DoStatement'); }); @@ -12,6 +10,5 @@ export default function DoStatementMutatorSpec(name: string, expectMutation: Exp it('should mutate the expression of a do statement', () => { expectMutation('do { console.log(); } while(a < b);', 'do { console.log(); } while(false);'); }); - }); } diff --git a/packages/mutator-specification/src/ForStatementMutatorSpec.ts b/packages/mutator-specification/src/ForStatementMutatorSpec.ts index b3b13806aa..9ba1755d4a 100644 --- a/packages/mutator-specification/src/ForStatementMutatorSpec.ts +++ b/packages/mutator-specification/src/ForStatementMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function ForStatementMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('ForStatementMutator', () => { - it('should have name "ForStatement"', () => { expect(name).eq('ForStatement'); }); @@ -14,7 +12,7 @@ export default function ForStatementMutatorSpec(name: string, expectMutation: Ex }); it('should mutate the condition of a for statement without a condition', () => { - expectMutation('for(let i=0;; i++) { console.log(); }', `for (let i = 0; false; i++) { console.log(); }`); + expectMutation('for(let i=0;; i++) { console.log(); }', 'for (let i = 0; false; i++) { console.log(); }'); }); }); } diff --git a/packages/mutator-specification/src/IfStatementMutatorSpec.ts b/packages/mutator-specification/src/IfStatementMutatorSpec.ts index 96ad1291c2..cac39c641c 100644 --- a/packages/mutator-specification/src/IfStatementMutatorSpec.ts +++ b/packages/mutator-specification/src/IfStatementMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function IfStatementMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('IfStatementMutator', () => { - it('should have name "IfStatement"', () => { expect(name).eq('IfStatement'); }); @@ -12,6 +10,5 @@ export default function IfStatementMutatorSpec(name: string, expectMutation: Exp it('should mutate an expression to `true` and `false`', () => { expectMutation('if (something) { a++ }', 'if (true) { a++ }', 'if (false) { a++ }'); }); - }); } diff --git a/packages/mutator-specification/src/ObjectLiteralMutatorSpec.ts b/packages/mutator-specification/src/ObjectLiteralMutatorSpec.ts index 1f3e2e5cd1..6e7e0cfff1 100644 --- a/packages/mutator-specification/src/ObjectLiteralMutatorSpec.ts +++ b/packages/mutator-specification/src/ObjectLiteralMutatorSpec.ts @@ -1,10 +1,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; -export default function ObjectLiteralMutatorSpec( - name: string, - expectMutation: ExpectMutation -) { +export default function ObjectLiteralMutatorSpec(name: string, expectMutation: ExpectMutation) { describe('ObjectLiteralMutator', () => { it('should have name "ObjectLiteral"', () => { expect(name).eq('ObjectLiteral'); diff --git a/packages/mutator-specification/src/PrefixUnaryExpressionMutatorSpec.ts b/packages/mutator-specification/src/PrefixUnaryExpressionMutatorSpec.ts index c49cafa7c8..b9016514c9 100644 --- a/packages/mutator-specification/src/PrefixUnaryExpressionMutatorSpec.ts +++ b/packages/mutator-specification/src/PrefixUnaryExpressionMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function PrefixUnaryExpressionMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('PrefixUnaryExpressionMutator', () => { - it('should have name "PrefixUnaryExpression"', () => { expect(name).eq('PrefixUnaryExpression'); }); diff --git a/packages/mutator-specification/src/StringLiteralMutatorSpec.ts b/packages/mutator-specification/src/StringLiteralMutatorSpec.ts index 4db4f3923e..a0954415c2 100644 --- a/packages/mutator-specification/src/StringLiteralMutatorSpec.ts +++ b/packages/mutator-specification/src/StringLiteralMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function StringLiteralMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('StringLiteralMutator', () => { - it('should have name "StringLiteral"', () => { expect(name).eq('StringLiteral'); }); @@ -14,7 +12,7 @@ export default function StringLiteralMutatorSpec(name: string, expectMutation: E }); it('should mutate a string literal with single quotes', () => { - expectMutation('const b = \'Hello world!\';', 'const b = "";'); + expectMutation("const b = 'Hello world!';", 'const b = "";'); }); it('should mutate a template string', () => { diff --git a/packages/mutator-specification/src/SwitchCaseMutatorSpec.ts b/packages/mutator-specification/src/SwitchCaseMutatorSpec.ts index f553f37981..800bc3ef3e 100644 --- a/packages/mutator-specification/src/SwitchCaseMutatorSpec.ts +++ b/packages/mutator-specification/src/SwitchCaseMutatorSpec.ts @@ -1,10 +1,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; -export default function SwitchCaseMutatorSpec( - name: string, - expectMutation: ExpectMutation -) { +export default function SwitchCaseMutatorSpec(name: string, expectMutation: ExpectMutation) { describe('SwitchCaseMutator', () => { it('should have name "SwitchCase"', () => { expect(name).eq('SwitchCase'); diff --git a/packages/mutator-specification/src/WhileStatementMutatorSpec.ts b/packages/mutator-specification/src/WhileStatementMutatorSpec.ts index 21765eeb5d..f1f5e47325 100644 --- a/packages/mutator-specification/src/WhileStatementMutatorSpec.ts +++ b/packages/mutator-specification/src/WhileStatementMutatorSpec.ts @@ -2,9 +2,7 @@ import { expect } from 'chai'; import ExpectMutation from './ExpectMutation'; export default function WhileStatementMutatorSpec(name: string, expectMutation: ExpectMutation) { - describe('WhileStatementMutator', () => { - it('should have name "WhileStatement"', () => { expect(name).eq('WhileStatement'); }); diff --git a/packages/test-helpers/.eslintrc b/packages/test-helpers/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/test-helpers/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/test-helpers/src/TestInjector.ts b/packages/test-helpers/src/TestInjector.ts index c590ae6525..1ac73763cf 100644 --- a/packages/test-helpers/src/TestInjector.ts +++ b/packages/test-helpers/src/TestInjector.ts @@ -6,16 +6,15 @@ import { Injector, rootInjector, Scope } from 'typed-inject'; import * as factory from './factory'; class TestInjector { - private readonly providePluginResolver = (): PluginResolver => { return this.pluginResolver; - } + }; private readonly provideLogger = (): Logger => { return this.logger; - } + }; private readonly provideOptions = () => { return this.options; - } + }; public pluginResolver: sinon.SinonStubbedInstance; public options: StrykerOptions; diff --git a/packages/test-helpers/src/factory.ts b/packages/test-helpers/src/factory.ts index 5f90f4cc29..9e5b6d31d6 100644 --- a/packages/test-helpers/src/factory.ts +++ b/packages/test-helpers/src/factory.ts @@ -13,7 +13,8 @@ import { Injector } from 'typed-inject'; /** * A 1x1 png base64 encoded */ -export const PNG_BASE64_ENCODED = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAAAASUVORK5CYII='; +export const PNG_BASE64_ENCODED = + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAEnQAABJ0Ad5mH3gAAAAMSURBVBhXY/j//z8ABf4C/qc1gYQAAAAASUVORK5CYII='; /** * Use this factory method to create deep test data @@ -100,9 +101,15 @@ export function logger(): sinon.SinonStubbedInstance { export function testFramework(): TestFramework { return { - beforeEach(codeFragment: string) { return `beforeEach(){ ${codeFragment}}`; }, - afterEach(codeFragment: string) { return `afterEach(){ ${codeFragment}}`; }, - filter(selections: TestSelection[]) { return `filter: ${selections}`; } + beforeEach(codeFragment: string) { + return `beforeEach(){ ${codeFragment}}`; + }, + afterEach(codeFragment: string) { + return `afterEach(){ ${codeFragment}}`; + }, + filter(selections: TestSelection[]) { + return `filter: ${selections}`; + } }; } @@ -148,12 +155,20 @@ export const strykerOptions = factoryMethod(() => new Config()); export const config = factoryMethod(() => new Config()); -export const ALL_REPORTER_EVENTS: (keyof Reporter)[] = - ['onSourceFileRead', 'onAllSourceFilesRead', 'onAllMutantsMatchedWithTests', 'onMutantTested', 'onAllMutantsTested', 'onMutationTestReportReady', 'onScoreCalculated', 'wrapUp']; +export const ALL_REPORTER_EVENTS: Array = [ + 'onSourceFileRead', + 'onAllSourceFilesRead', + 'onAllMutantsMatchedWithTests', + 'onMutantTested', + 'onAllMutantsTested', + 'onMutationTestReportReady', + 'onScoreCalculated', + 'wrapUp' +]; export function reporter(name = 'fooReporter'): sinon.SinonStubbedInstance> { const reporter = { name } as any; - ALL_REPORTER_EVENTS.forEach(event => reporter[event] = sinon.stub()); + ALL_REPORTER_EVENTS.forEach(event => (reporter[event] = sinon.stub())); return reporter; } @@ -192,7 +207,7 @@ export function injector(): sinon.SinonStubbedInstance { provideClass: sinon.stub(), provideFactory: sinon.stub(), provideValue: sinon.stub(), - resolve: sinon.stub(), + resolve: sinon.stub() }; injectorMock.provideClass.returnsThis(); injectorMock.provideFactory.returnsThis(); diff --git a/packages/typescript/.eslintrc b/packages/typescript/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/typescript/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/typescript/src/TypescriptConfigEditor.ts b/packages/typescript/src/TypescriptConfigEditor.ts index 3168a43778..34db3a8685 100644 --- a/packages/typescript/src/TypescriptConfigEditor.ts +++ b/packages/typescript/src/TypescriptConfigEditor.ts @@ -17,9 +17,8 @@ const COMPILER_OPTIONS_OVERRIDES: Readonly> = Object }); export default class TypescriptConfigEditor implements ConfigEditor { - public static inject = tokens(commonTokens.logger); - constructor(private readonly log: Logger) { } + constructor(private readonly log: Logger) {} public edit(strykerConfig: Config, host: ts.ParseConfigHost = ts.sys) { this.loadTSConfig(strykerConfig, host); @@ -34,7 +33,7 @@ export default class TypescriptConfigEditor implements ConfigEditor { strykerConfig[CONFIG_KEY] = this.overrideOptions(tsconfig); } } else { - this.log.debug('No \'%s\' specified, not loading any config', CONFIG_KEY_FILE); + this.log.debug("No '%s' specified, not loading any config", CONFIG_KEY_FILE); } } @@ -58,7 +57,8 @@ export default class TypescriptConfigEditor implements ConfigEditor { host, configFileBase, { project: configFileBase }, - tsconfigFileNameNormalizedForTypeScript); + tsconfigFileNameNormalizedForTypeScript + ); if (tsconfig.errors.length) { const error = ts.formatDiagnostics(tsconfig.errors, diagnosticsHost(configFileBase)); this.log.error(`Error while loading tsconfig file '${tsconfigFileName}': ${error}`); diff --git a/packages/typescript/src/TypescriptMutator.ts b/packages/typescript/src/TypescriptMutator.ts index 7f534cc878..fa4bf4db84 100644 --- a/packages/typescript/src/TypescriptMutator.ts +++ b/packages/typescript/src/TypescriptMutator.ts @@ -8,17 +8,14 @@ import { nodeMutators } from './mutator'; import NodeMutator from './mutator/NodeMutator'; export function typescriptMutatorFactory(injector: Injector): TypescriptMutator { - return injector - .provideValue(MUTATORS_TOKEN, nodeMutators) - .injectClass(TypescriptMutator); + return injector.provideValue(MUTATORS_TOKEN, nodeMutators).injectClass(TypescriptMutator); } typescriptMutatorFactory.inject = tokens(commonTokens.injector); export const MUTATORS_TOKEN = 'mutators'; export class TypescriptMutator { - public static inject = tokens(commonTokens.options, MUTATORS_TOKEN); - constructor(private readonly options: StrykerOptions, public readonly mutators: ReadonlyArray) { } + constructor(private readonly options: StrykerOptions, public readonly mutators: readonly NodeMutator[]) {} public mutate(inputFiles: File[]): Mutant[] { const tsConfig = getTSConfig(this.options); @@ -45,6 +42,8 @@ export class TypescriptMutator { } const shouldNodeBeSkipped = (node: ts.Node): boolean => { - return node.kind === ts.SyntaxKind.InterfaceDeclaration || - node.modifiers !== undefined && node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DeclareKeyword); + return ( + node.kind === ts.SyntaxKind.InterfaceDeclaration || + (node.modifiers !== undefined && node.modifiers.some(modifier => modifier.kind === ts.SyntaxKind.DeclareKeyword)) + ); }; diff --git a/packages/typescript/src/TypescriptTranspiler.ts b/packages/typescript/src/TypescriptTranspiler.ts index 4514bf8870..4e8a8c1ee2 100644 --- a/packages/typescript/src/TypescriptTranspiler.ts +++ b/packages/typescript/src/TypescriptTranspiler.ts @@ -12,14 +12,16 @@ export default class TypescriptTranspiler implements Transpiler { private readonly filter: TranspileFilter; public static inject = tokens(commonTokens.options, commonTokens.produceSourceMaps, commonTokens.getLogger); - constructor(private readonly options: StrykerOptions, - private readonly produceSourceMaps: boolean, - private readonly getLogger: LoggerFactoryMethod) { + constructor( + private readonly options: StrykerOptions, + private readonly produceSourceMaps: boolean, + private readonly getLogger: LoggerFactoryMethod + ) { guardTypescriptVersion(); this.filter = TranspileFilter.create(this.options); } - public transpile(files: ReadonlyArray): Promise> { + public transpile(files: readonly File[]): Promise { const typescriptFiles = this.filterIsIncluded(files); if (this.languageService) { this.languageService.replace(typescriptFiles); @@ -35,30 +37,34 @@ export default class TypescriptTranspiler implements Transpiler { } } - private filterIsIncluded(files: ReadonlyArray): ReadonlyArray { + private filterIsIncluded(files: readonly File[]): readonly File[] { return files.filter(file => this.filter.isIncluded(file.name)); } - private createLanguageService(typescriptFiles: ReadonlyArray) { + private createLanguageService(typescriptFiles: readonly File[]) { const tsConfig = getTSConfig(this.options); const compilerOptions: ts.CompilerOptions = (tsConfig && tsConfig.options) || {}; return new TranspilingLanguageService( - compilerOptions, typescriptFiles, getProjectDirectory(this.options), this.produceSourceMaps, this.getLogger); + compilerOptions, + typescriptFiles, + getProjectDirectory(this.options), + this.produceSourceMaps, + this.getLogger + ); } - private transpileFiles(files: ReadonlyArray): ReadonlyArray { + private transpileFiles(files: readonly File[]): readonly File[] { let isSingleOutput = false; const fileDictionary: { [name: string]: File } = {}; - files.forEach(file => fileDictionary[file.name] = file); + files.forEach(file => (fileDictionary[file.name] = file)); files.forEach(file => { if (!isHeaderFile(file.name)) { if (this.filter.isIncluded(file.name)) { - // File is to be transpiled. Only emit if more output is expected. if (!isSingleOutput) { const emitOutput = this.languageService.emit(file.name); isSingleOutput = emitOutput.singleResult; - emitOutput.outputFiles.forEach(file => fileDictionary[file.name] = file); + emitOutput.outputFiles.forEach(file => (fileDictionary[file.name] = file)); } // Remove original file diff --git a/packages/typescript/src/helpers/tsHelpers.ts b/packages/typescript/src/helpers/tsHelpers.ts index 98c4e499a0..a6d51a93aa 100644 --- a/packages/typescript/src/helpers/tsHelpers.ts +++ b/packages/typescript/src/helpers/tsHelpers.ts @@ -6,11 +6,7 @@ import * as ts from 'typescript'; import { CONFIG_KEY, CONFIG_KEY_FILE } from './keys'; export function parseFile(file: File, target: ts.ScriptTarget | undefined) { - return ts.createSourceFile( - file.name, - file.textContent, - target || ts.ScriptTarget.ES5, - /*setParentNodes*/ true); + return ts.createSourceFile(file.name, file.textContent, target || ts.ScriptTarget.ES5, /*setParentNodes*/ true); } /** @@ -42,7 +38,9 @@ export function getProjectDirectory(options: StrykerOptions) { */ export function guardTypescriptVersion() { if (!semver.satisfies(ts.version, '>=2.4')) { - throw new Error(`Installed typescript version ${ts.version} is not supported by stryker-typescript. Please install version 2.5 or higher (\`npm install typescript@^2.5\`).`); + throw new Error( + `Installed typescript version ${ts.version} is not supported by stryker-typescript. Please install version 2.5 or higher (\`npm install typescript@^2.5\`).` + ); } } diff --git a/packages/typescript/src/mutator/ArrayLiteralMutator.ts b/packages/typescript/src/mutator/ArrayLiteralMutator.ts index 31e1e5ecd2..fa00ff9ce5 100644 --- a/packages/typescript/src/mutator/ArrayLiteralMutator.ts +++ b/packages/typescript/src/mutator/ArrayLiteralMutator.ts @@ -12,7 +12,7 @@ export default class ArrayLiteralMutator extends NodeMutator return [{ node: fn, replacement: '() => undefined' }]; } - } diff --git a/packages/typescript/src/mutator/BinaryExpressionMutator.ts b/packages/typescript/src/mutator/BinaryExpressionMutator.ts index cdf02f469b..cd552126cd 100644 --- a/packages/typescript/src/mutator/BinaryExpressionMutator.ts +++ b/packages/typescript/src/mutator/BinaryExpressionMutator.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; import NodeMutator, { NodeReplacement } from './NodeMutator'; -const replaceTokens: ts.MapLike = { +const replaceTokens: ts.MapLike = { [ts.SyntaxKind.PlusToken]: ['-'], [ts.SyntaxKind.MinusToken]: ['+'], [ts.SyntaxKind.SlashToken]: ['*'], @@ -20,7 +20,6 @@ const replaceTokens: ts.MapLike = { }; export default class BinaryExpressionMutator extends NodeMutator { - public name: string = 'BinaryExpression'; public guard(node: ts.Node): node is ts.BinaryExpression { @@ -29,8 +28,7 @@ export default class BinaryExpressionMutator extends NodeMutator ({node: node.operatorToken, replacement })); + return replaceTokens[node.operatorToken.kind].map(replacement => ({ node: node.operatorToken, replacement })); } else { return []; } diff --git a/packages/typescript/src/mutator/BlockMutator.ts b/packages/typescript/src/mutator/BlockMutator.ts index 0ada4f1f7e..11ae4965fe 100644 --- a/packages/typescript/src/mutator/BlockMutator.ts +++ b/packages/typescript/src/mutator/BlockMutator.ts @@ -15,5 +15,4 @@ export default class BlockMutator extends NodeMutator { return []; } } - } diff --git a/packages/typescript/src/mutator/BooleanSubstitutionMutator.ts b/packages/typescript/src/mutator/BooleanSubstitutionMutator.ts index 68c1426f41..90571184b2 100644 --- a/packages/typescript/src/mutator/BooleanSubstitutionMutator.ts +++ b/packages/typescript/src/mutator/BooleanSubstitutionMutator.ts @@ -2,7 +2,6 @@ import * as ts from 'typescript'; import NodeMutator, { NodeReplacement } from './NodeMutator'; export default class BooleanSubstitutionMutator extends NodeMutator { - public name: string = 'BooleanSubstitution'; public guard(node: ts.Node): node is ts.BooleanLiteral { @@ -11,9 +10,9 @@ export default class BooleanSubstitutionMutator extends NodeMutator { - public name = 'DoStatement'; public guard(node: ts.Node): node is ts.DoStatement { @@ -12,5 +11,4 @@ export default class DoStatementMutator extends NodeMutator { protected identifyReplacements(node: ts.DoStatement): NodeReplacement[] { return [{ node: node.expression, replacement: 'false' }]; } - } diff --git a/packages/typescript/src/mutator/ForStatementMutator.ts b/packages/typescript/src/mutator/ForStatementMutator.ts index 66585565a9..5abb161cb3 100644 --- a/packages/typescript/src/mutator/ForStatementMutator.ts +++ b/packages/typescript/src/mutator/ForStatementMutator.ts @@ -3,7 +3,6 @@ import { printNode } from '../helpers/tsHelpers'; import NodeMutator, { NodeReplacement } from './NodeMutator'; export default class ForStatementMutator extends NodeMutator { - public name = 'ForStatement'; public guard(node: ts.Node): node is ts.ForStatement { @@ -20,5 +19,4 @@ export default class ForStatementMutator extends NodeMutator { return [{ node, replacement }]; } } - } diff --git a/packages/typescript/src/mutator/IfStatementMutator.ts b/packages/typescript/src/mutator/IfStatementMutator.ts index fb7cad649c..6def3a477b 100644 --- a/packages/typescript/src/mutator/IfStatementMutator.ts +++ b/packages/typescript/src/mutator/IfStatementMutator.ts @@ -9,10 +9,6 @@ export default class IfStatementMutator extends NodeMutator { } protected identifyReplacements(ifStatement: ts.IfStatement): NodeReplacement[] { - return [ - { node: ifStatement.expression, replacement: 'true' }, - { node: ifStatement.expression, replacement: 'false' } - ]; + return [{ node: ifStatement.expression, replacement: 'true' }, { node: ifStatement.expression, replacement: 'false' }]; } - } diff --git a/packages/typescript/src/mutator/NodeMutator.ts b/packages/typescript/src/mutator/NodeMutator.ts index 76f16c8349..ff1c4cf5b4 100644 --- a/packages/typescript/src/mutator/NodeMutator.ts +++ b/packages/typescript/src/mutator/NodeMutator.ts @@ -12,8 +12,7 @@ export default abstract class NodeMutator { public abstract guard(node: ts.Node): node is T; public mutate(node: T, sourceFile: ts.SourceFile): Mutant[] { - return this.identifyReplacements(node, sourceFile) - .map(replacement => this.createMutant(replacement.node, replacement.replacement, sourceFile)); + return this.identifyReplacements(node, sourceFile).map(replacement => this.createMutant(replacement.node, replacement.replacement, sourceFile)); } protected abstract identifyReplacements(node: T, sourceFile: ts.SourceFile): NodeReplacement[]; diff --git a/packages/typescript/src/mutator/ObjectLiteralMutator.ts b/packages/typescript/src/mutator/ObjectLiteralMutator.ts index 7ab9b4bd3c..ebc28ff5bd 100644 --- a/packages/typescript/src/mutator/ObjectLiteralMutator.ts +++ b/packages/typescript/src/mutator/ObjectLiteralMutator.ts @@ -15,5 +15,4 @@ export default class ObjectLiteralMutator extends NodeMutator } function isEmptyTemplate() { - return (str.kind === ts.SyntaxKind.FirstTemplateToken && (str as ts.NoSubstitutionTemplateLiteral).text === ''); + return str.kind === ts.SyntaxKind.FirstTemplateToken && (str as ts.NoSubstitutionTemplateLiteral).text === ''; } return isEmptyString() || isEmptyTemplate(); diff --git a/packages/typescript/src/mutator/SwitchCaseMutator.ts b/packages/typescript/src/mutator/SwitchCaseMutator.ts index c4f3bf01b2..9f0c960054 100644 --- a/packages/typescript/src/mutator/SwitchCaseMutator.ts +++ b/packages/typescript/src/mutator/SwitchCaseMutator.ts @@ -20,9 +20,7 @@ export default class SwitchCaseMutator extends NodeMutator 0) { - const clause = isDefaultClause(node) - ? ts.createDefaultClause([]) - : ts.createCaseClause(node.expression, []); + const clause = isDefaultClause(node) ? ts.createDefaultClause([]) : ts.createCaseClause(node.expression, []); const replacement = printNode(clause, sourceFile); return [{ node, replacement }]; } else { diff --git a/packages/typescript/src/mutator/index.ts b/packages/typescript/src/mutator/index.ts index 0badac28ba..ae4656833c 100644 --- a/packages/typescript/src/mutator/index.ts +++ b/packages/typescript/src/mutator/index.ts @@ -15,7 +15,7 @@ import StringLiteralMutator from './StringLiteralMutator'; import SwitchCaseMutator from './SwitchCaseMutator'; import WhileStatementMutator from './WhileStatementMutator'; -export const nodeMutators: ReadonlyArray = [ +export const nodeMutators: readonly NodeMutator[] = [ new BinaryExpressionMutator(), new BooleanSubstitutionMutator(), new ArrayLiteralMutator(), @@ -30,5 +30,5 @@ export const nodeMutators: ReadonlyArray = [ new ConditionalExpressionMutator(), new PrefixUnaryExpressionMutator(), new StringLiteralMutator(), - new SwitchCaseMutator(), + new SwitchCaseMutator() ]; diff --git a/packages/typescript/src/transpiler/ScriptFile.ts b/packages/typescript/src/transpiler/ScriptFile.ts index 6a1db1d492..300dfc198b 100644 --- a/packages/typescript/src/transpiler/ScriptFile.ts +++ b/packages/typescript/src/transpiler/ScriptFile.ts @@ -1,6 +1,5 @@ export default class ScriptFile { - constructor(public name: string, public content: string, public version = 0) { - } + constructor(public name: string, public content: string, public version = 0) {} public replace(newContent: string) { this.content = newContent; diff --git a/packages/typescript/src/transpiler/TranspileFilter.ts b/packages/typescript/src/transpiler/TranspileFilter.ts index c24ba3f3c9..ce7979f1bb 100644 --- a/packages/typescript/src/transpiler/TranspileFilter.ts +++ b/packages/typescript/src/transpiler/TranspileFilter.ts @@ -24,7 +24,6 @@ export default abstract class TranspileFilter { * A transpile filter based on ts config */ export class TSConfigFilter extends TranspileFilter { - private readonly fileNames: string[]; constructor({ fileNames }: { fileNames: string[] }) { @@ -33,7 +32,7 @@ export class TSConfigFilter extends TranspileFilter { } public isIncluded(fileName: string): boolean { - return this.fileNames.indexOf(fileName) !== -1; + return this.fileNames.includes(fileName); } } diff --git a/packages/typescript/src/transpiler/TranspilingLanguageService.ts b/packages/typescript/src/transpiler/TranspilingLanguageService.ts index f915220056..d3a9994f30 100644 --- a/packages/typescript/src/transpiler/TranspilingLanguageService.ts +++ b/packages/typescript/src/transpiler/TranspilingLanguageService.ts @@ -22,13 +22,15 @@ export default class TranspilingLanguageService { private readonly files: ts.MapLike = Object.create(null); private readonly diagnosticsFormatter: ts.FormatDiagnosticsHost; - constructor(compilerOptions: Readonly, - rootFiles: ReadonlyArray, - private readonly projectDirectory: string, - private readonly produceSourceMaps: boolean, - getLogger: LoggerFactoryMethod) { + constructor( + compilerOptions: Readonly, + rootFiles: readonly File[], + private readonly projectDirectory: string, + private readonly produceSourceMaps: boolean, + getLogger: LoggerFactoryMethod + ) { this.compilerOptions = this.adaptCompilerOptions(compilerOptions); - rootFiles.forEach(file => this.files[file.name] = new ScriptFile(file.name, file.textContent)); + rootFiles.forEach(file => (this.files[file.name] = new ScriptFile(file.name, file.textContent))); const host = this.createLanguageServiceHost(); this.languageService = ts.createLanguageService(host); this.logger = getLogger(TranspilingLanguageService.name); @@ -56,12 +58,11 @@ export default class TranspilingLanguageService { * Replaces the content of the given text files * @param mutantCandidate The mutant used to replace the original source */ - public replace(replacements: ReadonlyArray) { - replacements.forEach(replacement => - this.files[replacement.name].replace(replacement.textContent)); + public replace(replacements: readonly File[]) { + replacements.forEach(replacement => this.files[replacement.name].replace(replacement.textContent)); } - public getSemanticDiagnostics(files: ReadonlyArray) { + public getSemanticDiagnostics(files: readonly File[]) { const fileNames = files.map(file => file.name); const errors = flatMap(fileNames, fileName => this.languageService.getSemanticDiagnostics(normalizeFileForTypescript(fileName))); return ts.formatDiagnostics(errors, this.diagnosticsFormatter); @@ -123,13 +124,13 @@ export default class TranspilingLanguageService { } private resolveFileName(fileName: string) { - if (fileName.match(libRegex)) { + if (libRegex.exec(fileName)) { const typescriptLocation = require.resolve('typescript'); const newFileName = path.resolve(path.dirname(typescriptLocation), fileName); this.logger.debug(`Resolving lib file ${fileName} to ${newFileName}`); return newFileName; - } else { - return fileName; } + + return fileName; } } diff --git a/packages/typescript/test/integration/allowJS.it.spec.ts b/packages/typescript/test/integration/allowJS.it.spec.ts index 57ae7c2395..131ce53161 100644 --- a/packages/typescript/test/integration/allowJS.it.spec.ts +++ b/packages/typescript/test/integration/allowJS.it.spec.ts @@ -9,7 +9,6 @@ import TypescriptConfigEditor from '../../src/TypescriptConfigEditor'; import TypescriptTranspiler from '../../src/TypescriptTranspiler'; describe('AllowJS integration', () => { - let config: Config; let inputFiles: File[]; @@ -17,7 +16,7 @@ describe('AllowJS integration', () => { const configEditor = testInjector.injector.injectClass(TypescriptConfigEditor); config = new Config(); config.set({ - tsconfigFile: path.resolve(__dirname, '..', '..', 'testResources', 'allowJS', 'tsconfig.json'), + tsconfigFile: path.resolve(__dirname, '..', '..', 'testResources', 'allowJS', 'tsconfig.json') }); configEditor.edit(config); inputFiles = config[CONFIG_KEY].fileNames.map((fileName: string) => new File(fileName, fs.readFileSync(fileName, 'utf8'))); diff --git a/packages/typescript/test/integration/configEditor.it.spec.ts b/packages/typescript/test/integration/configEditor.it.spec.ts index 64ccc224d7..6cc5124b34 100644 --- a/packages/typescript/test/integration/configEditor.it.spec.ts +++ b/packages/typescript/test/integration/configEditor.it.spec.ts @@ -14,9 +14,6 @@ describe('Read TS Config file integration', () => { config.tsconfigFile = resolveSampleProject('tsconfig.json'); testInjector.injector.injectClass(TypescriptConfigEditor).edit(config); const actual = config.tsconfig; - expect(actual.fileNames.map(path.normalize)).deep.eq([ - resolveSampleProject('math.ts'), - resolveSampleProject('useMath.ts') - ]); + expect(actual.fileNames.map(path.normalize)).deep.eq([resolveSampleProject('math.ts'), resolveSampleProject('useMath.ts')]); }); }); diff --git a/packages/typescript/test/integration/ownDogFood.it.spec.ts b/packages/typescript/test/integration/ownDogFood.it.spec.ts index 40be551a4a..269e42ea9e 100644 --- a/packages/typescript/test/integration/ownDogFood.it.spec.ts +++ b/packages/typescript/test/integration/ownDogFood.it.spec.ts @@ -9,7 +9,6 @@ import TypescriptConfigEditor from '../../src/TypescriptConfigEditor'; import TypescriptTranspiler from '../../src/TypescriptTranspiler'; describe('@stryker-mutator/typescript', () => { - let config: Config; let inputFiles: File[]; @@ -17,7 +16,7 @@ describe('@stryker-mutator/typescript', () => { const configEditor = testInjector.injector.injectClass(TypescriptConfigEditor); config = new Config(); config.set({ - tsconfigFile: path.resolve(__dirname, '..', '..', 'tsconfig.src.json'), + tsconfigFile: path.resolve(__dirname, '..', '..', 'tsconfig.src.json') }); configEditor.edit(config); inputFiles = config[CONFIG_KEY].fileNames.map((fileName: string) => new File(fileName, fs.readFileSync(fileName, 'utf8'))); @@ -32,7 +31,7 @@ describe('@stryker-mutator/typescript', () => { it('should result in an error if a variable is declared as any and noImplicitAny = true', async () => { const transpiler = new TypescriptTranspiler(config, /*produceSourceMaps: */ true, () => testInjector.logger); inputFiles[0] = new File(inputFiles[0].name, inputFiles[0].textContent + 'function foo(bar) { return bar; } '); - return expect(transpiler.transpile(inputFiles)).rejectedWith('error TS7006: Parameter \'bar\' implicitly has an \'any\' type'); + return expect(transpiler.transpile(inputFiles)).rejectedWith("error TS7006: Parameter 'bar' implicitly has an 'any' type"); }); it('should not result in an error if a variable is declared as any and noImplicitAny = false', async () => { diff --git a/packages/typescript/test/integration/sample.it.spec.ts b/packages/typescript/test/integration/sample.it.spec.ts index d3e619ab0d..87c6452496 100644 --- a/packages/typescript/test/integration/sample.it.spec.ts +++ b/packages/typescript/test/integration/sample.it.spec.ts @@ -11,7 +11,6 @@ import { typescriptMutatorFactory } from '../../src/TypescriptMutator'; import TypescriptTranspiler from '../../src/TypescriptTranspiler'; describe('Sample integration', () => { - let config: Config; let inputFiles: File[]; @@ -19,7 +18,7 @@ describe('Sample integration', () => { const configEditor = testInjector.injector.injectClass(TypescriptConfigEditor); config = new Config(); config.set({ - tsconfigFile: path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'tsconfig.json'), + tsconfigFile: path.resolve(__dirname, '..', '..', 'testResources', 'sampleProject', 'tsconfig.json') }); configEditor.edit(config); inputFiles = config[CONFIG_KEY].fileNames.map((fileName: string) => new File(fileName, fs.readFileSync(fileName, 'utf8'))); @@ -60,14 +59,14 @@ describe('Sample integration', () => { const mathDotTS = inputFiles.filter(file => file.name.endsWith('math.ts'))[0]; const [firstBinaryMutant, stringSubtractMutant] = mutants.filter(m => m.mutatorName === 'BinaryExpression'); const correctResult = await transpiler.transpile([mutateFile(mathDotTS, firstBinaryMutant)]); - await expect(transpiler.transpile([mutateFile(mathDotTS, stringSubtractMutant)])) - .rejectedWith('error TS2362: The left-hand side of an arithmetic operation must be of type \'any\', \'number\', \'bigint\' or an enum type.'); + await expect(transpiler.transpile([mutateFile(mathDotTS, stringSubtractMutant)])).rejectedWith( + "error TS2362: The left-hand side of an arithmetic operation must be of type 'any', 'number', 'bigint' or an enum type." + ); expect(correctResult).lengthOf(1); expect(path.resolve(correctResult[0].name)).eq(path.resolve(path.dirname(mathDotTS.name), 'math.js')); }); function mutateFile(file: File, mutant: Mutant): File { - return new File(file.name, - file.content.slice(0, mutant.range[0]) + mutant.replacement + file.content.slice(mutant.range[1])); + return new File(file.name, `${file.content.slice(0, mutant.range[0])}${mutant.replacement}${file.content.slice(mutant.range[1])}`); } }); diff --git a/packages/typescript/test/integration/useHeaderFile.ts b/packages/typescript/test/integration/useHeaderFile.ts index a0a851032d..4cb1cf1bf3 100644 --- a/packages/typescript/test/integration/useHeaderFile.ts +++ b/packages/typescript/test/integration/useHeaderFile.ts @@ -10,7 +10,6 @@ import TypescriptConfigEditor from '../../src/TypescriptConfigEditor'; import TypescriptTranspiler from '../../src/TypescriptTranspiler'; describe('Use header file integration', () => { - let config: Config; let inputFiles: File[]; let transpiler: TypescriptTranspiler; @@ -19,7 +18,7 @@ describe('Use header file integration', () => { const configEditor = testInjector.injector.injectClass(TypescriptConfigEditor); config = new Config(); config.set({ - tsconfigFile: path.resolve(__dirname, '..', '..', 'testResources', 'useHeaderFile', 'tsconfig.json'), + tsconfigFile: path.resolve(__dirname, '..', '..', 'testResources', 'useHeaderFile', 'tsconfig.json') }); configEditor.edit(config); inputFiles = config[CONFIG_KEY].fileNames.map((fileName: string) => new File(fileName, fs.readFileSync(fileName, 'utf8'))); diff --git a/packages/typescript/test/unit/TypescriptConfigEditor.spec.ts b/packages/typescript/test/unit/TypescriptConfigEditor.spec.ts index 90f01476ae..a4b8af5128 100644 --- a/packages/typescript/test/unit/TypescriptConfigEditor.spec.ts +++ b/packages/typescript/test/unit/TypescriptConfigEditor.spec.ts @@ -11,7 +11,6 @@ import TypescriptConfigEditor from './../../src/TypescriptConfigEditor'; const CONFIG_KEY = 'tsconfigFile'; describe('TypescriptConfigEditor edit', () => { - let readFileSyncStub: SinonStub; let config: Config; let sut: TypescriptConfigEditor; @@ -25,7 +24,7 @@ describe('TypescriptConfigEditor edit', () => { it('should not load any config if "tsconfigFile" is not specified', () => { sut.edit(config); expect(config[CONFIG_KEY]).undefined; - expect(testInjector.logger.debug).calledWith('No \'%s\' specified, not loading any config', CONFIG_KEY); + expect(testInjector.logger.debug).calledWith("No '%s' specified, not loading any config", CONFIG_KEY); }); it('should load the given tsconfig file', () => { @@ -77,16 +76,16 @@ describe('TypescriptConfigEditor edit', () => { }); it('should log errors on failure during load', () => { - readFileSyncStub.returns(`invalid json`); + readFileSyncStub.returns('invalid json'); config[CONFIG_KEY] = 'tsconfig.json'; - expect(() => sut.edit(config)).throws('error TS1005: \'{\' expected.'); + expect(() => sut.edit(config)).throws("error TS1005: '{' expected."); }); it('should log errors on failure during load of extending file', () => { - readFileSyncStub.returns(`{ "extends": "./parent.tsconfig.json" }`); + readFileSyncStub.returns('{ "extends": "./parent.tsconfig.json" }'); config[CONFIG_KEY] = 'tsconfig.json'; - sut.edit(config, parseConfigHost({ readFile: () => `invalid json` })); - expect(testInjector.logger.error).calledWithMatch(match('error TS1005: \'{\' expected.')); + sut.edit(config, parseConfigHost({ readFile: () => 'invalid json' })); + expect(testInjector.logger.error).calledWithMatch(match("error TS1005: '{' expected.")); }); function parseConfigHost(overrides?: Partial): ts.ParseConfigHost { diff --git a/packages/typescript/test/unit/TypescriptMutator.spec.ts b/packages/typescript/test/unit/TypescriptMutator.spec.ts index 4f35125893..4c217c2ec4 100644 --- a/packages/typescript/test/unit/TypescriptMutator.spec.ts +++ b/packages/typescript/test/unit/TypescriptMutator.spec.ts @@ -13,10 +13,7 @@ class FunctionDeclarationMutator extends NodeMutator { return node.kind === ts.SyntaxKind.FunctionDeclaration; } protected identifyReplacements(node: ts.FunctionDeclaration): NodeReplacement[] { - return [ - { node, replacement: '// Function declaration removed' }, - { node, replacement: 'changedToOtherCall()' } - ]; + return [{ node, replacement: '// Function declaration removed' }, { node, replacement: 'changedToOtherCall()' }]; } } @@ -32,10 +29,7 @@ class SourceFileMutator extends NodeMutator { function createSut() { return testInjector.injector - .provideValue(MUTATORS_TOKEN, [ - new SourceFileMutator(), - new FunctionDeclarationMutator() - ]) + .provideValue(MUTATORS_TOKEN, [new SourceFileMutator(), new FunctionDeclarationMutator()]) .injectClass(TypescriptMutator); } @@ -46,10 +40,10 @@ describe('TypescriptMutator', () => { // Arrange const expectedMutatorNames = fs .readdirSync(path.resolve(__dirname, '..', '..', 'src', 'mutator')) - .filter(mutatorFile => path.extname(mutatorFile) === '.ts' - && !mutatorFile.endsWith('.d.ts') - && mutatorFile !== 'NodeMutator.ts' - && mutatorFile !== 'index.ts') + .filter( + mutatorFile => + path.extname(mutatorFile) === '.ts' && !mutatorFile.endsWith('.d.ts') && mutatorFile !== 'NodeMutator.ts' && mutatorFile !== 'index.ts' + ) .map(fileName => fileName.substr(0, fileName.length - 'Mutator.ts'.length)); // Act @@ -61,7 +55,6 @@ describe('TypescriptMutator', () => { }); describe('using 2 mutators', () => { - let file1: File; let file2: File; @@ -72,20 +65,19 @@ describe('TypescriptMutator', () => { `function add(n...: number[]) { return n.sum(); } - const a = add(1, 3, 4, 5);`); + const a = add(1, 3, 4, 5);` + ); file2 = new File( 'file2.ts', `function subtract(n...: numbers[]){ return n[0] - n.slice(1).sum(); } - const b = subtract(10, 3, 4);`); + const b = subtract(10, 3, 4);` + ); }); it('should deliver 6 mutants', () => { - const mutants = sut.mutate([ - file1, - file2 - ]); + const mutants = sut.mutate([file1, file2]); expect(mutants.filter(mutant => mutant.mutatorName === 'SourceFileForTest')).lengthOf(2); expect(mutants.filter(mutant => mutant.mutatorName === 'FunctionDeclarationForTest')).lengthOf(4); }); @@ -97,14 +89,10 @@ describe('TypescriptMutator', () => { it('should not skip a node when it does not have a declare modifier', () => { // Arrange - file1 = new File( - 'file1.ts', - `function mutated(a: number, b: number);`); + file1 = new File('file1.ts', 'function mutated(a: number, b: number);'); // Act - const mutants = sut.mutate([ - file1, - ]); + const mutants = sut.mutate([file1]); // Assert expect(mutants).to.deep.equal([ @@ -131,14 +119,10 @@ describe('TypescriptMutator', () => { it('should skip a node when it has a declare modifier', () => { // Arrange - file1 = new File( - 'file1.ts', - `declare function notMutated(a: number, b: number);`); + file1 = new File('file1.ts', 'declare function notMutated(a: number, b: number);'); // Act - const mutants = sut.mutate([ - file1, - ]); + const mutants = sut.mutate([file1]); // Assert expect(mutants).to.deep.equal([ @@ -146,7 +130,7 @@ describe('TypescriptMutator', () => { fileName: 'file1.ts', mutatorName: 'SourceFileForTest', range: [0, 50], - replacement: '"stryker was here"', + replacement: '"stryker was here"' } ]); }); @@ -158,12 +142,11 @@ describe('TypescriptMutator', () => { `interface Hello { value: string; sortable?: true; - }`); + }` + ); // Act - const mutants = sut.mutate([ - file1, - ]); + const mutants = sut.mutate([file1]); // Assert expect(mutants).to.deep.equal([ @@ -171,11 +154,10 @@ describe('TypescriptMutator', () => { fileName: 'file1.ts', mutatorName: 'SourceFileForTest', range: [0, 85], - replacement: '"stryker was here"', + replacement: '"stryker was here"' } ]); }); }); - }); }); diff --git a/packages/typescript/test/unit/TypescriptTranspiler.spec.ts b/packages/typescript/test/unit/TypescriptTranspiler.spec.ts index 9d833d09e1..14b85cd2c6 100644 --- a/packages/typescript/test/unit/TypescriptTranspiler.spec.ts +++ b/packages/typescript/test/unit/TypescriptTranspiler.spec.ts @@ -10,7 +10,6 @@ import { EmitOutput } from '../../src/transpiler/TranspilingLanguageService'; import TypescriptTranspiler from '../../src/TypescriptTranspiler'; describe(TypescriptTranspiler.name, () => { - let languageService: sinon.SinonStubbedInstance; let sut: TypescriptTranspiler; let transpileFilterMock: sinon.SinonStubbedInstance; @@ -26,30 +25,23 @@ describe(TypescriptTranspiler.name, () => { }); describe('transpile', () => { - beforeEach(() => { languageService.getSemanticDiagnostics.returns([]); // no errors by default - sut = testInjector.injector - .provideValue(commonTokens.produceSourceMaps, true) - .injectClass(TypescriptTranspiler); + sut = testInjector.injector.provideValue(commonTokens.produceSourceMaps, true).injectClass(TypescriptTranspiler); }); it('should transpile given files', async () => { // Arrange - const expected = [ - new File('foo.js', 'foo'), - new File('bar.js', 'bar') - ]; + const expected = [new File('foo.js', 'foo'), new File('bar.js', 'bar')]; arrangeIncludedFiles(); languageService.emit - .withArgs('foo.ts').returns(multiResult(expected[0])) - .withArgs('bar.ts').returns(multiResult(expected[1])); + .withArgs('foo.ts') + .returns(multiResult(expected[0])) + .withArgs('bar.ts') + .returns(multiResult(expected[1])); // Act - const outputFiles = await sut.transpile([ - new File('foo.ts', ''), - new File('bar.ts', '') - ]); + const outputFiles = await sut.transpile([new File('foo.ts', ''), new File('bar.ts', '')]); // Assert expectFilesEqual(outputFiles, expected); @@ -57,33 +49,24 @@ describe(TypescriptTranspiler.name, () => { it('should keep file order', async () => { // Arrange - const input = [ - new File('file1.js', ''), - new File('file2.ts', ''), - new File('file4.ts', ''), - ]; + const input = [new File('file1.js', ''), new File('file2.ts', ''), new File('file4.ts', '')]; arrangeIncludedFiles(input.slice(1)); languageService.emit - .withArgs('file2.ts').returns(multiResult(new File('file2.js', 'file2'))) - .withArgs('file4.ts').returns(multiResult(new File('file4.js', 'file4'))); + .withArgs('file2.ts') + .returns(multiResult(new File('file2.js', 'file2'))) + .withArgs('file4.ts') + .returns(multiResult(new File('file4.js', 'file4'))); // Act const outputFiles = await sut.transpile(input); // Assert - expectFilesEqual(outputFiles, [ - input[0], - new File('file2.js', 'file2'), - new File('file4.js', 'file4') - ]); + expectFilesEqual(outputFiles, [input[0], new File('file2.js', 'file2'), new File('file4.js', 'file4')]); }); it('should not transpile header files', async () => { // Arrange - const input = [ - new File('file1.ts', ''), - new File('file2.d.ts', ''), - ]; + const input = [new File('file1.ts', ''), new File('file2.d.ts', '')]; arrangeIncludedFiles(); languageService.emit.returns(multiResult(new File('file1.js', ''))); @@ -120,11 +103,11 @@ describe(TypescriptTranspiler.name, () => { }); }); - function expectFilesEqual(actual: ReadonlyArray, expected: ReadonlyArray) { + function expectFilesEqual(actual: readonly File[], expected: readonly File[]) { expect(serialize(orderByName(actual))).eq(serialize(orderByName(expected))); } - function orderByName(files: ReadonlyArray) { + function orderByName(files: readonly File[]) { files.slice().sort((a, b) => a.name.localeCompare(b.name)); } diff --git a/packages/typescript/test/unit/helpers/tsHelpers.spec.ts b/packages/typescript/test/unit/helpers/tsHelpers.spec.ts index cfe0981780..121f602477 100644 --- a/packages/typescript/test/unit/helpers/tsHelpers.spec.ts +++ b/packages/typescript/test/unit/helpers/tsHelpers.spec.ts @@ -13,10 +13,11 @@ describe('tsHelpers', () => { }); describe('guardTypescriptVersion', () => { - it('should throw an error when installed typescript version does not satisfy >=2.4', () => { satisfiesStub.returns(false); - expect(() => tsHelpers.guardTypescriptVersion()).throws(`Installed typescript version ${ts.version} is not supported by stryker-typescript. Please install version 2.5 or higher (\`npm install typescript@^2.5\`).`); + expect(() => tsHelpers.guardTypescriptVersion()).throws( + `Installed typescript version ${ts.version} is not supported by stryker-typescript. Please install version 2.5 or higher (\`npm install typescript@^2.5\`).` + ); expect(satisfiesStub).calledWith(ts.version, '>=2.4'); }); diff --git a/packages/typescript/test/unit/mutator/StringLiteralMutator.spec.ts b/packages/typescript/test/unit/mutator/StringLiteralMutator.spec.ts index 3210e75054..0abbeeb076 100644 --- a/packages/typescript/test/unit/mutator/StringLiteralMutator.spec.ts +++ b/packages/typescript/test/unit/mutator/StringLiteralMutator.spec.ts @@ -6,7 +6,6 @@ import { expectMutation, verifySpecification } from './mutatorAssertions'; verifySpecification(StringLiteralMutatorSpec, StringLiteralMutator); describe('StringLiteralMutator - Extras', () => { - let mutator: NodeMutator; before(() => { mutator = new StringLiteralMutator(); diff --git a/packages/typescript/test/unit/mutator/mutatorAssertions.ts b/packages/typescript/test/unit/mutator/mutatorAssertions.ts index 5e25a28b17..82dd630195 100644 --- a/packages/typescript/test/unit/mutator/mutatorAssertions.ts +++ b/packages/typescript/test/unit/mutator/mutatorAssertions.ts @@ -6,7 +6,7 @@ import * as ts from 'typescript'; import { parseFile } from '../../../src/helpers/tsHelpers'; import NodeMutator from '../../../src/mutator/NodeMutator'; -export type MutatorConstructor = new() => NodeMutator; +export type MutatorConstructor = new () => NodeMutator; export function verifySpecification(specification: (name: string, expectMutation: ExpectMutation) => void, MutatorClass: MutatorConstructor): void { specification(new MutatorClass().name, (actual: string, ...expected: string[]) => expectMutation(new MutatorClass(), actual, ...expected)); @@ -17,9 +17,7 @@ export function expectMutation(mutator: NodeMutator, sourceText: string, ...expe const sourceFile = parseFile(tsFile, undefined); const mutants = mutate(mutator, sourceFile, sourceFile); expect(mutants).lengthOf(expectedTexts.length); - const actualMutantTexts = mutants - .map(mutant => mutantToString(mutant, sourceText)) - .map(format); + const actualMutantTexts = mutants.map(mutant => mutantToString(mutant, sourceText)).map(format); expectedTexts.forEach(expected => { expect(actualMutantTexts, `was: ${actualMutantTexts.join(',')}`).to.include(format(expected)); }); @@ -42,7 +40,5 @@ function mutate(mutator: NodeMutator, node: ts.Node, sourceFile: ts.SourceFile): } function mutantToString(mutant: Mutant, sourceText: string) { - return sourceText.substr(0, mutant.range[0]) + - mutant.replacement + - sourceText.substr(mutant.range[1]); + return sourceText.substr(0, mutant.range[0]) + mutant.replacement + sourceText.substr(mutant.range[1]); } diff --git a/packages/typescript/test/unit/transpile/TranspileFilter.spec.ts b/packages/typescript/test/unit/transpile/TranspileFilter.spec.ts index 4ca818289b..38cb17bf27 100644 --- a/packages/typescript/test/unit/transpile/TranspileFilter.spec.ts +++ b/packages/typescript/test/unit/transpile/TranspileFilter.spec.ts @@ -4,7 +4,6 @@ import * as path from 'path'; import TranspileFilter, { DefaultFilter, TSConfigFilter } from '../../../src/transpiler/TranspileFilter'; describe('TranspileFilter', () => { - describe('create', () => { it('should result in the default filter tsconfig is undefined', () => { const config = new Config(); @@ -17,7 +16,6 @@ describe('TranspileFilter', () => { expect(TranspileFilter.create(config)).instanceof(TSConfigFilter); }); }); - }); describe('DefaultFilter', () => { diff --git a/packages/util/.eslintrc b/packages/util/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/util/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/util/src/StrykerError.ts b/packages/util/src/StrykerError.ts index e825e2f3b0..f30e2501e4 100644 --- a/packages/util/src/StrykerError.ts +++ b/packages/util/src/StrykerError.ts @@ -1,7 +1,7 @@ import { errorToString } from './errors'; export default class StrykerError extends Error { - constructor(message: string, readonly innerError?: Error) { + constructor(message: string, public readonly innerError?: Error) { super(`${message}${innerError ? `. Inner error: ${errorToString(innerError)}` : ''}`); Error.captureStackTrace(this, StrykerError); // TS recommendation: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work diff --git a/packages/util/src/errors.ts b/packages/util/src/errors.ts index 74e55a17e2..3c94b60d99 100644 --- a/packages/util/src/errors.ts +++ b/packages/util/src/errors.ts @@ -1,4 +1,3 @@ - export function isErrnoException(error: Error): error is NodeJS.ErrnoException { return typeof (error as NodeJS.ErrnoException).code === 'string'; } diff --git a/packages/util/src/fsAsPromised.ts b/packages/util/src/fsAsPromised.ts index 9d6c55b0fc..5cad268a96 100644 --- a/packages/util/src/fsAsPromised.ts +++ b/packages/util/src/fsAsPromised.ts @@ -16,5 +16,5 @@ export default { readFile: promisify(fs.readFile), stat: promisify(fs.stat), symlink: promisify(fs.symlink), - writeFile: promisify(fs.writeFile), + writeFile: promisify(fs.writeFile) }; diff --git a/packages/util/test/unit/childProcessAsPromised.spec.ts b/packages/util/test/unit/childProcessAsPromised.spec.ts index 5b79ddbc76..78f1e0c99c 100644 --- a/packages/util/test/unit/childProcessAsPromised.spec.ts +++ b/packages/util/test/unit/childProcessAsPromised.spec.ts @@ -4,7 +4,7 @@ import { promisify } from 'util'; import { childProcessAsPromised } from '../../src'; describe('childProcessAsPromised', () => { - it(`should expose promisified exec`, () => { + it('should expose promisified exec', () => { // It's difficult to test this any other way. At least this way, we know it is promisified. expect(childProcessAsPromised.exec.toString()).eq(promisify(childProcess.exec).toString()); }); diff --git a/packages/util/test/unit/fsAsPromised.spec.ts b/packages/util/test/unit/fsAsPromised.spec.ts index 87566e1a47..5cd496b4f3 100644 --- a/packages/util/test/unit/fsAsPromised.spec.ts +++ b/packages/util/test/unit/fsAsPromised.spec.ts @@ -4,7 +4,6 @@ import { promisify } from 'util'; import { fsAsPromised } from '../../src'; describe('fsAsPromised', () => { - describePromisifiedFunction('exists'); describePromisifiedFunction('lstat'); describePromisifiedFunction('symlink'); diff --git a/packages/vue-mutator/.eslintrc b/packages/vue-mutator/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/vue-mutator/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/vue-mutator/src/VueMutator.ts b/packages/vue-mutator/src/VueMutator.ts index 28778aaaa8..93102c1d7f 100644 --- a/packages/vue-mutator/src/VueMutator.ts +++ b/packages/vue-mutator/src/VueMutator.ts @@ -8,8 +8,7 @@ export default class VueMutator implements Mutator { private readonly javascriptMutatorName = 'javascript'; private readonly typescriptMutatorName = 'typescript'; - constructor(private readonly mutators: { [name: string]: Mutator; }) { - } + constructor(private readonly mutators: { [name: string]: Mutator }) {} public static inject = tokens(MUTATORS_TOKEN); public mutate(inputFiles: File[]): Mutant[] { @@ -18,12 +17,10 @@ export default class VueMutator implements Mutator { inputFiles.forEach(file => { if (file.name.endsWith('.vue')) { const script = compiler.parseComponent(file.textContent).script; - if (script) { // Vue file must have tag to be mutated + if (script) { + // Vue file must have tag to be mutated const { mutator, extension } = this.getVueScriptMutatorAndExtension(script); - const vueFile = new File( - file.name + extension, - file.textContent.substring(script.start, script.end) - ); + const vueFile = new File(file.name + extension, file.textContent.substring(script.start, script.end)); const vueMutants = mutator.mutate([vueFile]); vueMutants.forEach(mutant => { mutant.fileName = file.name; @@ -41,7 +38,7 @@ export default class VueMutator implements Mutator { return mutants; } - private getVueScriptMutatorAndExtension(script: any): { mutator: Mutator, extension: string } { + private getVueScriptMutatorAndExtension(script: any): { mutator: Mutator; extension: string } { const lang: string | undefined = script.attrs.lang; let mutatorName: string; let extension: string; @@ -63,7 +60,9 @@ export default class VueMutator implements Mutator { const mutator = this.mutators[mutatorName]; if (mutator === undefined) { - throw new Error(`The '${mutatorName}' mutator is required to mutate a `); + ` + ); const files = [vueFile]; const sut = createSut(); - expect(() => sut.mutate(files)).throws(`The 'javascript' mutator is required to mutate a `); + ` + ); const jsFile = new File('index.js', 'var name = "MyApp";'); const files = [jsFile, vueFile]; const sut = createSut(); - expect(() => sut.mutate(files)).throws(`Unable to mutate file "${jsFile.name}" because neither the typescript or the javascript mutator was installed. Please read the README of this package for information on configuration.`); + expect(() => sut.mutate(files)).throws( + `Unable to mutate file "${jsFile.name}" because neither the typescript or the javascript mutator was installed. Please read the README of this package for information on configuration.` + ); }); it('should pass Vue script blocks to the JavaScript mutator', () => { @@ -81,11 +87,13 @@ describe('VueMutator', () => { } } }`; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` - `); + ` + ); const files = [vueFile]; const sut = createSut(); @@ -103,11 +111,13 @@ describe('VueMutator', () => { } } }`; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` - `); + ` + ); const files = [vueFile]; const sut = createSut(); @@ -125,11 +135,13 @@ describe('VueMutator', () => { } } }`; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` - `); + ` + ); const files = [vueFile]; const sut = createSut(); @@ -165,7 +177,8 @@ describe('VueMutator', () => { describe('with TypeScript', () => { it('should throw an error if no TypeScript mutator is installed', () => { mutators = {}; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` @@ -177,17 +190,21 @@ describe('VueMutator', () => { } } } - `); + ` + ); const files = [vueFile]; const sut = createSut(); - expect(() => sut.mutate(files)).throws(`The 'typescript' mutator is required to mutate a `); + ` + ); const jsFile = new File('index.js', 'var name = "MyApp";'); const files = [jsFile, vueFile]; const sut = createSut(); - expect(() => sut.mutate(files)).throws(`Unable to mutate file "${jsFile.name}" because neither the typescript or the javascript mutator was installed. Please read the README of this package for information on configuration.`); + expect(() => sut.mutate(files)).throws( + `Unable to mutate file "${jsFile.name}" because neither the typescript or the javascript mutator was installed. Please read the README of this package for information on configuration.` + ); }); it('should pass Vue script blocks with lang="ts" to the TypeScript mutator', () => { @@ -217,11 +237,13 @@ describe('VueMutator', () => { } } }`; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` - `); + ` + ); const files = [vueFile]; const sut = createSut(); @@ -239,11 +261,13 @@ describe('VueMutator', () => { } } }`; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` - `); + ` + ); const files = [vueFile]; const sut = createSut(); @@ -265,31 +289,35 @@ describe('VueMutator', () => { }); it('should throw an error when a Vue script block has an unknown lang attribute', () => { - mutators = {}; - const script = `export default { + mutators = {}; + const script = `export default { data () { return { message: 'hello!' } } }`; - const vueFile = new File('Component.vue', + const vueFile = new File( + 'Component.vue', ` - `); - const files = [vueFile]; - const sut = createSut(); + ` + ); + const files = [vueFile]; + const sut = createSut(); - expect(() => sut.mutate(files)).throws(`Found unsupported language attribute 'lang="coffeescript"' on a `); + ` + ); const files = [vueFile]; const jsMutant: Mutant = { fileName: `${vueFile.name}.js`, @@ -331,7 +361,10 @@ describe('VueMutator', () => { expect(mutants.length).to.equal(1); expect(generatedMutant.mutatorName).to.equal(jsMutant.mutatorName); expect(generatedMutant.fileName).to.equal(vueFile.name); - expect(generatedMutant.range).to.deep.equal([vueFile.textContent.indexOf(codeToMutate), vueFile.textContent.indexOf(codeToMutate) + codeToMutate.length]); + expect(generatedMutant.range).to.deep.equal([ + vueFile.textContent.indexOf(codeToMutate), + vueFile.textContent.indexOf(codeToMutate) + codeToMutate.length + ]); expect(generatedMutant.replacement).to.equal(jsMutant.replacement); }); }); diff --git a/packages/wct-runner/.eslintrc b/packages/wct-runner/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/wct-runner/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/wct-runner/src/WctLogger.ts b/packages/wct-runner/src/WctLogger.ts index b918d83ea0..ba8781c033 100644 --- a/packages/wct-runner/src/WctLogger.ts +++ b/packages/wct-runner/src/WctLogger.ts @@ -2,7 +2,6 @@ import { Logger } from '@stryker-mutator/api/logging'; import { EventEmitter } from 'events'; export default class WctLogger { - private readonly logProxy = { ['log:debug']: this.log.debug.bind(this.log), ['log:error']: this.log.error.bind(this.log), diff --git a/packages/wct-runner/src/WctReporter.ts b/packages/wct-runner/src/WctReporter.ts index 83c86ca3c9..875014d709 100644 --- a/packages/wct-runner/src/WctReporter.ts +++ b/packages/wct-runner/src/WctReporter.ts @@ -23,21 +23,23 @@ export default class WctReporter { // Both testStart and testEnd are properties here, rather than methods. This is deliberate to allow for `this` pointer to work private readonly testStart = () => { this.before = new Date(); - } + }; private readonly testEnd = (_browser: BrowserDef, result: TestEndData) => { this.results.push({ failureMessages: this.toFailureMessages(result.error), name: this.testNamePartsToString(result.test), status: this.toTestResultStatus(result.state), - timeSpentMs: new Date().getTime() - this.before.getTime(), + timeSpentMs: new Date().getTime() - this.before.getTime() }); - } + }; private toFailureMessages(error: any): string[] | undefined { switch (typeof error) { - case 'undefined': return undefined; - case 'string': return [error]; + case 'undefined': + return undefined; + case 'string': + return [error]; case 'object': if (error) { if (error.stack) { @@ -55,7 +57,10 @@ export default class WctReporter { private testNamePartsToString(testNameParts: string[]): string { // First part is the file name - return testNameParts.splice(1).join(' ').trim(); + return testNameParts + .splice(1) + .join(' ') + .trim(); } private toTestResultStatus(state: CompletedState): TestStatus { diff --git a/packages/wct-runner/src/WctTestRunner.ts b/packages/wct-runner/src/WctTestRunner.ts index 3834785acd..50b5985852 100644 --- a/packages/wct-runner/src/WctTestRunner.ts +++ b/packages/wct-runner/src/WctTestRunner.ts @@ -12,7 +12,6 @@ const FORCED_WCT_OPTIONS = Object.freeze({ }); export default class WctTestRunner implements TestRunner { - private readonly reporter: WctReporter; private readonly context: Context; private readonly logger: WctLogger; @@ -20,7 +19,9 @@ export default class WctTestRunner implements TestRunner { public static inject = tokens(commonTokens.logger, commonTokens.options); constructor(private readonly log: Logger, options: StrykerOptions) { if (options.coverageAnalysis !== 'off') { - throw new Error(`Coverage analysis "${options.coverageAnalysis}" is not (yet) supported by the WCT test runner plugin. Please set \`coverageAnalysis: "off"\` in your stryker.conf.js file.`); + throw new Error( + `Coverage analysis "${options.coverageAnalysis}" is not (yet) supported by the WCT test runner plugin. Please set \`coverageAnalysis: "off"\` in your stryker.conf.js file.` + ); } this.log.debug('Running wct version %s from %s', require(`${WCT_PACKAGE}/package.json`).version, require.resolve(WCT_PACKAGE)); this.context = this.loadContext(options); @@ -31,7 +32,7 @@ export default class WctTestRunner implements TestRunner { private loadContext(options: StrykerOptions) { const context = new Context(Object.assign({}, options.wct, FORCED_WCT_OPTIONS)); if (this.log.isDebugEnabled()) { - this.log.debug(`WCT options: %s`, JSON.stringify(this.context.options)); + this.log.debug('WCT options: %s', JSON.stringify(this.context.options)); } return context; } @@ -66,7 +67,7 @@ export default class WctTestRunner implements TestRunner { } private static ignoreFailedTests(error: Error) { - if (!error.message.match(/\d+ failed tests?/)) { + if (!/\d+ failed tests?/.exec(error.message)) { throw error; } } diff --git a/packages/wct-runner/src/index.ts b/packages/wct-runner/src/index.ts index 13f055f541..28d0358894 100644 --- a/packages/wct-runner/src/index.ts +++ b/packages/wct-runner/src/index.ts @@ -1,6 +1,4 @@ import { declareClassPlugin, PluginKind } from '@stryker-mutator/api/plugin'; import WctTestRunner from './WctTestRunner'; -export const strykerPlugins = [ - declareClassPlugin(PluginKind.TestRunner, 'wct', WctTestRunner) -]; +export const strykerPlugins = [declareClassPlugin(PluginKind.TestRunner, 'wct', WctTestRunner)]; diff --git a/packages/wct-runner/test/helpers/consoleLoggerFactory.ts b/packages/wct-runner/test/helpers/consoleLoggerFactory.ts index fc984dc459..f8fcab6689 100644 --- a/packages/wct-runner/test/helpers/consoleLoggerFactory.ts +++ b/packages/wct-runner/test/helpers/consoleLoggerFactory.ts @@ -11,6 +11,6 @@ export default function consoleLoggerFactory(category: string) { isTraceEnabled: () => true, isWarnEnabled: () => true, trace: console.log.bind(console, category, 'TRACE'), - warn: console.log.bind(console, category, 'WARNING'), + warn: console.log.bind(console, category, 'WARNING') }; } diff --git a/packages/wct-runner/test/integration/WctTestRunner.it.spec.ts b/packages/wct-runner/test/integration/WctTestRunner.it.spec.ts index b22e49bb3a..4adf00a11b 100644 --- a/packages/wct-runner/test/integration/WctTestRunner.it.spec.ts +++ b/packages/wct-runner/test/integration/WctTestRunner.it.spec.ts @@ -12,7 +12,6 @@ type TimelessRunResult = { type TimelessTestResult = Pick>; describe('WctTestRunner integration', () => { - // The "root" wct configuration option is always loaded from the current directory. // In order to test it properly, we need to grab it before- and reset it after each test. let cwd: string; @@ -27,7 +26,13 @@ describe('WctTestRunner integration', () => { tests: [ { name: ' is awesome', status: TestStatus.Success, failureMessages: undefined }, { name: ' is failing', status: TestStatus.Failed, failureMessages: ['expected true to be false'] }, - { name: ' is throwing', status: TestStatus.Failed, failureMessages: ['This element is failing HTMLElement.throw at /components/@stryker-mutator/wct-runner/testResources/htmlTestSuite/src/failing-element.js:11'] } + { + name: ' is throwing', + status: TestStatus.Failed, + failureMessages: [ + 'This element is failing HTMLElement.throw at /components/@stryker-mutator/wct-runner/testResources/htmlTestSuite/src/failing-element.js:11' + ] + } ] }; // To enable console logging: LoggerFactory.setLogImplementation(consoleLoggerFactory); @@ -104,7 +109,13 @@ describe('WctTestRunner integration', () => { const sut = createSut(); const expectedResult: TimelessRunResult = { status: RunStatus.Complete, // We want to actually expect an error here, but wct doesn't let is. - tests: [{ name: '', status: TestStatus.Failed, failureMessages: ['Random error at /components/@stryker-mutator/wct-runner/testResources/garbage/test/gargbage-tests.js:1'] }] + tests: [ + { + name: '', + status: TestStatus.Failed, + failureMessages: ['Random error at /components/@stryker-mutator/wct-runner/testResources/garbage/test/gargbage-tests.js:1'] + } + ] }; // Act diff --git a/packages/wct-runner/test/unit/WctLogger.spec.ts b/packages/wct-runner/test/unit/WctLogger.spec.ts index f5717430a7..47417aa255 100644 --- a/packages/wct-runner/test/unit/WctLogger.spec.ts +++ b/packages/wct-runner/test/unit/WctLogger.spec.ts @@ -22,7 +22,9 @@ describe(WctLogger.name, () => { it('should not forward logging when verbose = false', () => { sut = new WctLogger(context, false, testInjector.logger); emitAllLogEvents(); - expect(testInjector.logger.debug).calledWith('Keeping wct quiet. To enable wct logging, set `wct.verbose` to `true` in your Stryker configuration file.'); + expect(testInjector.logger.debug).calledWith( + 'Keeping wct quiet. To enable wct logging, set `wct.verbose` to `true` in your Stryker configuration file.' + ); expect(testInjector.logger.info).not.called; expect(testInjector.logger.warn).not.called; expect(testInjector.logger.error).not.called; diff --git a/packages/wct-runner/test/unit/WctReporter.spec.ts b/packages/wct-runner/test/unit/WctReporter.spec.ts index b38eca9c49..fc2fb69bf4 100644 --- a/packages/wct-runner/test/unit/WctReporter.spec.ts +++ b/packages/wct-runner/test/unit/WctReporter.spec.ts @@ -6,7 +6,6 @@ import { TestEndData } from 'web-component-tester/runner/clireporter'; import WctReporter from '../../src/WctReporter'; describe(WctReporter.name, () => { - let context: EventEmitter; let sut: WctReporter; @@ -29,66 +28,70 @@ describe(WctReporter.name, () => { }); it('should report a passing test', () => { - actAssertReportTest({ - duration: 2, - error: null, - state: 'passing', - test: ['fooSpec.js', 'Foo', 'bar()', 'should baz'] - }, 10, { name: 'Foo bar() should baz', status: TestStatus.Success, timeSpentMs: 10, failureMessages: undefined }); + actAssertReportTest( + { + duration: 2, + error: null, + state: 'passing', + test: ['fooSpec.js', 'Foo', 'bar()', 'should baz'] + }, + 10, + { name: 'Foo bar() should baz', status: TestStatus.Success, timeSpentMs: 10, failureMessages: undefined } + ); }); it('should report a failing test with error as string', () => { actAssertReportTest({ duration: 2, error: 'fooError', state: 'failing', test: ['fooSpec.js', 'true should be false'] }, 40, { - failureMessages: ['fooError'], - name: 'true should be false', - status: TestStatus.Failed, - timeSpentMs: 40, - }); + failureMessages: ['fooError'], + name: 'true should be false', + status: TestStatus.Failed, + timeSpentMs: 40 + }); }); it('should report a failing test with error as serialized error', () => { actAssertReportTest({ duration: 2, error: { stack: 'fooError' }, state: 'failing', test: ['fooSpec.js', 'true should be false'] }, 40, { - failureMessages: ['fooError'], - name: 'true should be false', - status: TestStatus.Failed, - timeSpentMs: 40, - }); + failureMessages: ['fooError'], + name: 'true should be false', + status: TestStatus.Failed, + timeSpentMs: 40 + }); }); it('should report a failing test with error as object', () => { actAssertReportTest({ duration: 2, error: { a: 'fooError' }, state: 'failing', test: ['fooSpec.js', 'true should be false'] }, 40, { - failureMessages: ['{"a":"fooError"}'], - name: 'true should be false', - status: TestStatus.Failed, - timeSpentMs: 40, - }); + failureMessages: ['{"a":"fooError"}'], + name: 'true should be false', + status: TestStatus.Failed, + timeSpentMs: 40 + }); }); it('should report a failing test with error as number', () => { actAssertReportTest({ duration: 2, error: 42, state: 'failing', test: ['fooSpec.js', 'true should be false'] }, 40, { - failureMessages: ['42'], - name: 'true should be false', - status: TestStatus.Failed, - timeSpentMs: 40, - }); + failureMessages: ['42'], + name: 'true should be false', + status: TestStatus.Failed, + timeSpentMs: 40 + }); }); it('should report a failing test with error as string', () => { actAssertReportTest({ duration: 2, error: 'fooError', state: 'failing', test: ['fooSpec.js', 'true should be false'] }, 40, { - failureMessages: ['fooError'], - name: 'true should be false', - status: TestStatus.Failed, - timeSpentMs: 40, - }); + failureMessages: ['fooError'], + name: 'true should be false', + status: TestStatus.Failed, + timeSpentMs: 40 + }); }); it('should report a skipped test', () => { actAssertReportTest({ duration: 2, error: undefined, state: 'pending', test: ['fooSpec.js', 'true should be false'] }, 40, { - failureMessages: undefined, - name: 'true should be false', - status: TestStatus.Skipped, - timeSpentMs: 40, - }); + failureMessages: undefined, + name: 'true should be false', + status: TestStatus.Skipped, + timeSpentMs: 40 + }); }); function actAssertReportTest(actualTestData: TestEndData, actualTimeSpent: number, expectedTestResult: TestResult) { @@ -109,5 +112,4 @@ describe(WctReporter.name, () => { function raiseTestStarted() { context.emit('test-start'); } - }); diff --git a/packages/wct-runner/test/unit/WctTestRunner.spec.ts b/packages/wct-runner/test/unit/WctTestRunner.spec.ts index 9b29d3cf1e..db27e0612f 100644 --- a/packages/wct-runner/test/unit/WctTestRunner.spec.ts +++ b/packages/wct-runner/test/unit/WctTestRunner.spec.ts @@ -10,7 +10,6 @@ import WctReporter, * as wctReporterModule from '../../src/WctReporter'; import WctTestRunner from '../../src/WctTestRunner'; describe(WctTestRunner.name, () => { - let contextMock: sinon.SinonStubbedInstance; let wctLoggerMock: sinon.SinonStubbedInstance; let wctReporterMock: sinon.SinonStubbedInstance; @@ -64,7 +63,8 @@ describe(WctTestRunner.name, () => { it('should throw when coverageAnalysis != "off"', () => { testInjector.options.coverageAnalysis = 'all'; - const expectedError = 'Coverage analysis "all" is not (yet) supported by the WCT test runner plugin. Please set `coverageAnalysis: "off"` in your stryker.conf.js file.'; + const expectedError = + 'Coverage analysis "all" is not (yet) supported by the WCT test runner plugin. Please set `coverageAnalysis: "off"` in your stryker.conf.js file.'; expect(() => createSut()).throws(expectedError); }); @@ -92,7 +92,6 @@ describe(WctTestRunner.name, () => { }); describe('run', () => { - it('should run clear tests', async () => { stepsMock.runTests.resolves(); const sut = createSut(); @@ -106,7 +105,7 @@ describe(WctTestRunner.name, () => { stepsMock.runTests.resolves(); const sut = createSut(); const runPromise = sut.run(); - const expectedTests = wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Success, timeSpentMs: 4 }]; + const expectedTests = (wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Success, timeSpentMs: 4 }]); const actual = await runPromise; const expectedRunResult: RunResult = { status: RunStatus.Complete, tests: expectedTests }; expect(actual).deep.eq(expectedRunResult); @@ -116,7 +115,7 @@ describe(WctTestRunner.name, () => { stepsMock.runTests.rejects(new Error('23 failed tests')); const sut = createSut(); const runPromise = sut.run(); - const expectedTests = wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Failed, timeSpentMs: 4 }]; + const expectedTests = (wctReporterMock.results = [{ name: 'foobar', status: TestStatus.Failed, timeSpentMs: 4 }]); const actual = await runPromise; const expectedRunResult: RunResult = { status: RunStatus.Complete, tests: expectedTests }; expect(actual).deep.eq(expectedRunResult); diff --git a/packages/webpack-transpiler/.eslintrc b/packages/webpack-transpiler/.eslintrc new file mode 100644 index 0000000000..7e107338ab --- /dev/null +++ b/packages/webpack-transpiler/.eslintrc @@ -0,0 +1,3 @@ +{ + "extends": "../../.eslintrc.js" +} \ No newline at end of file diff --git a/packages/webpack-transpiler/src/WebpackTranspiler.ts b/packages/webpack-transpiler/src/WebpackTranspiler.ts index 809b0289a7..dea8ac9e10 100644 --- a/packages/webpack-transpiler/src/WebpackTranspiler.ts +++ b/packages/webpack-transpiler/src/WebpackTranspiler.ts @@ -12,14 +12,16 @@ export default class WebpackTranspiler implements Transpiler { private webpackCompiler: WebpackCompiler; public static inject = tokens(commonTokens.options, commonTokens.produceSourceMaps, pluginTokens.configLoader); - public constructor(options: StrykerOptions, produceSourceMaps: boolean, private readonly configLoader: ConfigLoader) { + constructor(options: StrykerOptions, produceSourceMaps: boolean, private readonly configLoader: ConfigLoader) { if (produceSourceMaps) { - throw new Error(`Invalid \`coverageAnalysis\` "${options.coverageAnalysis}" is not supported by the stryker-webpack-transpiler (yet). It is not able to produce source maps yet. Please set it "coverageAnalysis" to "off".`); + throw new Error( + `Invalid \`coverageAnalysis\` "${options.coverageAnalysis}" is not supported by the stryker-webpack-transpiler (yet). It is not able to produce source maps yet. Please set it "coverageAnalysis" to "off".` + ); } this.config = this.getStrykerWebpackConfig(options.webpack); } - public async transpile(files: ReadonlyArray): Promise> { + public async transpile(files: readonly File[]): Promise { if (!this.webpackCompiler) { // Initialize the webpack compiler with the current directory (process.cwd) const config = await this.configLoader.load(this.config); diff --git a/packages/webpack-transpiler/src/compiler/ConfigLoader.ts b/packages/webpack-transpiler/src/compiler/ConfigLoader.ts index 05b22875fc..822249ab6b 100644 --- a/packages/webpack-transpiler/src/compiler/ConfigLoader.ts +++ b/packages/webpack-transpiler/src/compiler/ConfigLoader.ts @@ -11,8 +11,7 @@ const PROGRESS_PLUGIN_NAME = 'ProgressPlugin'; export default class ConfigLoader { public static inject = tokens(commonTokens.logger, pluginTokens.require); - public constructor(private readonly log: Logger, private readonly requireFn: NodeRequireFunction) { - } + constructor(private readonly log: Logger, private readonly requireFn: NodeRequireFunction) {} public async load(config: StrykerWebpackConfig): Promise { let webpackConfig: Configuration; @@ -47,7 +46,10 @@ export default class ConfigLoader { if (webpackConfig.plugins) { webpackConfig.plugins = webpackConfig.plugins.filter(plugin => { if (plugin.constructor && plugin.constructor.name === PROGRESS_PLUGIN_NAME) { - this.log.debug('Removing webpack plugin "%s" to keep webpack bundling silent. Set `webpack: { silent: false }` in your stryker.conf.js file to disable this feature.', PROGRESS_PLUGIN_NAME); + this.log.debug( + 'Removing webpack plugin "%s" to keep webpack bundling silent. Set `webpack: { silent: false }` in your stryker.conf.js file to disable this feature.', + PROGRESS_PLUGIN_NAME + ); return false; } else { return true; diff --git a/packages/webpack-transpiler/src/compiler/WebpackCompiler.ts b/packages/webpack-transpiler/src/compiler/WebpackCompiler.ts index 887b575df2..af74bdef5b 100644 --- a/packages/webpack-transpiler/src/compiler/WebpackCompiler.ts +++ b/packages/webpack-transpiler/src/compiler/WebpackCompiler.ts @@ -7,9 +7,7 @@ import webpack from './Webpack'; export default class WebpackCompiler { private readonly _compiler: Compiler; - public constructor(webpackConfig: Configuration, - private readonly _inputFS = new InputFileSystem(), - private readonly _outputFS = new OutputFileSystem()) { + constructor(webpackConfig: Configuration, private readonly _inputFS = new InputFileSystem(), private readonly _outputFS = new OutputFileSystem()) { this._compiler = this.createCompiler(webpackConfig); } @@ -21,10 +19,10 @@ export default class WebpackCompiler { (compiler as any).resolvers.normal.fileSystem = this._inputFS; (compiler as any).resolvers.context.fileSystem = this._inputFS; - return compiler as Compiler; + return compiler; } - public writeFilesToFs(files: ReadonlyArray): void { + public writeFilesToFs(files: readonly File[]): void { files.forEach(file => this.writeToFs(file)); } diff --git a/packages/webpack-transpiler/src/fs/InputFileSystem.ts b/packages/webpack-transpiler/src/fs/InputFileSystem.ts index 662ca64333..b3aa4de3d7 100644 --- a/packages/webpack-transpiler/src/fs/InputFileSystem.ts +++ b/packages/webpack-transpiler/src/fs/InputFileSystem.ts @@ -1,8 +1,4 @@ -import { - CachedInputFileSystem, - NodeJsInputFileSystem, - Stats -} from 'enhanced-resolve'; +import { CachedInputFileSystem, NodeJsInputFileSystem, Stats } from 'enhanced-resolve'; import * as fs from 'fs'; import { dirname } from 'path'; import { Callback, webpack } from '../types'; @@ -12,8 +8,7 @@ import MemoryFS from './MemoryFS'; // => https://github.com/webpack/webpack/blob/efc576c8b744e7a015ab26f1f46932ba3ca7d4f1/lib/node/NodeEnvironmentPlugin.js#L14 const CACHE_DURATION = 60000; -export default class InputFileSystem extends CachedInputFileSystem - implements webpack.InputFileSystem { +export default class InputFileSystem extends CachedInputFileSystem implements webpack.InputFileSystem { private readonly memoryFS = new MemoryFS(); constructor(innerFS = new NodeJsInputFileSystem()) { diff --git a/packages/webpack-transpiler/src/fs/OutputFileSystem.ts b/packages/webpack-transpiler/src/fs/OutputFileSystem.ts index 5a72d23ea0..0b171c8c70 100644 --- a/packages/webpack-transpiler/src/fs/OutputFileSystem.ts +++ b/packages/webpack-transpiler/src/fs/OutputFileSystem.ts @@ -4,7 +4,6 @@ import * as path from 'path'; import { Callback, EmptyCallback, webpack } from '../types'; export default class OutputFileSystem implements webpack.OutputFileSystem { - private _files: { [name: string]: string | Buffer; }; @@ -18,8 +17,7 @@ export default class OutputFileSystem implements webpack.OutputFileSystem { } public collectFiles(): File[] { - return Object.keys(this._files).map(fileName => - new File(fileName, this._files[fileName])); + return Object.keys(this._files).map(fileName => new File(fileName, this._files[fileName])); } public mkdirp(_dir: string, opts: any, cb?: Callback): void { diff --git a/packages/webpack-transpiler/src/index.ts b/packages/webpack-transpiler/src/index.ts index d7383d9efb..6027b0cc94 100644 --- a/packages/webpack-transpiler/src/index.ts +++ b/packages/webpack-transpiler/src/index.ts @@ -3,9 +3,7 @@ import ConfigLoader from './compiler/ConfigLoader'; import { pluginTokens } from './pluginTokens'; import WebpackTranspiler from './WebpackTranspiler'; -export const strykerPlugins = [ - declareFactoryPlugin(PluginKind.Transpiler, 'webpack', webpackTranspilerFactory) -]; +export const strykerPlugins = [declareFactoryPlugin(PluginKind.Transpiler, 'webpack', webpackTranspilerFactory)]; function webpackTranspilerFactory(injector: Injector) { return injector diff --git a/packages/webpack-transpiler/src/pluginTokens.ts b/packages/webpack-transpiler/src/pluginTokens.ts index ad4bfcb12e..89608bf035 100644 --- a/packages/webpack-transpiler/src/pluginTokens.ts +++ b/packages/webpack-transpiler/src/pluginTokens.ts @@ -1,4 +1,3 @@ - function stringLiteral(literal: T) { return literal; } diff --git a/packages/webpack-transpiler/src/types.ts b/packages/webpack-transpiler/src/types.ts index 1f830a2216..522749ce57 100644 --- a/packages/webpack-transpiler/src/types.ts +++ b/packages/webpack-transpiler/src/types.ts @@ -5,7 +5,6 @@ export type EmptyCallback = (err?: NodeJS.ErrnoException | null) => void; export type Callback = (err: NodeJS.ErrnoException | null | undefined, arg?: T) => void; export declare namespace webpack { - /** * Grabbed from https://github.com/webpack/enhanced-resolve/blob/4a9488d1f0954351cbbee80fbf395688f853ef1f/lib/NodeJsInputFileSystem.js */ @@ -13,9 +12,13 @@ export declare namespace webpack { stat(path: string, callback: Callback): void; readdir(path: string, callback: Callback): void; - readFile(path: string, options: { encoding: string; flag?: string; } | string, callback: Callback): void; - readFile(path: string, options: { encoding?: null; flag?: string; } | undefined | null, callback: Callback): void; - readFile(path: string, options: { encoding?: string | null; flag?: string; } | string | undefined | null, callback: Callback): void; + readFile(path: string, options: { encoding: string; flag?: string } | string, callback: Callback): void; + readFile(path: string, options: { encoding?: null; flag?: string } | undefined | null, callback: Callback): void; + readFile( + path: string, + options: { encoding?: string | null; flag?: string } | string | undefined | null, + callback: Callback + ): void; readFile(path: string, callback: Callback): void; readlink(path: string, callback: Callback): void; @@ -37,7 +40,12 @@ export declare namespace webpack { mkdir(name: string, callback: EmptyCallback): void; unlink(name: string, callback: EmptyCallback): void; writeFile(path: string, data: any, callback: EmptyCallback): void; - writeFile(path: string, data: any, options: { encoding?: string | null; mode?: number | string; flag?: string; } | string | undefined | null, callback: EmptyCallback): void; + writeFile( + path: string, + data: any, + options: { encoding?: string | null; mode?: number | string; flag?: string } | string | undefined | null, + callback: EmptyCallback + ): void; join(...paths: string[]): string; } } diff --git a/packages/webpack-transpiler/test/helpers/mockInterfaces.ts b/packages/webpack-transpiler/test/helpers/mockInterfaces.ts index e264ebac4d..9032425015 100644 --- a/packages/webpack-transpiler/test/helpers/mockInterfaces.ts +++ b/packages/webpack-transpiler/test/helpers/mockInterfaces.ts @@ -1,8 +1,8 @@ import { Stats } from 'webpack'; export interface WebpackCompilerMock { - run(callback: (err: Error, stats: Stats) => void): void; - inputFileSystem: { fileSystem: any }; - outputFileSystem: { fileSystem: any }; - resolvers: { normal: { fileSystem: any }, context: { fileSystem: any } }; + run(callback: (err: Error, stats: Stats) => void): void; + inputFileSystem: { fileSystem: any }; + outputFileSystem: { fileSystem: any }; + resolvers: { normal: { fileSystem: any }; context: { fileSystem: any } }; } diff --git a/packages/webpack-transpiler/test/helpers/producers.ts b/packages/webpack-transpiler/test/helpers/producers.ts index c0553872c1..16d9b5e019 100644 --- a/packages/webpack-transpiler/test/helpers/producers.ts +++ b/packages/webpack-transpiler/test/helpers/producers.ts @@ -4,7 +4,7 @@ import { Configuration } from 'webpack'; import { StrykerWebpackConfig } from '../../src/WebpackTranspiler'; import { WebpackCompilerMock } from './mockInterfaces'; -export type Mock = { [K in keyof T]: sinon.SinonStub; }; +export type Mock = { [K in keyof T]: sinon.SinonStub }; export type Constructor = new (...args: any[]) => T; @@ -44,6 +44,6 @@ export function createWebpackMock(): WebpackCompilerMock { context: { fileSystem: {} }, normal: { fileSystem: {} } }, - run: () => { } + run: () => {} }; } diff --git a/packages/webpack-transpiler/test/integration/inputFS.it.spec.ts b/packages/webpack-transpiler/test/integration/inputFS.it.spec.ts index b7763e61c0..35c15c298a 100644 --- a/packages/webpack-transpiler/test/integration/inputFS.it.spec.ts +++ b/packages/webpack-transpiler/test/integration/inputFS.it.spec.ts @@ -30,17 +30,14 @@ describe('InputFileSystem integration', () => { it('should be able to stat a dir', done => { testResourcePath('inputFileSystem', 'dir1', 'tempFile'); - sut.stat( - testResourcePath('inputFileSystem', 'dir2'), - (err, stats: any) => { - if (err) { - done(err); - } else { - expect(stats.isDirectory()).ok; - done(); - } + sut.stat(testResourcePath('inputFileSystem', 'dir2'), (err, stats: any) => { + if (err) { + done(err); + } else { + expect(stats.isDirectory()).ok; + done(); } - ); + }); }); }); }); diff --git a/packages/webpack-transpiler/test/integration/transpiler.it.spec.ts b/packages/webpack-transpiler/test/integration/transpiler.it.spec.ts index dd482c0763..f5b876bf71 100644 --- a/packages/webpack-transpiler/test/integration/transpiler.it.spec.ts +++ b/packages/webpack-transpiler/test/integration/transpiler.it.spec.ts @@ -9,7 +9,6 @@ import { pluginTokens } from '../../src/pluginTokens'; import WebpackTranspiler from '../../src/WebpackTranspiler'; describe('Webpack transpiler', () => { - beforeEach(() => { testInjector.options.webpack = {}; }); @@ -20,7 +19,7 @@ describe('Webpack transpiler', () => { const files = readFiles(); const transpiledFiles = await sut.transpile(files); - const bundleFile = transpiledFiles.filter(file => file.name.indexOf('my-first-webpack.bundle.js') >= 0)[0]; + const bundleFile = transpiledFiles.filter(file => file.name.includes('my-first-webpack.bundle.js'))[0]; expect(transpiledFiles).lengthOf(3); // input + output expect(bundleFile.textContent).include('Hello world!'); // input + output }); diff --git a/packages/webpack-transpiler/test/unit/WebpackTranspiler.spec.ts b/packages/webpack-transpiler/test/unit/WebpackTranspiler.spec.ts index 2f71d5508c..c1e6552d8f 100644 --- a/packages/webpack-transpiler/test/unit/WebpackTranspiler.spec.ts +++ b/packages/webpack-transpiler/test/unit/WebpackTranspiler.spec.ts @@ -45,8 +45,11 @@ describe('WebpackTranspiler', () => { it('should throw an error if `produceSourceMaps` is `true`', () => { testInjector.options.coverageAnalysis = 'perTest'; - expect(() => new WebpackTranspiler(factory.strykerOptions({ coverageAnalysis: 'perTest' }), true, configLoaderStub as unknown as ConfigLoader)) - .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"'); + expect( + () => new WebpackTranspiler(factory.strykerOptions({ coverageAnalysis: 'perTest' }), true, (configLoaderStub as unknown) as ConfigLoader) + ).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"' + ); }); it('should call the webpackCompiler.writeFilesToFs function with the given files', async () => { @@ -83,7 +86,7 @@ describe('WebpackTranspiler', () => { function createSut() { return testInjector.injector .provideValue(commonTokens.produceSourceMaps, false) - .provideValue('configLoader', configLoaderStub as unknown as ConfigLoader) + .provideValue('configLoader', (configLoaderStub as unknown) as ConfigLoader) .injectClass(WebpackTranspiler); } }); diff --git a/packages/webpack-transpiler/test/unit/compiler/ConfigLoader.spec.ts b/packages/webpack-transpiler/test/unit/compiler/ConfigLoader.spec.ts index eafaaefc7e..27fb75566e 100644 --- a/packages/webpack-transpiler/test/unit/compiler/ConfigLoader.spec.ts +++ b/packages/webpack-transpiler/test/unit/compiler/ConfigLoader.spec.ts @@ -8,9 +8,17 @@ import ConfigLoader from '../../../src/compiler/ConfigLoader'; import { pluginTokens } from '../../../src/pluginTokens'; import { createStrykerWebpackConfig } from '../../helpers/producers'; -class FooPlugin implements Plugin { public foo = true; public apply() { } } -class ProgressPlugin implements Plugin { public apply() { } } -class BarPlugin implements Plugin { public bar = true; public apply() { } } +class FooPlugin implements Plugin { + public foo = true; + public apply() {} +} +class ProgressPlugin implements Plugin { + public apply() {} +} +class BarPlugin implements Plugin { + public bar = true; + public apply() {} +} describe('ConfigLoader', () => { let sut: ConfigLoader; @@ -21,9 +29,7 @@ describe('ConfigLoader', () => { requireStub = sinon.stub(); existsSyncStub = sinon.stub(fs, 'existsSync'); - sut = testInjector.injector - .provideValue(pluginTokens.require, requireStub) - .injectClass(ConfigLoader); + sut = testInjector.injector.provideValue(pluginTokens.require, requireStub).injectClass(ConfigLoader); }); it('should load webpack config from given location', async () => { @@ -49,7 +55,7 @@ describe('ConfigLoader', () => { it('should remove "ProgressPlugin" if silent is `true`', async () => { // Arrange - const bazPlugin = { baz: true, apply() { } }; + const bazPlugin = { baz: true, apply() {} }; const webpackConfig: Configuration = { plugins: [new FooPlugin(), new ProgressPlugin(), new BarPlugin(), bazPlugin] }; @@ -61,9 +67,16 @@ describe('ConfigLoader', () => { const result = await sut.load(createStrykerWebpackConfig({ configFile: 'webpack.config.js', silent: true })); // Assert - expect(result.plugins).to.be.an('array').that.does.not.deep.include(new ProgressPlugin()); - expect(result.plugins).to.be.an('array').that.deep.equals([new FooPlugin(), new BarPlugin(), bazPlugin]); - expect(testInjector.logger.debug).calledWith('Removing webpack plugin "%s" to keep webpack bundling silent. Set `webpack: { silent: false }` in your stryker.conf.js file to disable this feature.', 'ProgressPlugin'); + expect(result.plugins) + .to.be.an('array') + .that.does.not.deep.include(new ProgressPlugin()); + expect(result.plugins) + .to.be.an('array') + .that.deep.equals([new FooPlugin(), new BarPlugin(), bazPlugin]); + expect(testInjector.logger.debug).calledWith( + 'Removing webpack plugin "%s" to keep webpack bundling silent. Set `webpack: { silent: false }` in your stryker.conf.js file to disable this feature.', + 'ProgressPlugin' + ); }); it('should not remove "ProgressPlugin" if silent is `false`', async () => { @@ -75,7 +88,9 @@ describe('ConfigLoader', () => { existsSyncStub.returns(true); const result = await sut.load(createStrykerWebpackConfig({ configFile: 'webpack.config.js', silent: false })); - expect(result.plugins).to.be.an('array').that.does.deep.include(new ProgressPlugin()); + expect(result.plugins) + .to.be.an('array') + .that.does.deep.include(new ProgressPlugin()); }); it('should return an object with the context property pointing to the projectRoot when webpack.config.js does not exist', async () => { @@ -93,11 +108,12 @@ describe('ConfigLoader', () => { existsSyncStub.returns(false); - return expect(sut.load(createStrykerWebpackConfig({ configFile }))) - .rejectedWith(`Could not load webpack config at "${path.resolve(configFile)}", file not found.`); + return expect(sut.load(createStrykerWebpackConfig({ configFile }))).rejectedWith( + `Could not load webpack config at "${path.resolve(configFile)}", file not found.` + ); }); - it('should log a debug message when the Webpack configuration is not found and it\'s trying webpack 4 zero config instead', async () => { + it("should log a debug message when the Webpack configuration is not found and it's trying webpack 4 zero config instead", async () => { const contextPath: string = '/path/to/project/root'; existsSyncStub.returns(false); diff --git a/packages/webpack-transpiler/test/unit/compiler/WebpackCompiler.spec.ts b/packages/webpack-transpiler/test/unit/compiler/WebpackCompiler.spec.ts index 10c4eba798..0cbcc2a3c9 100644 --- a/packages/webpack-transpiler/test/unit/compiler/WebpackCompiler.spec.ts +++ b/packages/webpack-transpiler/test/unit/compiler/WebpackCompiler.spec.ts @@ -25,7 +25,6 @@ describe('WebpackCompiler', () => { }); describe('writeFilesToFs', () => { - beforeEach(() => { sut = new WebpackCompiler(fakeWebpackConfig, inputFileSystemMock as any, outputFileSystemMock as any); }); diff --git a/packages/webpack-transpiler/test/unit/fs/InputFileSystem.spec.ts b/packages/webpack-transpiler/test/unit/fs/InputFileSystem.spec.ts index 3e87773d6b..e0eccb21b2 100644 --- a/packages/webpack-transpiler/test/unit/fs/InputFileSystem.spec.ts +++ b/packages/webpack-transpiler/test/unit/fs/InputFileSystem.spec.ts @@ -18,11 +18,11 @@ describe('InputFileSystem', () => { }); describe('writeFileSync', () => { - it(`should forward to memory fs`, () => { + it('should forward to memory fs', () => { sut.writeFileSync('path', 'content'); expect(memoryFSMock.writeFileSync).calledWith('path', 'content'); }); - it(`should replace empty string content`, () => { + it('should replace empty string content', () => { sut.writeFileSync('path', ''); expect(memoryFSMock.writeFileSync).calledWith('path', ' '); }); @@ -33,7 +33,6 @@ describe('InputFileSystem', () => { }); describe('stat', () => { - it('should forward to memory fs', done => { memoryFSMock.stat.callsArgWith(1, undefined, 'foobar'); sut.stat('arg1', (_error, value) => { @@ -61,7 +60,6 @@ describe('InputFileSystem', () => { // Act sut.stat('foo/bar/not/exits', err => { - // Assert expect(err).eq(expectedError); done(); @@ -70,7 +68,6 @@ describe('InputFileSystem', () => { }); describe('readFile', () => { - it('should forward readFile to memory FS', done => { memoryFSMock.readFile.callsArgOnWith(2, sut, undefined, 'foobar'); sut.readFile('path', {}, (_error: Error, value: string) => { @@ -82,7 +79,7 @@ describe('InputFileSystem', () => { }); it('should forward to real FS if memory-fs gave an error', done => { - memoryFSMock.readFile.callsArgOnWith(1, sut, new Error('foobar')); + memoryFSMock.readFile.callsArgOnWith(1, sut, new Error('foobar')); innerFSMock.readFile.callsArgWith(1, undefined, 'the content'); sut.readFile('foobar', (_error: Error, content: string) => { expect(content).eq('the content'); @@ -99,7 +96,6 @@ describe('InputFileSystem', () => { // Act sut.readFile('foo/bar/not/exits', (error: Error) => { - // Assert expect(error).eq(expectedError); done(); diff --git a/packages/webpack-transpiler/test/unit/fs/OutputFileSystem.spec.ts b/packages/webpack-transpiler/test/unit/fs/OutputFileSystem.spec.ts index bea7dbc66b..1565e5f5d8 100644 --- a/packages/webpack-transpiler/test/unit/fs/OutputFileSystem.spec.ts +++ b/packages/webpack-transpiler/test/unit/fs/OutputFileSystem.spec.ts @@ -4,7 +4,6 @@ import * as path from 'path'; import OutputFileSystem from '../../../src/fs/OutputFileSystem'; describe('OutputFileSystem', () => { - let sut: OutputFileSystem; beforeEach(() => { @@ -32,29 +31,24 @@ describe('OutputFileSystem', () => { }); describe('when "collectFiles"', () => { - it('should collect a css file', () => { const fileName = 'file.css'; const fileContent = 'body: { background: blue }'; - sut.writeFile(fileName, fileContent, () => { }); + sut.writeFile(fileName, fileContent, () => {}); const expectedFile = new File(path.resolve(fileName), fileContent); expect(sut.collectFiles()).deep.eq([expectedFile]); }); it('should collect files', () => { const binContent = Buffer.from(''); - sut.writeFile('bin1.ico', binContent, () => { }); - sut.writeFile('file1.js', 'data', () => { }); - const expectedFiles = [ - new File(path.resolve('bin1.ico'), binContent), - new File(path.resolve('file1.js'), 'data') - ]; + sut.writeFile('bin1.ico', binContent, () => {}); + sut.writeFile('file1.js', 'data', () => {}); + const expectedFiles = [new File(path.resolve('bin1.ico'), binContent), new File(path.resolve('file1.js'), 'data')]; expect(sut.collectFiles()).deep.eq(expectedFiles); }); }); - function actions(...actions: ('mkdir' | 'rmdir')[]) { + function actions(...actions: Array<'mkdir' | 'rmdir'>) { return actions; } - }); diff --git a/tslint.json b/tslint.json deleted file mode 100644 index 0a796ba67a..0000000000 --- a/tslint.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "defaultSeverity": "error", - "extends": [ - "tslint:recommended" - ], - "rules": { - "array-type": [true, "array"], - "arrow-parens": [true, "ban-single-arg-parens"], - "ban-types": { - "options": [ - ["Object", "Avoid using the `Object` type. Did you mean `object`?"], - ["Boolean", "Avoid using the `Boolean` type. Did you mean `boolean`?"], - ["Number", "Avoid using the `Number` type. Did you mean `number`?"], - ["String", "Avoid using the `String` type. Did you mean `string`?"], - ["Symbol", "Avoid using the `Symbol` type. Did you mean `symbol`?"] - ] - }, - "class-name": true, - "comment-format": [true, "check-space"], - "completed-docs": false, - "curly": [true, "ignore-same-line"], - "file-name-casing": [ false, "camel-case" ], - "forin": false, - "function-constructor": false, - "indent": [true, "spaces"], - "interface-name": [true, "never-prefix"], - "jsdoc-format": true, - "max-classes-per-file": false, - "max-line-length": false, - "member-ordering": false, - "no-any": false, - "no-bitwise": false, - "no-conditional-assignment": false, - "no-console": false, - "no-consecutive-blank-lines": true, - "no-construct": true, - "no-debugger": true, - "no-duplicate-super": true, - "no-duplicate-switch-case": true, - "no-duplicate-variable": true, - "no-empty": false, - "no-empty-interface": false, - "no-eval": true, - "no-internal-module": true, - "no-invalid-this": false, - "no-irregular-whitespace": true, - "no-namespace": false, - "no-parameter-reassignment": false, - "no-return-await": true, - "no-shadowed-variable": false, - "no-sparse-arrays": true, - "no-string-literal": true, - "no-switch-case-fall-through": true, - "no-trailing-whitespace": true, - "no-unnecessary-class": [true, "allow-constructor-only", "allow-empty-class", "allow-static-only"], - "no-unused-expression": false, - "no-unsafe-any": false, - "no-unsafe-finally": true, - "no-var-keyword": true, - "no-var-requires": false, - "object-literal-sort-keys": [true, "ignore-case"], - "one-line": [true, "check-open-brace", "check-whitespace"], - "only-arrow-functions": [true, "allow-declarations", "allow-named-functions"], - "ordered-imports": true, - "prefer-const": true, - "prefer-method-signature": true, - "prefer-readonly": true, - "quotemark": [true, "single"], - "semicolon": [true, "always"], - "trailing-comma": false, - "triple-equals": [true, "allow-null-check"], - "typedef-whitespace": [ - true, - { - "call-signature": "nospace", - "index-signature": "nospace", - "parameter": "nospace", - "property-declaration": "nospace", - "variable-declaration": "nospace" - } - ], - "unified-signatures": true, - "variable-name": false, - "whitespace": [true, "check-branch", "check-decl", "check-module", "check-operator", "check-separator", "check-type"], - - "naming-convention": [ - true, - { "type": "default", "format": "", "leadingUnderscore": "allow", "trailingUnderscore": "allow" }, - { "type": "variable", "modifiers": ["global", "const"], "format": "" }, - { "type": "variable", "modifiers": ["export", "const"], "format": "" }, - { "type": "functionVariable", "modifiers": ["export", "const"], "format": "" }, - { "type": "parameter", "modifiers": "unused", "leadingUnderscore": "allow" }, - { "type": "member", "modifiers": "private", "leadingUnderscore": "allow" }, - { "type": "member", "modifiers": "protected", "leadingUnderscore": "allow" }, - { "type": "method", "filter": "^toJSON$", "format": null }, - { "type": "property", "modifiers": ["public", "static", "const"], "format": "" }, - { "type": "type", "format": "PascalCase" }, - { "type": "class", "modifiers": "abstract", "prefix": "" }, - { "type": "interface", "prefix": "" }, - { "type": "genericTypeParameter", "prefix": "" }, - { "type": "enumMember", "format": "PascalCase" } - ], - "prefer-const-enum": true - }, - "rulesDirectory": ["tslint-consistent-codestyle"] -} \ No newline at end of file diff --git a/workspace.code-workspace b/workspace.code-workspace index a3df5b9a5a..33b98c0554 100644 --- a/workspace.code-workspace +++ b/workspace.code-workspace @@ -1,92 +1,124 @@ { - "folders": [ - { - "path": "packages/core" - }, - { - "path": "packages/grunt-stryker" - }, - { - "path": "packages/api" - }, - { - "path": "packages/babel-transpiler" - }, - { - "path": "packages/html-reporter" - }, - { - "path": "packages/jasmine-framework" - }, - { - "path": "packages/jasmine-runner" - }, - { - "path": "packages/javascript-mutator" - }, - { - "path": "packages/jest-runner" - }, - { - "path": "packages/karma-runner" - }, - { - "path": "packages/mocha-framework" - }, - { - "path": "packages/mocha-runner" - }, - { - "path": "packages/mutator-specification" - }, - { - "path": "packages/typescript" - }, - { - "path": "packages/vue-mutator" - }, - { - "path": "packages/webpack-transpiler" - }, - { - "path": "packages/wct-runner" - }, - { - "path": "packages/util" - }, - { - "path": "packages/test-helpers" - }, - { - "path": "e2e" - }, - { - "path": "perf" - }, - { - "path": ".", - "name": "parent" - } - ], - "settings": { - "typescript.tsdk": "node_modules\\typescript\\lib", - "files.exclude": { - ".git": true, - ".tscache": true, - "{src,test}/**/*.d.ts": true, //base mapping not possible: https://github.com/Microsoft/vscode/issues/40850 - "*.d.ts": true, // needed to exclude d.ts files in api - "**/*.js": { - "when": "$(basename).ts" - }, - "**/**/*.js": { // HACK! Cannot reuse same key, but this key means essentially the same - "when": "$(basename).tsx" - }, - "**/*.map": { - "when": "$(basename)" - } - }, - "editor.codeActionsOnSave": { - "source.fixAll.tslint": true - } - } + "folders": [ + { + "name": "core", + "path": "packages\\core" + }, + { + "name": "grunt-stryker", + "path": "packages\\grunt-stryker" + }, + { + "name": "api", + "path": "packages\\api" + }, + { + "name": "babel-transpiler", + "path": "packages\\babel-transpiler" + }, + { + "name": "html-reporter", + "path": "packages\\html-reporter" + }, + { + "name": "jasmine-framework", + "path": "packages\\jasmine-framework" + }, + { + "name": "javascript-mutator", + "path": "packages\\javascript-mutator" + }, + { + "name": "jasmine-runner", + "path": "packages\\jasmine-runner" + }, + { + "name": "jest-runner", + "path": "packages\\jest-runner" + }, + { + "name": "karma-runner", + "path": "packages\\karma-runner" + }, + { + "name": "mocha-framework", + "path": "packages\\mocha-framework" + }, + { + "name": "mocha-runner", + "path": "packages\\mocha-runner" + }, + { + "name": "mutator-specification", + "path": "packages\\mutator-specification" + }, + { + "name": "typescript", + "path": "packages\\typescript" + }, + { + "name": "vue-mutator", + "path": "packages\\vue-mutator" + }, + { + "name": "webpack-transpiler", + "path": "packages\\webpack-transpiler" + }, + { + "name": "wct-runner", + "path": "packages\\wct-runner" + }, + { + "name": "util", + "path": "packages\\util" + }, + { + "name": "test-helpers", + "path": "packages\\test-helpers" + }, + { + "name": "e2e", + "path": "e2e" + }, + { + "name": "perf", + "path": "perf" + }, + { + "name": "parent", + "path": "." + } + ], + "settings": { + "typescript.tsdk": "node_modules\\typescript\\lib", + "files.exclude": { + ".git": true, + ".tscache": true, + "{src,test}/**/*.d.ts": true, //base mapping not possible: https://github.com/Microsoft/vscode/issues/40850 + "*.d.ts": true, // needed to exclude d.ts files in api + "**/*.js": { + "when": "$(basename).ts" + }, + "**/**/*.js": { // HACK! Cannot reuse same key, but this key means essentially the same + "when": "$(basename).tsx" + }, + "**/*.map": { + "when": "$(basename)" + } + }, + "eslint.enable": true, + "eslint.autoFixOnSave": true, + "eslint.validate": [ + "javascript", + { + "language": "typescript", + "autoFix": true + } + ] + }, + "extensions": { + "recommendations": [ + "dbaeumer.vscode-eslint" + ] + } }