Comparing version 0.20.1 to 0.21.0
@@ -6,2 +6,27 @@ # Change Log | ||
<a name="0.21.0"></a> | ||
# [0.21.0](https://github.com/stryker-mutator/stryker/compare/stryker@0.20.1...stryker@0.21.0) (2018-04-04) | ||
### Bug Fixes | ||
* **Progress reporter:** don't prevent stryker from closing ([21255aa](https://github.com/stryker-mutator/stryker/commit/21255aa)) | ||
### Features | ||
* **identify-files:** use git to list files in `InputFileResolver` ([df6169a](https://github.com/stryker-mutator/stryker/commit/df6169a)) | ||
### BREAKING CHANGES | ||
* **identify-files:** * The `InputFileDescriptor` syntax for files is no longer supported. | ||
* Test runner plugins should keep track of which files are included | ||
into a test run and in which order. | ||
* Transpiler plugins should keep track of which files are to be | ||
transpiled. | ||
<a name="0.20.1"></a> | ||
@@ -8,0 +33,0 @@ ## [0.20.1](https://github.com/stryker-mutator/stryker/compare/stryker@0.20.0...stryker@0.20.1) (2018-03-22) |
{ | ||
"name": "stryker", | ||
"version": "0.20.1", | ||
"version": "0.21.0", | ||
"description": "The extendable JavaScript mutation testing framework", | ||
@@ -71,4 +71,4 @@ "main": "src/Stryker.js", | ||
"rxjs": "^5.4.3", | ||
"serialize-javascript": "^1.3.0", | ||
"source-map": "^0.6.1", | ||
"surrial": "^0.1.3", | ||
"tslib": "^1.5.0", | ||
@@ -83,7 +83,7 @@ "typed-rest-client": "^1.0.7" | ||
"@types/progress": "^2.0.1", | ||
"stryker-api": "^0.14.0" | ||
"stryker-api": "^0.15.0" | ||
}, | ||
"peerDependencies": { | ||
"stryker-api": "^0.14.0" | ||
"stryker-api": "^0.15.0" | ||
} | ||
} |
@@ -50,7 +50,6 @@ [![Build Status](https://travis-ci.org/stryker-mutator/stryker.svg?branch=master)](https://travis-ci.org/stryker-mutator/stryker) | ||
config.set({ | ||
files: ['test/helpers/**/*.js', | ||
'test/unit/**/*.js', | ||
{ pattern: 'src/**/*.js', included: false, mutated: true } | ||
{ pattern: 'src/templates/*.html', included: false, mutated: false } | ||
'!src/fileToIgnore.js'], | ||
mutate: [ | ||
'src/**/*.js' | ||
'!src/index.js' | ||
], | ||
testFramework: 'mocha', | ||
@@ -65,5 +64,5 @@ testRunner: 'mocha', | ||
As you can see, the config file is *not* a simple JSON file. It should be a common js (a.k.a. node) module. You might recognize this way of working from the karma test runner. | ||
As you can see, the config file is *not* a simple JSON file. It should be a node module. You might recognize this way of working from the karma test runner. | ||
Make sure you *at least* specify the `files` and the `testRunner` options when mixing the config file and/or command line options. | ||
Make sure you *at least* specify the `testRunner` options when mixing the config file and/or command line options. | ||
@@ -75,3 +74,3 @@ ## Command-line interface | ||
``` | ||
```bash | ||
$ npm install -g stryker-cli | ||
@@ -82,6 +81,8 @@ ``` | ||
## Supported mutators | ||
## Supported mutators | ||
See our website for the [list of currently supported mutators](https://stryker-mutator.io/mutators.html). | ||
## Configuration | ||
All configuration options can either be set via the command line or via the `stryker.conf.js` config file. | ||
@@ -91,34 +92,14 @@ | ||
This is the same globbing format you might know from [Grunt](https://github.com/gruntjs/grunt) or [Karma](https://github.com/karma-runner/karma). | ||
You can *ignore* files by adding an exclamation mark (`!`) at the start of an expression. | ||
#### Files required to run your tests | ||
**Command line:** `[--files|-f] node_modules/a-lib/**/*.js,src/**/*.js,a.js,test/**/*.js` | ||
**Config file:** `files: ['{ pattern: 'src/**/*.js', mutated: true }, '!src/**/index.js', 'test/**/*.js']` | ||
**Default value:** *none* | ||
**Mandatory**: yes | ||
**Description:** | ||
With `files` you specify all files needed to run your tests. If the test runner you use already provides the test framework (Jasmine, Mocha, etc.), | ||
you should *not* include those files here as well. | ||
The files will be loaded in the order in which they are specified. Files that you want to ignore should be mentioned last. | ||
When using the command line, the list can only contain a comma separated list of globbing expressions. | ||
When using the config file you can provide an array with `string`s or `InputFileDescriptor` objects, like so: | ||
* `string`: The globbing expression used for selecting the files needed to run the tests. | ||
* `InputFileDescriptor` object: `{ pattern: 'pattern', included: true, mutated: false }`: | ||
* The `pattern` property is mandatory and contains the globbing expression used for selecting the files. Using `!` to ignore files is *not* supported here. | ||
* The `included` property is optional and determines whether or not this file should be loaded initially by the test-runner (default: true). With `included: false` the files will be copied to the sandbox during testing, but they wont be explicitly loaded by the test runner. Two usecases for `included: false` are for HTML files and for source files when your tests `require()` them. | ||
* The `mutated` property is optional and determines whether or not this file should be targeted for mutations (default: false) | ||
*Note*: To include a file/folder which start with an exclamation mark (`!`), use the `InputFileDescriptor` syntax. | ||
#### Source code files to mutate | ||
#### Files to mutate | ||
**Command line:** `[--mutate|-m] src/**/*.js,a.js` | ||
**Config file:** `mutate: ['src/**/*.js', 'a.js']` | ||
**Default value:** *none* | ||
**Mandatory**: no | ||
**Mandatory**: No | ||
**Description:** | ||
With `mutate` you configure the subset of files to use for mutation testing. Generally speaking, these should be your own source files. | ||
This is optional, as you can also use the `mutated` property with the `files` parameter or not mutate any files at all to perform a dry-run (test-run). | ||
We expect a comma separated list of globbing expressions, which will be used to select the files to be mutated. | ||
With `mutate` you configure the subset of files to use for mutation testing. | ||
Generally speaking, these should be your own source files. | ||
This is optional, as you can choose to not mutate any files at all and perform a dry-run (running only your tests without mutating). | ||
@@ -139,7 +120,10 @@ #### Test runner | ||
**Default value:** *none* | ||
**Mandatory**: yes | ||
**Mandatory**: No | ||
**Description:** | ||
With `testFramework` you configure which test framework your tests are using. This value is directly consumed by the test runner and therefore | ||
depends what framework that specific test runner supports. By default, this value is also used for `testFramework`. | ||
Configure which test framework you are using. | ||
This option is not mandatory, as Stryker is test framework agnostic (it doesn't care what framework you use), | ||
However, it is required when `coverageAnalysis` is set to `'perTest'`, because Stryker needs to hook into the test framework in order to measure code coverage results per test and filter tests to run. | ||
Make sure the a plugin is installed for your chosen test framework. E.g. install `stryker-mocha-framework` to use `'mocha'` as a test framework. | ||
#### Type of coverage analysis | ||
@@ -151,3 +135,4 @@ **Full notation:** `--coverageAnalysis perTest` | ||
**Description:** | ||
With `coverageAnalysis` you specify which coverage analysis strategy you want to use. | ||
With `coverageAnalysis` you specify which coverage analysis strategy you want to use. | ||
Stryker can analyse code coverage results. This can potentially speed up mutation testing a lot, as only the tests covering a | ||
@@ -222,2 +207,20 @@ particular mutation are tested for each mutant. | ||
#### Files in the sandbox | ||
**Command line:** `[--files|-f] src/**/*.js,a.js,test/**/*.js` | ||
**Config file:** `files: ['src/**/*.js', '!src/**/index.js', 'test/**/*.js']` | ||
**Default value:** result of `git ls-files --others --exclude-standard --cached` | ||
**Mandatory**: No | ||
**Description:** | ||
With `files` you can choose which files should be included in your test runner sandbox. | ||
This is normally not needed as it defaults to all files not ignored by git. | ||
Try it out yourself with this command: `git ls-files --others --exclude-standard --cached`. | ||
If you do need to override `files` (for example: when your project does not live in a git repository), | ||
you can override the files here. | ||
When using the command line, the list can only contain a comma separated list of globbing expressions. | ||
When using the config file you can provide an array with `string`s | ||
You can *ignore* files by adding an exclamation mark (`!`) at the start of an expression. | ||
#### Plugins | ||
@@ -298,4 +301,3 @@ **Command line:** `--plugins stryker-html-reporter,stryker-karma-runner` | ||
It is not allowed to only supply one value. However, `high` and `low` values can be the same, making sure colors are either red or green. | ||
Set `break` to `null` (default) to never let the process crash. | ||
It is not allowed to only supply one value of the values (it's all or nothing). However, `high` and `low` values can be the same, making sure colors are either red or green. Set `break` to `null` (default) to never let your build fail. | ||
@@ -302,0 +304,0 @@ #### Log level |
@@ -10,2 +10,3 @@ export declare type ChildProxy<T> = { | ||
private workerTasks; | ||
private log; | ||
private constructor(); | ||
@@ -29,2 +30,3 @@ /** | ||
dispose(): void; | ||
private logUnidentifiedMessage(message); | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var child_process_1 = require("child_process"); | ||
var core_1 = require("stryker-api/core"); | ||
var messageProtocol_1 = require("./messageProtocol"); | ||
var objectUtils_1 = require("../utils/objectUtils"); | ||
var Task_1 = require("../utils/Task"); | ||
var log4js_1 = require("log4js"); | ||
var ChildProcessProxy = /** @class */ (function () { | ||
@@ -12,2 +14,3 @@ function ChildProcessProxy(requirePath, logLevel, plugins, constructorFunction, constructorParams) { | ||
this.workerTasks = []; | ||
this.log = log4js_1.getLogger(ChildProcessProxy.name); | ||
this.worker = child_process_1.fork(require.resolve('./ChildProcessProxyWorker'), [messageProtocol_1.autoStart], { silent: false, execArgv: [] }); | ||
@@ -67,9 +70,17 @@ this.initTask = new Task_1.default(); | ||
this.worker.on('message', function (serializedMessage) { | ||
var message = objectUtils_1.deserialize(serializedMessage); | ||
if (message === 'init_done') { | ||
_this.initTask.resolve(undefined); | ||
var message = objectUtils_1.deserialize(serializedMessage, [core_1.File]); | ||
switch (message.kind) { | ||
case messageProtocol_1.ParentMessageKind.Initialized: | ||
_this.initTask.resolve(undefined); | ||
break; | ||
case messageProtocol_1.ParentMessageKind.Result: | ||
_this.workerTasks[message.correlationId].resolve(message.result); | ||
break; | ||
case messageProtocol_1.ParentMessageKind.Rejection: | ||
_this.workerTasks[message.correlationId].reject(new Error(message.error)); | ||
break; | ||
default: | ||
_this.logUnidentifiedMessage(message); | ||
break; | ||
} | ||
else { | ||
_this.workerTasks[message.correlationId].resolve(message.result); | ||
} | ||
}); | ||
@@ -80,2 +91,5 @@ }; | ||
}; | ||
ChildProcessProxy.prototype.logUnidentifiedMessage = function (message) { | ||
this.log.error("Received unidentified message " + message); | ||
}; | ||
return ChildProcessProxy; | ||
@@ -82,0 +96,0 @@ }()); |
@@ -0,0 +0,0 @@ export default class ChildProcessProxyWorker { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var log4js_1 = require("log4js"); | ||
var core_1 = require("stryker-api/core"); | ||
var objectUtils_1 = require("../utils/objectUtils"); | ||
var messageProtocol_1 = require("./messageProtocol"); | ||
var log4js_1 = require("log4js"); | ||
var PluginLoader_1 = require("../PluginLoader"); | ||
@@ -20,3 +21,3 @@ var ChildProcessProxyWorker = /** @class */ (function () { | ||
var handler = function (serializedMessage) { | ||
var message = objectUtils_1.deserialize(serializedMessage); | ||
var message = objectUtils_1.deserialize(serializedMessage, [core_1.File]); | ||
switch (message.kind) { | ||
@@ -28,12 +29,22 @@ case messageProtocol_1.WorkerMessageKind.Init: | ||
_this.realSubject = new (RealSubjectClass.bind.apply(RealSubjectClass, [void 0].concat(message.constructorArgs)))(); | ||
_this.send('init_done'); | ||
_this.send({ kind: messageProtocol_1.ParentMessageKind.Initialized }); | ||
_this.removeAnyAdditionalMessageListeners(handler); | ||
break; | ||
case messageProtocol_1.WorkerMessageKind.Work: | ||
var result = (_a = _this.realSubject)[message.methodName].apply(_a, message.args); | ||
Promise.resolve(result).then(function (result) { | ||
new Promise(function (resolve) { | ||
return resolve((_a = _this.realSubject)[message.methodName].apply(_a, message.args)); | ||
var _a; | ||
}) | ||
.then(function (result) { | ||
_this.send({ | ||
kind: messageProtocol_1.ParentMessageKind.Result, | ||
correlationId: message.correlationId, | ||
result: result | ||
}); | ||
}).catch(function (error) { | ||
_this.send({ | ||
kind: messageProtocol_1.ParentMessageKind.Rejection, | ||
error: objectUtils_1.errorToString(error), | ||
correlationId: message.correlationId | ||
}); | ||
}); | ||
@@ -43,3 +54,2 @@ _this.removeAnyAdditionalMessageListeners(handler); | ||
} | ||
var _a; | ||
}; | ||
@@ -46,0 +56,0 @@ process.on('message', handler); |
@@ -5,4 +5,11 @@ export declare enum WorkerMessageKind { | ||
} | ||
export declare enum ParentMessageKind { | ||
'Initialized' = 0, | ||
'Result' = 1, | ||
'Rejection' = 2, | ||
} | ||
export declare type WorkerMessage = InitMessage | WorkMessage; | ||
export declare type ParentMessage = WorkResult | 'init_done'; | ||
export declare type ParentMessage = WorkResult | { | ||
kind: ParentMessageKind.Initialized; | ||
} | RejectionResult; | ||
export declare const autoStart = "childProcessAutoStart12937129s7d"; | ||
@@ -17,5 +24,11 @@ export interface InitMessage { | ||
export interface WorkResult { | ||
kind: ParentMessageKind.Result; | ||
correlationId: number; | ||
result: any; | ||
} | ||
export interface RejectionResult { | ||
kind: ParentMessageKind.Rejection; | ||
correlationId: number; | ||
error: string; | ||
} | ||
export interface WorkMessage { | ||
@@ -22,0 +35,0 @@ correlationId: number; |
@@ -8,2 +8,8 @@ "use strict"; | ||
})(WorkerMessageKind = exports.WorkerMessageKind || (exports.WorkerMessageKind = {})); | ||
var ParentMessageKind; | ||
(function (ParentMessageKind) { | ||
ParentMessageKind[ParentMessageKind["Initialized"] = 0] = "Initialized"; | ||
ParentMessageKind[ParentMessageKind["Result"] = 1] = "Result"; | ||
ParentMessageKind[ParentMessageKind["Rejection"] = 2] = "Rejection"; | ||
})(ParentMessageKind = exports.ParentMessageKind || (exports.ParentMessageKind = {})); | ||
// Make this an unlikely command line argument | ||
@@ -10,0 +16,0 @@ // (prevents incidental start of child process) |
@@ -0,0 +0,0 @@ import { Config } from 'stryker-api/config'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { TestFramework } from 'stryker-api/test_framework'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { StatementMap } from 'stryker-api/test_runner'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=FileStatements.js.map |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ import { RestClient } from 'typed-rest-client/RestClient'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ interface PromptOption { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=PromptOption.js.map |
@@ -0,0 +0,0 @@ import { StrykerOptions } from 'stryker-api/core'; |
@@ -30,6 +30,2 @@ "use strict"; | ||
configObject = { | ||
files: [ | ||
{ pattern: 'src/**/*.js', mutated: true, included: false }, | ||
'test/**/*.js' | ||
], | ||
testRunner: selectedTestRunner ? selectedTestRunner.name : '', | ||
@@ -36,0 +32,0 @@ mutator: selectedMutator ? selectedMutator.name : '', |
@@ -0,0 +0,0 @@ import NpmClient from './NpmClient'; |
@@ -57,3 +57,2 @@ "use strict"; | ||
.concat(selectedReporters)); | ||
this.installNpmDependencies(npmDependencies); | ||
_c = (_b = configWriter).write; | ||
@@ -69,3 +68,4 @@ _d = [selectedTestRunner, | ||
_e.sent(); | ||
this.out('Done configuring stryker. Please review `stryker.conf.js`, you might need to configure your files and test runner correctly.'); | ||
this.installNpmDependencies(npmDependencies); | ||
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`'); | ||
@@ -72,0 +72,0 @@ return [2 /*return*/]; |
@@ -0,0 +0,0 @@ import PromptOption from './PromptOption'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { RunnerOptions } from 'stryker-api/test_runner'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=IsolatedRunnerOptions.js.map |
@@ -0,0 +0,0 @@ /// <reference types="node" /> |
@@ -94,3 +94,8 @@ "use strict"; | ||
if (_this.currentTask.kind === 'init') { | ||
_this.currentTask.resolve(undefined); | ||
if (message.errorMessage) { | ||
_this.currentTask.reject(message.errorMessage); | ||
} | ||
else { | ||
_this.currentTask.resolve(undefined); | ||
} | ||
} | ||
@@ -97,0 +102,0 @@ else { |
@@ -64,11 +64,19 @@ "use strict"; | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var err_1; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
if (!this.underlyingTestRunner.init) return [3 /*break*/, 2]; | ||
if (!this.underlyingTestRunner.init) return [3 /*break*/, 4]; | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 3, , 4]); | ||
return [4 /*yield*/, this.underlyingTestRunner.init()]; | ||
case 1: | ||
case 2: | ||
_a.sent(); | ||
_a.label = 2; | ||
case 2: | ||
return [3 /*break*/, 4]; | ||
case 3: | ||
err_1 = _a.sent(); | ||
this.sendInitDone(objectUtils_1.errorToString(err_1)); | ||
return [3 /*break*/, 4]; | ||
case 4: | ||
this.sendInitDone(); | ||
@@ -80,4 +88,5 @@ return [2 /*return*/]; | ||
}; | ||
IsolatedTestRunnerAdapterWorker.prototype.sendInitDone = function () { | ||
var message = { kind: 'initDone' }; | ||
IsolatedTestRunnerAdapterWorker.prototype.sendInitDone = function (errorMessage) { | ||
if (errorMessage === void 0) { errorMessage = null; } | ||
var message = { kind: 'initDone', errorMessage: errorMessage }; | ||
if (process.send) { | ||
@@ -84,0 +93,0 @@ process.send(message); |
@@ -5,3 +5,3 @@ import { RunResult } from 'stryker-api/test_runner'; | ||
export declare type AdapterMessage = RunMessage | StartMessage | EmptyAdapterMessage; | ||
export declare type WorkerMessage = ResultMessage | EmptyWorkerMessage; | ||
export declare type WorkerMessage = ResultMessage | EmptyWorkerMessage | InitDoneMessage; | ||
export interface ResultMessage { | ||
@@ -20,2 +20,6 @@ kind: 'result'; | ||
} | ||
export interface InitDoneMessage { | ||
kind: 'initDone'; | ||
errorMessage: string | null; | ||
} | ||
export interface EmptyAdapterMessage { | ||
@@ -25,3 +29,3 @@ kind: 'init' | 'dispose'; | ||
export interface EmptyWorkerMessage { | ||
kind: 'initDone' | 'disposeDone'; | ||
kind: 'disposeDone'; | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=MessageProtocol.js.map |
@@ -0,0 +0,0 @@ import IsolatedRunnerOptions from './IsolatedRunnerOptions'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { RunOptions, RunResult } from 'stryker-api/test_runner'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -1,5 +0,3 @@ | ||
/// <reference types="node" /> | ||
import { EventEmitter } from 'events'; | ||
import { TestRunner, RunOptions, RunResult } from 'stryker-api/test_runner'; | ||
export default class TestRunnerDecorator extends EventEmitter implements TestRunner { | ||
export default class TestRunnerDecorator implements TestRunner { | ||
private testRunnerProducer; | ||
@@ -6,0 +4,0 @@ protected innerRunner: TestRunner; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tslib_1 = require("tslib"); | ||
var events_1 = require("events"); | ||
var TestRunnerDecorator = /** @class */ (function (_super) { | ||
tslib_1.__extends(TestRunnerDecorator, _super); | ||
var TestRunnerDecorator = /** @class */ (function () { | ||
function TestRunnerDecorator(testRunnerProducer) { | ||
var _this = _super.call(this) || this; | ||
_this.testRunnerProducer = testRunnerProducer; | ||
_this.createInnerRunner(); | ||
return _this; | ||
this.testRunnerProducer = testRunnerProducer; | ||
this.createInnerRunner(); | ||
} | ||
@@ -36,4 +31,4 @@ TestRunnerDecorator.prototype.init = function () { | ||
return TestRunnerDecorator; | ||
}(events_1.EventEmitter)); | ||
}()); | ||
exports.default = TestRunnerDecorator; | ||
//# sourceMappingURL=TestRunnerDecorator.js.map |
@@ -7,2 +7,3 @@ import { RunOptions, RunResult } from 'stryker-api/test_runner'; | ||
export default class TimeoutDecorator extends TestRunnerDecorator { | ||
private readonly log; | ||
run(options: RunOptions): Promise<RunResult>; | ||
@@ -9,0 +10,0 @@ dispose(): Promise<any>; |
@@ -8,2 +8,3 @@ "use strict"; | ||
var TestRunnerDecorator_1 = require("./TestRunnerDecorator"); | ||
var log4js_1 = require("log4js"); | ||
var MAX_WAIT_FOR_DISPOSE = 2500; | ||
@@ -16,6 +17,9 @@ /** | ||
function TimeoutDecorator() { | ||
return _super !== null && _super.apply(this, arguments) || this; | ||
var _this = _super !== null && _super.apply(this, arguments) || this; | ||
_this.log = log4js_1.getLogger(TimeoutDecorator.name); | ||
return _this; | ||
} | ||
TimeoutDecorator.prototype.run = function (options) { | ||
var _this = this; | ||
this.log.debug('Starting timeout timer (%s ms) for a test run', options.timeout); | ||
var runTask = new Task_1.default(options.timeout, function () { return _this.handleTimeout(); }); | ||
@@ -22,0 +26,0 @@ runTask.chainTo(_super.prototype.run.call(this, options)); |
@@ -10,3 +10,3 @@ import { RunResult } from 'stryker-api/test_runner'; | ||
private mutants; | ||
private files; | ||
private filesToMutate; | ||
private initialRunResult; | ||
@@ -18,3 +18,3 @@ private sourceMapper; | ||
private readonly log; | ||
constructor(mutants: Mutant[], files: File[], initialRunResult: RunResult, sourceMapper: SourceMapper, coveragePerFile: CoverageMapsByFile, options: StrykerOptions, reporter: StrictReporter); | ||
constructor(mutants: ReadonlyArray<Mutant>, filesToMutate: ReadonlyArray<File>, initialRunResult: RunResult, sourceMapper: SourceMapper, coveragePerFile: CoverageMapsByFile, options: StrykerOptions, reporter: StrictReporter); | ||
private readonly baseline; | ||
@@ -21,0 +21,0 @@ matchWithMutants(): TestableMutant[]; |
@@ -15,5 +15,5 @@ "use strict"; | ||
var MutantTestMatcher = /** @class */ (function () { | ||
function MutantTestMatcher(mutants, files, initialRunResult, sourceMapper, coveragePerFile, options, reporter) { | ||
function MutantTestMatcher(mutants, filesToMutate, initialRunResult, sourceMapper, coveragePerFile, options, reporter) { | ||
this.mutants = mutants; | ||
this.files = files; | ||
this.filesToMutate = filesToMutate; | ||
this.initialRunResult = initialRunResult; | ||
@@ -46,3 +46,3 @@ this.sourceMapper = sourceMapper; | ||
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(function (mutant) { return mutant.selectAllTests(_this.initialRunResult, TestableMutant_1.TestSelectionResult.FailedButAlreadyReporter); }); | ||
testableMutants.forEach(function (mutant) { return mutant.selectAllTests(_this.initialRunResult, TestableMutant_1.TestSelectionResult.FailedButAlreadyReported); }); | ||
} | ||
@@ -112,3 +112,3 @@ else { | ||
var _this = this; | ||
var sourceFiles = this.files.filter(function (file) { return file.mutated; }).map(function (file) { return new SourceFile_1.default(file); }); | ||
var sourceFiles = this.filesToMutate.map(function (file) { return new SourceFile_1.default(file); }); | ||
return objectUtils_1.filterEmpty(this.mutants.map(function (mutant, index) { | ||
@@ -115,0 +115,0 @@ var sourceFile = sourceFiles.find(function (file) { return file.name === mutant.fileName; }); |
@@ -7,4 +7,4 @@ import { File } from 'stryker-api/core'; | ||
constructor(config: Config); | ||
mutate(inputFiles: File[]): Mutant[]; | ||
mutate(inputFiles: ReadonlyArray<File>): ReadonlyArray<Mutant>; | ||
private getMutatorName(mutator); | ||
} |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -11,3 +11,3 @@ import { Config } from 'stryker-api/config'; | ||
private mutateForFile(file); | ||
private mutateForNodes(sourceFile, nodes); | ||
private mutateForNodes(file, nodes); | ||
} |
@@ -5,3 +5,2 @@ "use strict"; | ||
var log4js_1 = require("log4js"); | ||
var core_1 = require("stryker-api/core"); | ||
var parserUtils = require("../utils/parserUtils"); | ||
@@ -35,17 +34,10 @@ var objectUtils_1 = require("../utils/objectUtils"); | ||
var _this = this; | ||
return _.flatMap(files, function (file) { | ||
if (file.mutated && file.kind === core_1.FileKind.Text) { | ||
return _this.mutateForFile(file); | ||
} | ||
else { | ||
return []; | ||
} | ||
}); | ||
return _.flatMap(files, function (file) { return _this.mutateForFile(file); }); | ||
}; | ||
ES5Mutator.prototype.mutateForFile = function (file) { | ||
var abstractSyntaxTree = parserUtils.parse(file.content); | ||
var abstractSyntaxTree = parserUtils.parse(file.textContent); | ||
var nodes = new parserUtils.NodeIdentifier().identifyAndFreeze(abstractSyntaxTree); | ||
return this.mutateForNodes(file, nodes); | ||
}; | ||
ES5Mutator.prototype.mutateForNodes = function (sourceFile, nodes) { | ||
ES5Mutator.prototype.mutateForNodes = function (file, nodes) { | ||
var _this = this; | ||
@@ -63,3 +55,3 @@ return _.flatMap(nodes, function (astNode) { | ||
if (mutatedNodes.length > 0) { | ||
_this.log.debug("The mutator '" + mutator.name + "' mutated " + mutatedNodes.length + " node" + (mutatedNodes.length > 1 ? 's' : '') + " between (Ln " + astNode.loc.start.line + ", Col " + astNode.loc.start.column + ") and (Ln " + astNode.loc.end.line + ", Col " + astNode.loc.end.column + ") in file " + sourceFile.name); | ||
_this.log.debug("The mutator '" + mutator.name + "' mutated " + mutatedNodes.length + " node" + (mutatedNodes.length > 1 ? 's' : '') + " between (Ln " + astNode.loc.start.line + ", Col " + astNode.loc.start.column + ") and (Ln " + astNode.loc.end.line + ", Col " + astNode.loc.end.column + ") in file " + file.name); | ||
} | ||
@@ -69,3 +61,3 @@ return mutatedNodes.map(function (mutatedNode) { | ||
var originalNode = nodes[mutatedNode.nodeID]; | ||
var mutant = { mutatorName: mutator.name, fileName: sourceFile.name, replacement: replacement, range: originalNode.range }; | ||
var mutant = { mutatorName: mutator.name, fileName: file.name, replacement: replacement, range: originalNode.range }; | ||
return mutant; | ||
@@ -72,0 +64,0 @@ }); |
@@ -0,0 +0,0 @@ import { Node } from 'estree'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=IdentifiedNode.js.map |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { IdentifiedNode } from './IdentifiedNode'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=NodeMutator.js.map |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import NodeMutator from './NodeMutator'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ export default class PluginLoader { |
@@ -0,0 +0,0 @@ "use strict"; |
import { RunResult } from 'stryker-api/test_runner'; | ||
import { TestFramework } from 'stryker-api/test_framework'; | ||
import { Config } from 'stryker-api/config'; | ||
import { File } from 'stryker-api/core'; | ||
import Timer from '../utils/Timer'; | ||
import { CoverageMapsByFile } from '../transpiler/CoverageInstrumenterTranspiler'; | ||
import InputFileCollection from '../input/InputFileCollection'; | ||
import SourceMapper from '../transpiler/SourceMapper'; | ||
export interface InitialTestRunResult { | ||
runResult: RunResult; | ||
transpiledFiles: File[]; | ||
sourceMapper: SourceMapper; | ||
coverageMaps: CoverageMapsByFile; | ||
@@ -14,11 +15,12 @@ } | ||
private options; | ||
private files; | ||
private inputFiles; | ||
private testFramework; | ||
private timer; | ||
private readonly log; | ||
constructor(options: Config, files: File[], testFramework: TestFramework | null, timer: Timer); | ||
constructor(options: Config, inputFiles: InputFileCollection, testFramework: TestFramework | null, timer: Timer); | ||
run(): Promise<InitialTestRunResult>; | ||
private initialRunInSandbox(); | ||
private runInSandbox(files); | ||
private transpileInputFiles(); | ||
private annotateForCodeCoverage(files, sourceMapper); | ||
private validateResult(runResult); | ||
private createDryRunResult(); | ||
/** | ||
@@ -29,5 +31,5 @@ * Creates a facade for the transpile pipeline. | ||
*/ | ||
private createTranspilerFacade(coverageInstrumenterTranspiler); | ||
private createCoverageInstrumenterTranspiler(); | ||
private logTranspileResult(transpileResult); | ||
private createTranspilerFacade(); | ||
private getCollectCoverageHooksIfNeeded(); | ||
private logTranspileResult(transpiledFiles); | ||
private filterOutFailedTests(runResult); | ||
@@ -34,0 +36,0 @@ private logInitialTestRunSucceeded(tests); |
@@ -10,2 +10,4 @@ "use strict"; | ||
var CoverageInstrumenterTranspiler_1 = require("../transpiler/CoverageInstrumenterTranspiler"); | ||
var SourceMapper_1 = require("../transpiler/SourceMapper"); | ||
var coverageHooks_1 = require("../transpiler/coverageHooks"); | ||
// The initial run might take a while. | ||
@@ -16,5 +18,5 @@ // For example: angular-bootstrap takes up to 45 seconds. | ||
var InitialTestExecutor = /** @class */ (function () { | ||
function InitialTestExecutor(options, files, testFramework, timer) { | ||
function InitialTestExecutor(options, inputFiles, testFramework, timer) { | ||
this.options = options; | ||
this.files = files; | ||
this.inputFiles = inputFiles; | ||
this.testFramework = testFramework; | ||
@@ -26,16 +28,24 @@ this.timer = timer; | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var result; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
var transpiledFiles, sourceMapper, _a, coverageMaps, instrumentedFiles, runResult; | ||
return tslib_1.__generator(this, function (_b) { | ||
switch (_b.label) { | ||
case 0: | ||
if (!(this.files.length > 0)) return [3 /*break*/, 2]; | ||
this.log.info("Starting initial test run. This may take a while."); | ||
return [4 /*yield*/, this.initialRunInSandbox()]; | ||
return [4 /*yield*/, this.transpileInputFiles()]; | ||
case 1: | ||
result = _a.sent(); | ||
this.validateResult(result.runResult); | ||
return [2 /*return*/, result]; | ||
transpiledFiles = _b.sent(); | ||
sourceMapper = SourceMapper_1.default.create(transpiledFiles, this.options); | ||
return [4 /*yield*/, this.annotateForCodeCoverage(transpiledFiles, sourceMapper)]; | ||
case 2: | ||
this.log.info("No files have been found. Aborting initial test run."); | ||
return [2 /*return*/, this.createDryRunResult()]; | ||
_a = _b.sent(), coverageMaps = _a.coverageMaps, instrumentedFiles = _a.instrumentedFiles; | ||
this.logTranspileResult(instrumentedFiles); | ||
return [4 /*yield*/, this.runInSandbox(instrumentedFiles)]; | ||
case 3: | ||
runResult = _b.sent(); | ||
this.validateResult(runResult); | ||
return [2 /*return*/, { | ||
sourceMapper: sourceMapper, | ||
runResult: runResult, | ||
coverageMaps: coverageMaps | ||
}]; | ||
} | ||
@@ -45,31 +55,17 @@ }); | ||
}; | ||
InitialTestExecutor.prototype.initialRunInSandbox = function () { | ||
InitialTestExecutor.prototype.runInSandbox = function (files) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var coverageInstrumenterTranspiler, transpilerFacade, transpileResult, sandbox_1, runResult; | ||
var sandbox, runResult; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
coverageInstrumenterTranspiler = this.createCoverageInstrumenterTranspiler(); | ||
transpilerFacade = this.createTranspilerFacade(coverageInstrumenterTranspiler); | ||
return [4 /*yield*/, transpilerFacade.transpile(this.files)]; | ||
case 0: return [4 /*yield*/, Sandbox_1.default.create(this.options, 0, files, this.testFramework)]; | ||
case 1: | ||
transpileResult = _a.sent(); | ||
if (!transpileResult.error) return [3 /*break*/, 2]; | ||
throw new Error("Could not transpile input files: " + transpileResult.error); | ||
sandbox = _a.sent(); | ||
return [4 /*yield*/, sandbox.run(INITIAL_RUN_TIMEOUT, this.getCollectCoverageHooksIfNeeded())]; | ||
case 2: | ||
this.logTranspileResult(transpileResult); | ||
return [4 /*yield*/, Sandbox_1.default.create(this.options, 0, transpileResult.outputFiles, this.testFramework)]; | ||
runResult = _a.sent(); | ||
return [4 /*yield*/, sandbox.dispose()]; | ||
case 3: | ||
sandbox_1 = _a.sent(); | ||
return [4 /*yield*/, sandbox_1.run(INITIAL_RUN_TIMEOUT)]; | ||
case 4: | ||
runResult = _a.sent(); | ||
return [4 /*yield*/, sandbox_1.dispose()]; | ||
case 5: | ||
_a.sent(); | ||
return [2 /*return*/, { | ||
runResult: runResult, | ||
transpiledFiles: transpileResult.outputFiles, | ||
coverageMaps: coverageInstrumenterTranspiler.fileCoverageMaps | ||
}]; | ||
return [2 /*return*/, runResult]; | ||
} | ||
@@ -79,2 +75,31 @@ }); | ||
}; | ||
InitialTestExecutor.prototype.transpileInputFiles = function () { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var transpilerFacade; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
transpilerFacade = this.createTranspilerFacade(); | ||
return [4 /*yield*/, transpilerFacade.transpile(this.inputFiles.files)]; | ||
case 1: return [2 /*return*/, _a.sent()]; | ||
} | ||
}); | ||
}); | ||
}; | ||
InitialTestExecutor.prototype.annotateForCodeCoverage = function (files, sourceMapper) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var filesToInstrument, coverageInstrumenterTranspiler, instrumentedFiles; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
filesToInstrument = this.inputFiles.filesToMutate.map(function (mutateFile) { return sourceMapper.transpiledFileNameFor(mutateFile.name); }); | ||
coverageInstrumenterTranspiler = new CoverageInstrumenterTranspiler_1.default(this.options, filesToInstrument); | ||
return [4 /*yield*/, coverageInstrumenterTranspiler.transpile(files)]; | ||
case 1: | ||
instrumentedFiles = _a.sent(); | ||
return [2 /*return*/, { coverageMaps: coverageInstrumenterTranspiler.fileCoverageMaps, instrumentedFiles: instrumentedFiles }]; | ||
} | ||
}); | ||
}); | ||
}; | ||
InitialTestExecutor.prototype.validateResult = function (runResult) { | ||
@@ -105,13 +130,2 @@ switch (runResult.status) { | ||
}; | ||
InitialTestExecutor.prototype.createDryRunResult = function () { | ||
return { | ||
runResult: { | ||
status: test_runner_1.RunStatus.Complete, | ||
tests: [], | ||
errorMessages: [] | ||
}, | ||
transpiledFiles: [], | ||
coverageMaps: Object.create(null) | ||
}; | ||
}; | ||
/** | ||
@@ -122,3 +136,3 @@ * Creates a facade for the transpile pipeline. | ||
*/ | ||
InitialTestExecutor.prototype.createTranspilerFacade = function (coverageInstrumenterTranspiler) { | ||
InitialTestExecutor.prototype.createTranspilerFacade = function () { | ||
// Let the transpiler produce source maps only if coverage analysis is enabled | ||
@@ -129,13 +143,20 @@ var transpilerSettings = { | ||
}; | ||
return new TranspilerFacade_1.default(transpilerSettings, { | ||
name: CoverageInstrumenterTranspiler_1.default.name, | ||
transpiler: coverageInstrumenterTranspiler | ||
}); | ||
return new TranspilerFacade_1.default(transpilerSettings); | ||
}; | ||
InitialTestExecutor.prototype.createCoverageInstrumenterTranspiler = function () { | ||
return new CoverageInstrumenterTranspiler_1.default({ produceSourceMaps: true, config: this.options }, this.testFramework); | ||
InitialTestExecutor.prototype.getCollectCoverageHooksIfNeeded = function () { | ||
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\"."); | ||
return coverageHooks_1.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.'); | ||
} | ||
} | ||
return undefined; | ||
}; | ||
InitialTestExecutor.prototype.logTranspileResult = function (transpileResult) { | ||
InitialTestExecutor.prototype.logTranspileResult = function (transpiledFiles) { | ||
if (this.options.transpilers.length && this.log.isDebugEnabled()) { | ||
this.log.debug("Transpiled files in order:" + os_1.EOL + transpileResult.outputFiles.map(function (f) { return f.name + " (included: " + f.included + ")"; }).join(os_1.EOL)); | ||
this.log.debug("Transpiled files: " + JSON.stringify(transpiledFiles.map(function (f) { return "" + f.name; }), null, 2)); | ||
} | ||
@@ -142,0 +163,0 @@ }; |
@@ -12,5 +12,5 @@ import { MutantResult } from 'stryker-api/report'; | ||
private reporter; | ||
constructor(config: Config, inputFiles: File[], testFramework: TestFramework | null, reporter: StrictReporter); | ||
constructor(config: Config, inputFiles: ReadonlyArray<File>, testFramework: TestFramework | null, reporter: StrictReporter); | ||
run(allMutants: TestableMutant[]): Promise<MutantResult[]>; | ||
private runInsideSandboxes(sandboxes, transpiledMutants); | ||
} |
@@ -18,3 +18,3 @@ "use strict"; | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var mutantTranspiler, transpileResult, sandboxPool, result; | ||
var mutantTranspiler, transpiledFiles, sandboxPool, result; | ||
return tslib_1.__generator(this, function (_a) { | ||
@@ -26,4 +26,4 @@ switch (_a.label) { | ||
case 1: | ||
transpileResult = _a.sent(); | ||
sandboxPool = new SandboxPool_1.default(this.config, this.testFramework, transpileResult.outputFiles); | ||
transpiledFiles = _a.sent(); | ||
sandboxPool = new SandboxPool_1.default(this.config, this.testFramework, transpiledFiles); | ||
return [4 /*yield*/, this.runInsideSandboxes(sandboxPool.streamSandboxes(), mutantTranspiler.transpileMutants(allMutants))]; | ||
@@ -30,0 +30,0 @@ case 2: |
@@ -0,0 +0,0 @@ import { StrykerOptions } from 'stryker-api/core'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Reporter, SourceFile, MutantResult, MatchedMutant, ScoreResult } from 'stryker-api/report'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Reporter, MutantResult, ScoreResult } from 'stryker-api/report'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { MutationScoreThresholds } from 'stryker-api/core'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { HttpClient } from 'typed-rest-client/HttpClient'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Reporter, ScoreResult } from 'stryker-api/report'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Reporter, MutantResult } from 'stryker-api/report'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { StrykerOptions } from 'stryker-api/core'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { MatchedMutant } from 'stryker-api/report'; |
@@ -15,4 +15,6 @@ "use strict"; | ||
_super.prototype.onAllMutantsMatchedWithTests.call(this, matchedMutants); | ||
this.timer = new Timer_1.default(); | ||
this.intervalReference = setInterval(function () { return _this.render(); }, 10000); | ||
if (matchedMutants.length) { | ||
this.timer = new Timer_1.default(); | ||
this.intervalReference = setInterval(function () { return _this.render(); }, 10000); | ||
} | ||
}; | ||
@@ -19,0 +21,0 @@ ProgressAppendOnlyReporter.prototype.onAllMutantsTested = function () { |
import ProgressBar = require('progress'); | ||
export default ProgressBar; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { MatchedMutant, Reporter, MutantResult } from 'stryker-api/report'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { MatchedMutant, MutantResult } from 'stryker-api/report'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { Reporter, SourceFile, MutantResult, MatchedMutant, ScoreResult } from 'stryker-api/report'; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
//# sourceMappingURL=StrictReporter.js.map |
@@ -15,7 +15,6 @@ import { Config } from 'stryker-api/config'; | ||
private workingFolder; | ||
private testHooksFile; | ||
private constructor(); | ||
private initialize(); | ||
static create(options: Config, index: number, files: ReadonlyArray<File>, testFramework: TestFramework | null): Promise<Sandbox>; | ||
run(timeout: number): Promise<RunResult>; | ||
run(timeout: number, testHooks: string | undefined): Promise<RunResult>; | ||
dispose(): Promise<void>; | ||
@@ -29,3 +28,3 @@ runMutant(transpiledMutant: TranspiledMutant): Promise<RunResult>; | ||
private calculateTimeout(mutant); | ||
private filterTests(mutant); | ||
private getFilterTestsHooks(mutant); | ||
} |
@@ -7,7 +7,6 @@ "use strict"; | ||
var mkdirp = require("mkdirp"); | ||
var core_1 = require("stryker-api/core"); | ||
var objectUtils_1 = require("./utils/objectUtils"); | ||
var ResilientTestRunnerFactory_1 = require("./isolated-runner/ResilientTestRunnerFactory"); | ||
var TempFolder_1 = require("./utils/TempFolder"); | ||
var fileUtils = require("./utils/fileUtils"); | ||
var fileUtils_1 = require("./utils/fileUtils"); | ||
var TestableMutant_1 = require("./TestableMutant"); | ||
@@ -20,17 +19,5 @@ var Sandbox = /** @class */ (function () { | ||
this.log = log4js_1.getLogger(Sandbox.name); | ||
this.testHooksFile = path.resolve('___testHooksForStryker.js'); | ||
this.workingFolder = TempFolder_1.TempFolder.instance().createRandomFolder('sandbox'); | ||
this.log.debug('Creating a sandbox for files in %s', this.workingFolder); | ||
this.files = files.slice(); // Create a copy | ||
if (testFramework) { | ||
this.testHooksFile = path.resolve('___testHooksForStryker.js'); | ||
this.files.unshift({ | ||
name: this.testHooksFile, | ||
content: '', | ||
mutated: false, | ||
included: true, | ||
transpiled: false, | ||
kind: core_1.FileKind.Text | ||
}); | ||
} | ||
} | ||
@@ -53,4 +40,4 @@ Sandbox.prototype.initialize = function () { | ||
}; | ||
Sandbox.prototype.run = function (timeout) { | ||
return this.testRunner.run({ timeout: timeout }); | ||
Sandbox.prototype.run = function (timeout, testHooks) { | ||
return this.testRunner.run({ timeout: timeout, testHooks: testHooks }); | ||
}; | ||
@@ -71,6 +58,6 @@ Sandbox.prototype.dispose = function () { | ||
} | ||
return [4 /*yield*/, Promise.all(mutantFiles.map(function (mutatedFile) { return _this.writeFileInSandbox(mutatedFile); }).concat(this.filterTests(transpiledMutant.mutant)))]; | ||
return [4 /*yield*/, Promise.all(mutantFiles.map(function (mutatedFile) { return _this.writeFileInSandbox(mutatedFile); }))]; | ||
case 1: | ||
_a.sent(); | ||
return [4 /*yield*/, this.run(this.calculateTimeout(transpiledMutant.mutant))]; | ||
return [4 /*yield*/, this.run(this.calculateTimeout(transpiledMutant.mutant), this.getFilterTestsHooks(transpiledMutant.mutant))]; | ||
case 2: | ||
@@ -89,19 +76,7 @@ runResult = _a.sent(); | ||
var originalFiles = this.files.filter(function (originalFile) { return mutatedFiles.some(function (mutatedFile) { return mutatedFile.name === originalFile.name; }); }); | ||
return Promise.all(originalFiles.map(function (file) { | ||
if (file.kind !== core_1.FileKind.Web) { | ||
return fileUtils.writeFile(_this.fileMap[file.name], file.content); | ||
} | ||
else { | ||
return Promise.resolve(); | ||
} | ||
})); | ||
return Promise.all(originalFiles.map(function (file) { return fileUtils_1.writeFile(_this.fileMap[file.name], file.content); })); | ||
}; | ||
Sandbox.prototype.writeFileInSandbox = function (file) { | ||
switch (file.kind) { | ||
case core_1.FileKind.Web: | ||
return Promise.resolve(); | ||
default: | ||
var fileNameInSandbox = this.fileMap[file.name]; | ||
return fileUtils.writeFile(fileNameInSandbox, file.content); | ||
} | ||
var fileNameInSandbox = this.fileMap[file.name]; | ||
return fileUtils_1.writeFile(fileNameInSandbox, file.content); | ||
}; | ||
@@ -116,27 +91,13 @@ Sandbox.prototype.fillSandbox = function () { | ||
Sandbox.prototype.fillFile = function (file) { | ||
switch (file.kind) { | ||
case core_1.FileKind.Web: | ||
this.fileMap[file.name] = file.name; | ||
return Promise.resolve(); | ||
default: | ||
var cwd = process.cwd(); | ||
var relativePath = path.relative(cwd, file.name); | ||
var folderName = path.join(this.workingFolder, path.dirname(relativePath)); | ||
mkdirp.sync(folderName); | ||
var targetFile = path.join(folderName, path.basename(relativePath)); | ||
this.fileMap[file.name] = targetFile; | ||
return fileUtils.writeFile(targetFile, file.content); | ||
} | ||
var relativePath = path.relative(process.cwd(), file.name); | ||
var folderName = path.join(this.workingFolder, path.dirname(relativePath)); | ||
mkdirp.sync(folderName); | ||
var targetFile = path.join(folderName, path.basename(relativePath)); | ||
this.fileMap[file.name] = targetFile; | ||
return fileUtils_1.writeFile(targetFile, file.content); | ||
}; | ||
Sandbox.prototype.initializeTestRunner = function () { | ||
var _this = this; | ||
var files = this.files.map(function (originalFile) { return ({ | ||
name: _this.fileMap[originalFile.name], | ||
mutated: originalFile.mutated, | ||
included: originalFile.included, | ||
kind: originalFile.kind, | ||
transpiled: originalFile.transpiled | ||
}); }); | ||
var settings = { | ||
files: files, | ||
fileNames: Object.keys(this.fileMap).map(function (sourceFileName) { return _this.fileMap[sourceFileName]; }), | ||
strykerOptions: this.options, | ||
@@ -154,9 +115,8 @@ port: this.options.port + this.index, | ||
}; | ||
Sandbox.prototype.filterTests = function (mutant) { | ||
Sandbox.prototype.getFilterTestsHooks = function (mutant) { | ||
if (this.testFramework) { | ||
var fileContent = objectUtils_1.wrapInClosure(this.testFramework.filter(mutant.selectedTests)); | ||
return fileUtils.writeFile(this.fileMap[this.testHooksFile], fileContent); | ||
return objectUtils_1.wrapInClosure(this.testFramework.filter(mutant.selectedTests)); | ||
} | ||
else { | ||
return Promise.resolve(void 0); | ||
return undefined; | ||
} | ||
@@ -163,0 +123,0 @@ }; |
@@ -13,3 +13,3 @@ import { Observable } from 'rxjs'; | ||
private isDisposed; | ||
constructor(options: Config, testFramework: TestFramework | null, initialFiles: File[]); | ||
constructor(options: Config, testFramework: TestFramework | null, initialFiles: ReadonlyArray<File>); | ||
streamSandboxes(): Observable<Sandbox>; | ||
@@ -16,0 +16,0 @@ private registerSandbox(promisedSandbox); |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ import { MutationScoreThresholds } from 'stryker-api/core'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -1,7 +0,7 @@ | ||
import { TextFile, Range, Location, Position } from 'stryker-api/core'; | ||
import { File, Range, Location, Position } from 'stryker-api/core'; | ||
export declare function isLineBreak(ch: number): boolean; | ||
export default class SourceFile { | ||
file: TextFile; | ||
file: File; | ||
private lineStarts; | ||
constructor(file: TextFile); | ||
constructor(file: File); | ||
readonly name: string; | ||
@@ -8,0 +8,0 @@ readonly content: string; |
@@ -34,3 +34,3 @@ "use strict"; | ||
get: function () { | ||
return this.file.content; | ||
return this.file.textContent; | ||
}, | ||
@@ -93,8 +93,8 @@ enumerable: true, | ||
var lineStart = 0; | ||
while (pos < this.file.content.length) { | ||
var ch = this.file.content.charCodeAt(pos); | ||
while (pos < this.file.textContent.length) { | ||
var ch = this.file.textContent.charCodeAt(pos); | ||
pos++; | ||
switch (ch) { | ||
case 13 /* carriageReturn */: | ||
if (this.file.content.charCodeAt(pos) === 10 /* lineFeed */) { | ||
if (this.file.textContent.charCodeAt(pos) === 10 /* lineFeed */) { | ||
pos++; | ||
@@ -101,0 +101,0 @@ } |
@@ -17,3 +17,3 @@ import { Config } from 'stryker-api/config'; | ||
runMutationTest(): Promise<MutantResult[]>; | ||
private mutate(inputFiles, initialTestRunResult); | ||
private mutate(input, initialTestRunResult); | ||
private logMutantCount(includedMutantCount, totalMutantCount); | ||
@@ -27,5 +27,3 @@ private removeExcludedMutants(mutants); | ||
private setGlobalLogLevel(); | ||
private createMutationTester(inputFiles); | ||
private createInitialTestRunProcess(inputFiles); | ||
private reportScore(mutantResults); | ||
} |
@@ -8,3 +8,3 @@ "use strict"; | ||
var MutantTestMatcher_1 = require("./MutantTestMatcher"); | ||
var InputFileResolver_1 = require("./InputFileResolver"); | ||
var InputFileResolver_1 = require("./input/InputFileResolver"); | ||
var ConfigReader_1 = require("./ConfigReader"); | ||
@@ -21,3 +21,2 @@ var PluginLoader_1 = require("./PluginLoader"); | ||
var MutationTestExecutor_1 = require("./process/MutationTestExecutor"); | ||
var SourceMapper_1 = require("./transpiler/SourceMapper"); | ||
var Stryker = /** @class */ (function () { | ||
@@ -53,4 +52,5 @@ /** | ||
inputFiles = _a.sent(); | ||
if (!inputFiles.files.length) return [3 /*break*/, 8]; | ||
TempFolder_1.TempFolder.instance().initialize(); | ||
initialTestRunProcess = this.createInitialTestRunProcess(inputFiles); | ||
initialTestRunProcess = new InitialTestExecutor_1.default(this.config, inputFiles, this.testFramework, this.timer); | ||
return [4 /*yield*/, initialTestRunProcess.run()]; | ||
@@ -63,3 +63,3 @@ case 2: | ||
if (!(initialTestRunResult.runResult.tests.length && testableMutants.length)) return [3 /*break*/, 8]; | ||
mutationTestExecutor = this.createMutationTester(inputFiles); | ||
mutationTestExecutor = new MutationTestExecutor_1.default(this.config, inputFiles.files, this.testFramework, this.reporter); | ||
return [4 /*yield*/, mutationTestExecutor.run(testableMutants)]; | ||
@@ -84,8 +84,8 @@ case 4: | ||
}; | ||
Stryker.prototype.mutate = function (inputFiles, initialTestRunResult) { | ||
Stryker.prototype.mutate = function (input, initialTestRunResult) { | ||
var mutator = new MutatorFacade_1.default(this.config); | ||
var allMutants = mutator.mutate(inputFiles); | ||
var allMutants = mutator.mutate(input.filesToMutate); | ||
var includedMutants = this.removeExcludedMutants(allMutants); | ||
this.logMutantCount(includedMutants.length, allMutants.length); | ||
var mutantRunResultMatcher = new MutantTestMatcher_1.default(includedMutants, inputFiles, initialTestRunResult.runResult, SourceMapper_1.default.create(initialTestRunResult.transpiledFiles, this.config), initialTestRunResult.coverageMaps, this.config, this.reporter); | ||
var mutantRunResultMatcher = new MutantTestMatcher_1.default(includedMutants, input.filesToMutate, initialTestRunResult.runResult, initialTestRunResult.sourceMapper, initialTestRunResult.coverageMaps, this.config, this.reporter); | ||
return mutantRunResultMatcher.matchWithMutants(); | ||
@@ -137,3 +137,10 @@ }; | ||
Stryker.prototype.freezeConfig = function () { | ||
objectUtils_1.freezeRecursively(this.config); | ||
// A config class instance is not serializable using surrial. | ||
// This is a temporary work around | ||
// See https://github.com/stryker-mutator/stryker/issues/365 | ||
var config = {}; | ||
for (var prop in this.config) { | ||
config[prop] = this.config[prop]; | ||
} | ||
this.config = objectUtils_1.freezeRecursively(config); | ||
if (this.log.isDebugEnabled()) { | ||
@@ -149,8 +156,2 @@ this.log.debug("Using config: " + JSON.stringify(this.config)); | ||
}; | ||
Stryker.prototype.createMutationTester = function (inputFiles) { | ||
return new MutationTestExecutor_1.default(this.config, inputFiles, this.testFramework, this.reporter); | ||
}; | ||
Stryker.prototype.createInitialTestRunProcess = function (inputFiles) { | ||
return new InitialTestExecutor_1.default(this.config, inputFiles, this.testFramework, this.timer); | ||
}; | ||
Stryker.prototype.reportScore = function (mutantResults) { | ||
@@ -157,0 +158,0 @@ var calculator = new ScoreResultCalculator_1.default(); |
@@ -0,0 +0,0 @@ export default class StrykerCli { |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -9,3 +9,3 @@ import { Location } from 'stryker-api/core'; | ||
Failed = 0, | ||
FailedButAlreadyReporter = 1, | ||
FailedButAlreadyReported = 1, | ||
Success = 2, | ||
@@ -25,3 +25,2 @@ } | ||
readonly fileName: string; | ||
readonly included: boolean; | ||
readonly mutatorName: string; | ||
@@ -28,0 +27,0 @@ readonly range: [number, number]; |
@@ -8,3 +8,3 @@ "use strict"; | ||
TestSelectionResult[TestSelectionResult["Failed"] = 0] = "Failed"; | ||
TestSelectionResult[TestSelectionResult["FailedButAlreadyReporter"] = 1] = "FailedButAlreadyReporter"; | ||
TestSelectionResult[TestSelectionResult["FailedButAlreadyReported"] = 1] = "FailedButAlreadyReported"; | ||
TestSelectionResult[TestSelectionResult["Success"] = 2] = "Success"; | ||
@@ -43,9 +43,2 @@ })(TestSelectionResult = exports.TestSelectionResult || (exports.TestSelectionResult = {})); | ||
}); | ||
Object.defineProperty(TestableMutant.prototype, "included", { | ||
get: function () { | ||
return this.sourceFile.file.included; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
Object.defineProperty(TestableMutant.prototype, "mutatorName", { | ||
@@ -52,0 +45,0 @@ get: function () { |
@@ -0,0 +0,0 @@ import { TestFramework } from 'stryker-api/test_framework'; |
@@ -0,0 +0,0 @@ "use strict"; |
import TestableMutant from './TestableMutant'; | ||
import { TranspileResult } from 'stryker-api/transpile'; | ||
import { File } from 'stryker-api/core'; | ||
import TranspileResult from './transpiler/TranspileResult'; | ||
export default class TranspiledMutant { | ||
@@ -15,3 +14,2 @@ mutant: TestableMutant; | ||
constructor(mutant: TestableMutant, transpileResult: TranspileResult, changedAnyTranspiledFiles: boolean); | ||
static create(mutant: TestableMutant, transpileResult: TranspileResult, unMutatedFiles: File[]): TranspiledMutant; | ||
} |
@@ -15,17 +15,2 @@ "use strict"; | ||
} | ||
TranspiledMutant.create = function (mutant, transpileResult, unMutatedFiles) { | ||
return new TranspiledMutant(mutant, transpileResult, someFilesChanged()); | ||
function someFilesChanged() { | ||
return transpileResult.outputFiles.some(function (file) { return fileChanged(file); }); | ||
} | ||
function fileChanged(file) { | ||
if (unMutatedFiles) { | ||
var unMutatedFile = unMutatedFiles.find(function (f) { return f.name === file.name; }); | ||
return !unMutatedFile || unMutatedFile.content !== file.content; | ||
} | ||
else { | ||
return true; | ||
} | ||
} | ||
}; | ||
return TranspiledMutant; | ||
@@ -32,0 +17,0 @@ }()); |
@@ -1,4 +0,3 @@ | ||
import { Transpiler, TranspileResult, TranspilerOptions } from 'stryker-api/transpile'; | ||
import { File } from 'stryker-api/core'; | ||
import { TestFramework } from 'stryker-api/test_framework'; | ||
import { Transpiler } from 'stryker-api/transpile'; | ||
import { StrykerOptions, File } from 'stryker-api/core'; | ||
import { Range } from 'istanbul-lib-coverage'; | ||
@@ -18,8 +17,7 @@ export interface CoverageMaps { | ||
private settings; | ||
private testFramework; | ||
private filesToInstrument; | ||
private instrumenter; | ||
fileCoverageMaps: CoverageMapsByFile; | ||
private log; | ||
constructor(settings: TranspilerOptions, testFramework: TestFramework | null); | ||
transpile(files: File[]): Promise<TranspileResult>; | ||
constructor(settings: StrykerOptions, filesToInstrument: ReadonlyArray<string>); | ||
transpile(files: ReadonlyArray<File>): Promise<ReadonlyArray<File>>; | ||
/** | ||
@@ -39,5 +37,2 @@ * Coverage variable *must* have the name '__coverage__'. Only that variable | ||
private retrieveCoverageMaps(input); | ||
private addCollectCoverageFileIfNeeded(result); | ||
private coveragePerTestFileContent(testFramework); | ||
private errorResult(error); | ||
} |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tslib_1 = require("tslib"); | ||
var istanbul_lib_instrument_1 = require("istanbul-lib-instrument"); | ||
var core_1 = require("stryker-api/core"); | ||
var istanbul_lib_instrument_1 = require("istanbul-lib-instrument"); | ||
var objectUtils_1 = require("../utils/objectUtils"); | ||
var log4js_1 = require("log4js"); | ||
var COVERAGE_CURRENT_TEST_VARIABLE_NAME = '__strykerCoverageCurrentTest__'; | ||
var coverageHooks_1 = require("./coverageHooks"); | ||
var StrykerError_1 = require("../utils/StrykerError"); | ||
var CoverageInstrumenterTranspiler = /** @class */ (function () { | ||
function CoverageInstrumenterTranspiler(settings, testFramework) { | ||
function CoverageInstrumenterTranspiler(settings, filesToInstrument) { | ||
this.settings = settings; | ||
this.testFramework = testFramework; | ||
this.filesToInstrument = filesToInstrument; | ||
this.fileCoverageMaps = Object.create(null); | ||
this.instrumenter = istanbul_lib_instrument_1.createInstrumenter({ coverageVariable: this.coverageVariable, preserveComments: true }); | ||
this.log = log4js_1.getLogger(CoverageInstrumenterTranspiler.name); | ||
} | ||
CoverageInstrumenterTranspiler.prototype.transpile = function (files) { | ||
var _this = this; | ||
try { | ||
var result = { | ||
outputFiles: files.map(function (file) { return _this.instrumentFileIfNeeded(file); }), | ||
error: null | ||
}; | ||
return Promise.resolve(this.addCollectCoverageFileIfNeeded(result)); | ||
} | ||
catch (error) { | ||
return Promise.resolve(this.errorResult(objectUtils_1.errorToString(error))); | ||
} | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var _this = this; | ||
return tslib_1.__generator(this, function (_a) { | ||
return [2 /*return*/, files.map(function (file) { return _this.instrumentFileIfNeeded(file); })]; | ||
}); | ||
}); | ||
}; | ||
@@ -40,5 +34,5 @@ Object.defineProperty(CoverageInstrumenterTranspiler.prototype, "coverageVariable", { | ||
get: function () { | ||
switch (this.settings.config.coverageAnalysis) { | ||
switch (this.settings.coverageAnalysis) { | ||
case 'perTest': | ||
return COVERAGE_CURRENT_TEST_VARIABLE_NAME; | ||
return coverageHooks_1.COVERAGE_CURRENT_TEST_VARIABLE_NAME; | ||
default: | ||
@@ -71,3 +65,3 @@ return '__coverage__'; | ||
CoverageInstrumenterTranspiler.prototype.instrumentFileIfNeeded = function (file) { | ||
if (this.settings.config.coverageAnalysis !== 'off' && file.kind === core_1.FileKind.Text && file.mutated) { | ||
if (this.settings.coverageAnalysis !== 'off' && this.filesToInstrument.some(function (fileName) { return fileName === file.name; })) { | ||
return this.instrumentFile(file); | ||
@@ -81,16 +75,9 @@ } | ||
try { | ||
var content = this.instrumenter.instrumentSync(sourceFile.content, sourceFile.name); | ||
var content = this.instrumenter.instrumentSync(sourceFile.textContent, sourceFile.name); | ||
var fileCoverage = this.patchRanges(this.instrumenter.lastFileCoverage()); | ||
this.fileCoverageMaps[sourceFile.name] = this.retrieveCoverageMaps(fileCoverage); | ||
return { | ||
mutated: sourceFile.mutated, | ||
included: sourceFile.included, | ||
name: sourceFile.name, | ||
transpiled: sourceFile.transpiled, | ||
kind: core_1.FileKind.Text, | ||
content: content | ||
}; | ||
return new core_1.File(sourceFile.name, Buffer.from(content)); | ||
} | ||
catch (error) { | ||
throw new Error("Could not instrument \"" + sourceFile.name + "\" for code coverage. " + objectUtils_1.errorToString(error)); | ||
throw new StrykerError_1.default("Could not instrument \"" + sourceFile.name + "\" for code coverage", error); | ||
} | ||
@@ -106,39 +93,5 @@ }; | ||
}; | ||
CoverageInstrumenterTranspiler.prototype.addCollectCoverageFileIfNeeded = function (result) { | ||
if (Object.keys(this.fileCoverageMaps).length && this.settings.config.coverageAnalysis === 'perTest') { | ||
if (this.testFramework) { | ||
// Add piece of javascript to collect coverage per test results | ||
var content = this.coveragePerTestFileContent(this.testFramework); | ||
var fileName = '____collectCoveragePerTest____.js'; | ||
result.outputFiles.unshift({ | ||
kind: core_1.FileKind.Text, | ||
name: fileName, | ||
included: true, | ||
transpiled: false, | ||
mutated: false, | ||
content: content | ||
}); | ||
this.log.debug("Adding test hooks file for coverageAnalysis \"perTest\": " + fileName); | ||
} | ||
else { | ||
return this.errorResult('Cannot measure coverage results per test, there is no testFramework and thus no way of executing code right before and after each test.'); | ||
} | ||
} | ||
return result; | ||
}; | ||
CoverageInstrumenterTranspiler.prototype.coveragePerTestFileContent = function (testFramework) { | ||
return objectUtils_1.wrapInClosure("\n var id = 0, globalCoverage, coverageResult;\n window.__coverage__ = globalCoverage = { deviations: {} };\n " + testFramework.beforeEach(beforeEachFragmentPerTest) + "\n " + testFramework.afterEach(afterEachFragmentPerTest) + "\n " + cloneFunctionFragment + ";\n "); | ||
}; | ||
CoverageInstrumenterTranspiler.prototype.errorResult = function (error) { | ||
return { | ||
error: error, | ||
outputFiles: [] | ||
}; | ||
}; | ||
return CoverageInstrumenterTranspiler; | ||
}()); | ||
exports.default = CoverageInstrumenterTranspiler; | ||
var cloneFunctionFragment = " \nfunction clone(source) {\n var result = source;\n if (Array.isArray(source)) {\n result = [];\n source.forEach(function (child, index) {\n result[index] = clone(child);\n });\n } else if (typeof source == \"object\") {\n result = {};\n for (var i in source) {\n result[i] = clone(source[i]);\n }\n }\n return result;\n}"; | ||
var beforeEachFragmentPerTest = "\nif (!globalCoverage.baseline && window." + COVERAGE_CURRENT_TEST_VARIABLE_NAME + ") {\nglobalCoverage.baseline = clone(window." + COVERAGE_CURRENT_TEST_VARIABLE_NAME + ");\n}"; | ||
var afterEachFragmentPerTest = "\nglobalCoverage.deviations[id] = coverageResult = {};\nid++;\nvar coveragePerFile = window." + COVERAGE_CURRENT_TEST_VARIABLE_NAME + ";\nif(coveragePerFile) {\nObject.keys(coveragePerFile).forEach(function (file) {\n var coverage = coveragePerFile[file];\n var baseline = globalCoverage.baseline[file];\n var fileResult = { s: {}, f: {} };\n var touchedFile = false;\n for(var i in coverage.s){\n if(coverage.s[i] !== baseline.s[i]){\n fileResult.s[i] = coverage.s[i];\n touchedFile = true;\n }\n }\n for(var i in coverage.f){\n if(coverage.f[i] !== baseline.f[i]){\n fileResult.f[i] = coverage.f[i];\n touchedFile = true;\n }\n }\n if(touchedFile){\n coverageResult[file] = fileResult;\n }\n});\n}"; | ||
//# sourceMappingURL=CoverageInstrumenterTranspiler.js.map |
@@ -5,3 +5,2 @@ import { Observable } from 'rxjs'; | ||
import { File } from 'stryker-api/core'; | ||
import { TranspileResult } from 'stryker-api/transpile'; | ||
import TranspiledMutant from '../TranspiledMutant'; | ||
@@ -19,6 +18,7 @@ export default class MutantTranspiler { | ||
constructor(config: Config); | ||
initialize(files: File[]): Promise<TranspileResult>; | ||
initialize(files: ReadonlyArray<File>): Promise<ReadonlyArray<File>>; | ||
transpileMutants(allMutants: TestableMutant[]): Observable<TranspiledMutant>; | ||
dispose(): void; | ||
private createTranspiledMutant(mutant, transpileResult, unMutatedFiles); | ||
private transpileMutant(mutant); | ||
} |
@@ -8,2 +8,3 @@ "use strict"; | ||
var TranspiledMutant_1 = require("../TranspiledMutant"); | ||
var objectUtils_1 = require("../utils/objectUtils"); | ||
var MutantTranspiler = /** @class */ (function () { | ||
@@ -27,5 +28,5 @@ /** | ||
var _this = this; | ||
return this.proxy.transpile(files).then(function (transpileResult) { | ||
_this.unMutatedFiles = transpileResult.outputFiles; | ||
return transpileResult; | ||
return this.proxy.transpile(files).then(function (transpiledFiles) { | ||
_this.unMutatedFiles = transpiledFiles; | ||
return transpiledFiles; | ||
}); | ||
@@ -41,3 +42,3 @@ }; | ||
_this.transpileMutant(mutant) | ||
.then(function (transpileResult) { return observer.next(TranspiledMutant_1.default.create(mutant, transpileResult, _this.unMutatedFiles)); }) | ||
.then(function (transpiledFiles) { return observer.next(_this.createTranspiledMutant(mutant, transpiledFiles, _this.unMutatedFiles)); }) | ||
.then(nextMutant) | ||
@@ -58,18 +59,28 @@ .catch(function (error) { return observer.error(error); }); | ||
}; | ||
MutantTranspiler.prototype.createTranspiledMutant = function (mutant, transpileResult, unMutatedFiles) { | ||
return new TranspiledMutant_1.default(mutant, transpileResult, someFilesChanged()); | ||
function someFilesChanged() { | ||
return transpileResult.outputFiles.some(function (file) { return fileChanged(file); }); | ||
} | ||
function fileChanged(file) { | ||
if (unMutatedFiles) { | ||
var unMutatedFile = unMutatedFiles.find(function (f) { return f.name === file.name; }); | ||
return !unMutatedFile || unMutatedFile.textContent !== file.textContent; | ||
} | ||
else { | ||
return true; | ||
} | ||
} | ||
}; | ||
MutantTranspiler.prototype.transpileMutant = function (mutant) { | ||
var filesToTranspile = []; | ||
if (this.currentMutatedFile && this.currentMutatedFile.file.name !== mutant.fileName) { | ||
if (this.currentMutatedFile && this.currentMutatedFile.name !== mutant.fileName) { | ||
filesToTranspile.push(this.currentMutatedFile.file); | ||
} | ||
this.currentMutatedFile = mutant.sourceFile; | ||
var mutatedFile = { | ||
name: mutant.fileName, | ||
content: mutant.mutatedCode, | ||
kind: core_1.FileKind.Text, | ||
mutated: this.currentMutatedFile.file.mutated, | ||
transpiled: this.currentMutatedFile.file.transpiled, | ||
included: mutant.included | ||
}; | ||
var mutatedFile = new core_1.File(mutant.fileName, Buffer.from(mutant.mutatedCode)); | ||
filesToTranspile.push(mutatedFile); | ||
return this.proxy.transpile(filesToTranspile); | ||
return this.proxy.transpile(filesToTranspile) | ||
.then(function (transpiledFiles) { return ({ outputFiles: transpiledFiles, error: null }); }) | ||
.catch(function (error) { return ({ outputFiles: [], error: objectUtils_1.errorToString(error) }); }); | ||
}; | ||
@@ -76,0 +87,0 @@ return MutantTranspiler; |
import { File, Location } from 'stryker-api/core'; | ||
import { Config } from 'stryker-api/config'; | ||
import StrykerError from '../utils/StrykerError'; | ||
export interface MappedLocation { | ||
@@ -7,4 +8,4 @@ fileName: string; | ||
} | ||
export declare class SourceMapError extends Error { | ||
constructor(message: string); | ||
export declare class SourceMapError extends StrykerError { | ||
constructor(message: string, innerError?: Error); | ||
} | ||
@@ -22,3 +23,4 @@ /** | ||
abstract transpiledLocationFor(originalLocation: MappedLocation): MappedLocation; | ||
static create(transpiledFiles: File[], config: Config): SourceMapper; | ||
abstract transpiledFileNameFor(originalFileName: string): string; | ||
static create(transpiledFiles: ReadonlyArray<File>, config: Config): SourceMapper; | ||
} | ||
@@ -28,4 +30,9 @@ export declare class TranspiledSourceMapper extends SourceMapper { | ||
private sourceMaps; | ||
constructor(transpiledFiles: File[]); | ||
private log; | ||
constructor(transpiledFiles: ReadonlyArray<File>); | ||
/** | ||
* @inheritDoc | ||
*/ | ||
transpiledFileNameFor(originalFileName: string): string; | ||
/** | ||
* @inheritdoc | ||
@@ -43,2 +50,3 @@ */ | ||
private createSourceMaps(); | ||
private getRawSourceMap(sourceMapFile); | ||
private getSourceMapForFile(transpiledFile); | ||
@@ -69,3 +77,7 @@ /** | ||
*/ | ||
transpiledFileNameFor(originalFileName: string): string; | ||
/** | ||
* @inheritdoc | ||
*/ | ||
transpiledLocationFor(originalLocation: MappedLocation): MappedLocation; | ||
} |
@@ -8,7 +8,9 @@ "use strict"; | ||
var objectUtils_1 = require("../utils/objectUtils"); | ||
var log4js_1 = require("log4js"); | ||
var StrykerError_1 = require("../utils/StrykerError"); | ||
var SOURCE_MAP_URL_REGEX = /\/\/\s*#\s*sourceMappingURL=(.*)/g; | ||
var SourceMapError = /** @class */ (function (_super) { | ||
tslib_1.__extends(SourceMapError, _super); | ||
function SourceMapError(message) { | ||
var _this = _super.call(this, 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.") || this; | ||
function SourceMapError(message, innerError) { | ||
var _this = _super.call(this, 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) || this; | ||
Error.captureStackTrace(_this, SourceMapError); | ||
@@ -20,3 +22,3 @@ // TS recommendation: https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#extending-built-ins-like-error-array-and-map-may-no-longer-work | ||
return SourceMapError; | ||
}(Error)); | ||
}(StrykerError_1.default)); | ||
exports.SourceMapError = SourceMapError; | ||
@@ -47,5 +49,13 @@ /** | ||
_this.transpiledFiles = transpiledFiles; | ||
_this.log = log4js_1.getLogger(SourceMapper.name); | ||
return _this; | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
TranspiledSourceMapper.prototype.transpiledFileNameFor = function (originalFileName) { | ||
var sourceMap = this.getSourceMap(originalFileName); | ||
return sourceMap.transpiledFile.name; | ||
}; | ||
/** | ||
* @inheritdoc | ||
@@ -55,17 +65,12 @@ */ | ||
var sourceMap = this.getSourceMap(originalLocation.fileName); | ||
if (!sourceMap) { | ||
throw new SourceMapError("Source map not found for \"" + originalLocation.fileName + "\""); | ||
} | ||
else { | ||
var relativeSource = this.getRelativeSource(sourceMap, originalLocation); | ||
var start = sourceMap.generatedPositionFor(originalLocation.location.start, relativeSource); | ||
var end = sourceMap.generatedPositionFor(originalLocation.location.end, relativeSource); | ||
return { | ||
fileName: sourceMap.transpiledFile.name, | ||
location: { | ||
start: start, | ||
end: end | ||
} | ||
}; | ||
} | ||
var relativeSource = this.getRelativeSource(sourceMap, originalLocation); | ||
var start = sourceMap.generatedPositionFor(originalLocation.location.start, relativeSource); | ||
var end = sourceMap.generatedPositionFor(originalLocation.location.end, relativeSource); | ||
return { | ||
fileName: sourceMap.transpiledFile.name, | ||
location: { | ||
start: start, | ||
end: end | ||
} | ||
}; | ||
}; | ||
@@ -83,3 +88,9 @@ TranspiledSourceMapper.prototype.getRelativeSource = function (from, to) { | ||
} | ||
return this.sourceMaps[path.resolve(sourceFileName)]; | ||
var sourceMap = this.sourceMaps[path.resolve(sourceFileName)]; | ||
if (sourceMap) { | ||
return sourceMap; | ||
} | ||
else { | ||
throw new SourceMapError("Source map not found for \"" + sourceFileName + "\""); | ||
} | ||
}; | ||
@@ -93,8 +104,8 @@ /** | ||
this.transpiledFiles.forEach(function (transpiledFile) { | ||
if (transpiledFile.mutated && transpiledFile.kind === core_1.FileKind.Text) { | ||
var sourceMapFile_1 = _this.getSourceMapForFile(transpiledFile); | ||
var rawSourceMap = JSON.parse(sourceMapFile_1.content); | ||
var sourceMap_1 = new SourceMap(transpiledFile, sourceMapFile_1.name, rawSourceMap); | ||
var sourceMapFile = _this.getSourceMapForFile(transpiledFile); | ||
if (sourceMapFile) { | ||
var rawSourceMap = _this.getRawSourceMap(sourceMapFile); | ||
var sourceMap_1 = new SourceMap(transpiledFile, sourceMapFile.name, rawSourceMap); | ||
rawSourceMap.sources.forEach(function (source) { | ||
var sourceFileName = path.resolve(path.dirname(sourceMapFile_1.name), source); | ||
var sourceFileName = path.resolve(path.dirname(sourceMapFile.name), source); | ||
sourceMaps[sourceFileName] = sourceMap_1; | ||
@@ -106,6 +117,18 @@ }); | ||
}; | ||
TranspiledSourceMapper.prototype.getRawSourceMap = function (sourceMapFile) { | ||
try { | ||
return JSON.parse(sourceMapFile.textContent); | ||
} | ||
catch (error) { | ||
throw new SourceMapError("Source map file \"" + sourceMapFile.name + "\" could not be parsed as json", error); | ||
} | ||
}; | ||
TranspiledSourceMapper.prototype.getSourceMapForFile = function (transpiledFile) { | ||
var sourceMappingUrl = this.getSourceMapUrl(transpiledFile); | ||
var sourceMapFile = this.getSourceMapFileFromUrl(sourceMappingUrl, transpiledFile); | ||
return sourceMapFile; | ||
if (sourceMappingUrl) { | ||
return this.getSourceMapFileFromUrl(sourceMappingUrl, transpiledFile); | ||
} | ||
else { | ||
return null; | ||
} | ||
}; | ||
@@ -120,8 +143,3 @@ /** | ||
this.getInlineSourceMap(sourceMapUrl, transpiledFile) : this.getExternalSourceMap(sourceMapUrl, transpiledFile); | ||
if (sourceMapFile.kind === core_1.FileKind.Text) { | ||
return sourceMapFile; | ||
} | ||
else { | ||
throw new SourceMapError("Source map file \"" + sourceMapFile.name + "\" has the wrong file kind. \"" + core_1.FileKind[sourceMapFile.kind] + "\" instead of \"" + core_1.FileKind[core_1.FileKind.Text] + "\""); | ||
} | ||
return sourceMapFile; | ||
}; | ||
@@ -138,10 +156,3 @@ TranspiledSourceMapper.prototype.isInlineUrl = function (sourceMapUrl) { | ||
var content = objectUtils_1.base64Decode(sourceMapUrl.substr(supportedDataPrefix.length)); | ||
return { | ||
name: transpiledFile.name, | ||
content: content, | ||
kind: core_1.FileKind.Text, | ||
included: false, | ||
mutated: false, | ||
transpiled: false | ||
}; | ||
return new core_1.File(transpiledFile.name, content); | ||
} | ||
@@ -173,10 +184,12 @@ else { | ||
// Retrieve the final sourceMappingURL comment in the file | ||
while (currentMatch = SOURCE_MAP_URL_REGEX.exec(transpiledFile.content)) { | ||
while (currentMatch = SOURCE_MAP_URL_REGEX.exec(transpiledFile.textContent)) { | ||
lastMatch = currentMatch; | ||
} | ||
if (lastMatch) { | ||
this.log.debug('Source map url found in transpiled file "%s"', transpiledFile.name); | ||
return lastMatch[1]; | ||
} | ||
else { | ||
throw new SourceMapError("No source map reference found in transpiled file \"" + transpiledFile.name + "\""); | ||
this.log.debug('No source map url found in transpiled file "%s"', transpiledFile.name); | ||
return null; | ||
} | ||
@@ -195,2 +208,8 @@ }; | ||
*/ | ||
PassThroughSourceMapper.prototype.transpiledFileNameFor = function (originalFileName) { | ||
return originalFileName; | ||
}; | ||
/** | ||
* @inheritdoc | ||
*/ | ||
PassThroughSourceMapper.prototype.transpiledLocationFor = function (originalLocation) { | ||
@@ -197,0 +216,0 @@ return originalLocation; |
import { File } from 'stryker-api/core'; | ||
import { Transpiler, TranspileResult, TranspilerOptions } from 'stryker-api/transpile'; | ||
import { Transpiler, TranspilerOptions } from 'stryker-api/transpile'; | ||
export default class TranspilerFacade implements Transpiler { | ||
private innerTranspilers; | ||
constructor(options: TranspilerOptions, additionalTranspiler?: { | ||
name: string; | ||
transpiler: Transpiler; | ||
}); | ||
transpile(files: File[]): Promise<TranspileResult>; | ||
private performTranspileChain(currentResult, remainingChain?); | ||
private createPassThruTranspileResult(input); | ||
constructor(options: TranspilerOptions); | ||
transpile(files: ReadonlyArray<File>): Promise<ReadonlyArray<File>>; | ||
private performTranspileChain(input, remainingChain?); | ||
} |
@@ -5,2 +5,3 @@ "use strict"; | ||
var transpile_1 = require("stryker-api/transpile"); | ||
var StrykerError_1 = require("../utils/StrykerError"); | ||
var NamedTranspiler = /** @class */ (function () { | ||
@@ -14,34 +15,26 @@ function NamedTranspiler(name, transpiler) { | ||
var TranspilerFacade = /** @class */ (function () { | ||
function TranspilerFacade(options, additionalTranspiler) { | ||
function TranspilerFacade(options) { | ||
this.innerTranspilers = options.config.transpilers | ||
.map(function (transpilerName) { return new NamedTranspiler(transpilerName, transpile_1.TranspilerFactory.instance().create(transpilerName, options)); }); | ||
if (additionalTranspiler) { | ||
this.innerTranspilers.push(new NamedTranspiler(additionalTranspiler.name, additionalTranspiler.transpiler)); | ||
} | ||
} | ||
TranspilerFacade.prototype.transpile = function (files) { | ||
return this.performTranspileChain(this.createPassThruTranspileResult(files)); | ||
return this.performTranspileChain(files); | ||
}; | ||
TranspilerFacade.prototype.performTranspileChain = function (currentResult, remainingChain) { | ||
TranspilerFacade.prototype.performTranspileChain = function (input, remainingChain) { | ||
if (remainingChain === void 0) { remainingChain = this.innerTranspilers.slice(); } | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var next, nextResult; | ||
var current, output; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
next = remainingChain.shift(); | ||
if (!next) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, next.transpiler.transpile(currentResult.outputFiles)]; | ||
current = remainingChain.shift(); | ||
if (!current) return [3 /*break*/, 2]; | ||
return [4 /*yield*/, current.transpiler.transpile(input) | ||
.catch(function (error) { | ||
throw new StrykerError_1.default("An error occurred in transpiler \"" + current.name + "\"", error); | ||
})]; | ||
case 1: | ||
nextResult = _a.sent(); | ||
if (nextResult.error) { | ||
nextResult.error = "Execute " + next.name + ": " + nextResult.error; | ||
return [2 /*return*/, nextResult]; | ||
} | ||
else { | ||
return [2 /*return*/, this.performTranspileChain(nextResult, remainingChain)]; | ||
} | ||
return [3 /*break*/, 3]; | ||
case 2: return [2 /*return*/, currentResult]; | ||
case 3: return [2 /*return*/]; | ||
output = _a.sent(); | ||
return [2 /*return*/, this.performTranspileChain(output, remainingChain)]; | ||
case 2: return [2 /*return*/, input]; | ||
} | ||
@@ -51,8 +44,2 @@ }); | ||
}; | ||
TranspilerFacade.prototype.createPassThruTranspileResult = function (input) { | ||
return { | ||
error: null, | ||
outputFiles: input | ||
}; | ||
}; | ||
return TranspilerFacade; | ||
@@ -59,0 +46,0 @@ }()); |
/// <reference types="node" /> | ||
import { FileKind } from 'stryker-api/core'; | ||
export declare function glob(expression: string): Promise<string[]>; | ||
@@ -10,3 +9,2 @@ export declare function deleteDir(dirToDelete: string): Promise<void>; | ||
export declare function importModule(moduleName: string): void; | ||
export declare function isOnlineFile(path: string): boolean; | ||
/** | ||
@@ -19,2 +17,1 @@ * Writes data to a specified file. | ||
export declare function writeFile(fileName: string, data: string | Buffer): Promise<void>; | ||
export declare function determineFileKind(fileName: string): FileKind; |
@@ -5,7 +5,5 @@ "use strict"; | ||
var fs = require("mz/fs"); | ||
var path = require("path"); | ||
var nodeGlob = require("glob"); | ||
var mkdirp = require("mkdirp"); | ||
var rimraf = require("rimraf"); | ||
var core_1 = require("stryker-api/core"); | ||
function glob(expression) { | ||
@@ -53,17 +51,2 @@ return new Promise(function (resolve, reject) { | ||
exports.importModule = importModule; | ||
function isOnlineFile(path) { | ||
return path.indexOf('http://') === 0 || path.indexOf('https://') === 0; | ||
} | ||
exports.isOnlineFile = isOnlineFile; | ||
var binaryExtensions = [ | ||
'.png', | ||
'.jpeg', | ||
'.jpg', | ||
'.zip', | ||
'.tar', | ||
'.gif' // Still more to add | ||
]; | ||
function isBinaryFile(name) { | ||
return binaryExtensions.indexOf(path.extname(name)) > -1; | ||
} | ||
/** | ||
@@ -84,14 +67,2 @@ * Writes data to a specified file. | ||
exports.writeFile = writeFile; | ||
function determineFileKind(fileName) { | ||
if (isOnlineFile(fileName)) { | ||
return core_1.FileKind.Web; | ||
} | ||
if (isBinaryFile(fileName)) { | ||
return core_1.FileKind.Binary; | ||
} | ||
else { | ||
return core_1.FileKind.Text; | ||
} | ||
} | ||
exports.determineFileKind = determineFileKind; | ||
//# sourceMappingURL=fileUtils.js.map |
@@ -0,0 +0,0 @@ import { Location } from 'stryker-api/core'; |
@@ -0,0 +0,0 @@ "use strict"; |
/// <reference types="node" /> | ||
export { serialize, deserialize } from 'surrial'; | ||
export declare function freezeRecursively<T extends { | ||
@@ -7,11 +8,2 @@ [prop: string]: any; | ||
export declare function filterEmpty<T>(input: (T | null | void)[]): T[]; | ||
/** | ||
* Serializes javascript without using `JSON.stringify` (directly), as it does not allow for regexes or functions, etc | ||
*/ | ||
export declare const serialize: (obj: any) => string; | ||
/** | ||
* Deserialize javascript without using `JSON.parse` (directly), as it does not allow for regexes or functions, etc | ||
* (Uses eval instead) | ||
*/ | ||
export declare function deserialize(serializedJavascript: String): any; | ||
export declare function isErrnoException(error: Error): error is NodeJS.ErrnoException; | ||
@@ -30,1 +22,6 @@ export declare function errorToString(error: any): any; | ||
export declare function base64Decode(base64EncodedString: string): string; | ||
/** | ||
* Consolidates multiple consecutive white spaces into a single space. | ||
* @param str The string to be normalized | ||
*/ | ||
export declare function normalizeWhiteSpaces(str: string): string; |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var _ = require("lodash"); | ||
var surrial_1 = require("surrial"); | ||
exports.serialize = surrial_1.serialize; | ||
exports.deserialize = surrial_1.deserialize; | ||
function freezeRecursively(target) { | ||
@@ -22,16 +25,2 @@ Object.freeze(target); | ||
exports.filterEmpty = filterEmpty; | ||
/** | ||
* Serializes javascript without using `JSON.stringify` (directly), as it does not allow for regexes or functions, etc | ||
*/ | ||
exports.serialize = require('serialize-javascript'); | ||
/** | ||
* Deserialize javascript without using `JSON.parse` (directly), as it does not allow for regexes or functions, etc | ||
* (Uses eval instead) | ||
*/ | ||
function deserialize(serializedJavascript) { | ||
// tslint:disable | ||
return eval("(" + serializedJavascript + ")"); | ||
// tslint:enable | ||
} | ||
exports.deserialize = deserialize; | ||
function isErrnoException(error) { | ||
@@ -93,2 +82,10 @@ return typeof error.code === 'string'; | ||
exports.base64Decode = base64Decode; | ||
/** | ||
* Consolidates multiple consecutive white spaces into a single space. | ||
* @param str The string to be normalized | ||
*/ | ||
function normalizeWhiteSpaces(str) { | ||
return str.replace(/\s+/g, ' ').trim(); | ||
} | ||
exports.normalizeWhiteSpaces = normalizeWhiteSpaces; | ||
//# sourceMappingURL=objectUtils.js.map |
@@ -0,0 +0,0 @@ import * as estree from 'estree'; |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ /// <reference types="node" /> |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ /** |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ /// <reference types="node" /> |
@@ -0,0 +0,0 @@ "use strict"; |
@@ -0,0 +0,0 @@ export default class Timer { |
@@ -0,0 +0,0 @@ "use strict"; |
Unpopular package
QualityThis package is not very popular.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
350634
155
304
4
647
6333
1
+ Addedsurrial@^0.1.3
+ Addedstryker-api@0.15.0(transitive)
+ Addedsurrial@0.1.3(transitive)
- Removedserialize-javascript@^1.3.0
- Removedserialize-javascript@1.9.1(transitive)
- Removedstryker-api@0.14.0(transitive)