diff --git a/packages/stryker-webpack/.gitignore b/packages/stryker-webpack/.gitignore
index 20dd5ff0bf..1e4732ddc3 100644
--- a/packages/stryker-webpack/.gitignore
+++ b/packages/stryker-webpack/.gitignore
@@ -2,4 +2,10 @@
/node_modules
**/*.js
**/*.js.map
-**/*.d.ts
\ No newline at end of file
+**/*.d.ts
+
+!testResources/**/*.js
+!stryker.conf.js
+/package-lock.json
+/reports
+/.nyc_output
\ No newline at end of file
diff --git a/packages/stryker-webpack/.idea/vcs.xml b/packages/stryker-webpack/.idea/vcs.xml
deleted file mode 100644
index 35eb1ddfbb..0000000000
--- a/packages/stryker-webpack/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/packages/stryker-webpack/.travis.yml b/packages/stryker-webpack/.travis.yml
index 72ed477b34..c8996bd8ab 100644
--- a/packages/stryker-webpack/.travis.yml
+++ b/packages/stryker-webpack/.travis.yml
@@ -6,4 +6,12 @@ node_js:
- '4'
install: npm install
before_install:
-- if [[ `npm -v` = 2* ]]; then npm i -g npm@3; fi
\ No newline at end of file
+- if [[ `npm -v` = 2* ]]; then npm i -g npm@3; fi
+notifications:
+ slack:
+ secure: PaygAi3u8LIEIpsMbzr6BKgU8qW4abz9D+I5fBaxtrCycBYcj6qWSz5SY6XMAsf6cHj0U0CLl7e7tw7sR8FJlha61ej9u/p+gnHlPPQDsAfOYNifuTyCoj7s7HjF1bboKGKB66P/Np/QPyFvTHwVdA6l2JPoJO5a6eiqaUUvhWoZKGvs4HGBzGfd3g/6RZ7ugUH1C6DoUnPv1eoFoA22QH8pj30ugq/E0xZnD64nx0vBrFzfbpH9r9wDOphe9lnLygxHTzXY/smJfXqNrpdRyWnztTcI0XwTEe4lZR1l9y5UkziVvqrVoSblUp68JvB9yrfdZzWkLyXAoYaiOPIsszHnT9ovUWFLYqsbBkjqJOYPZX0koCeP2a07i5UMXbjwKcwdg+HKW3ZMq0oRX5n1dMOVBlCqlaZjW9Q0TJQiseLvrOmtpM2X3mKb+2ibyuoObpnUEBNdluQ1xiqBFsrFNpmowPUidt4TDmT4R2ggOM11O5iW5kv4gaE6N4q6hu1+tlQR6eu5LM5d8CNrJ3k362STqw11bOQbXtondVMV2kOo7+VJT8JHDK4V8aX5FRk8yhrC80FxD7Et7eJOSXOnz8HXQo064+0vjJjdzzEjfJKeqwuhLTjZvQnSjBDRa6u59h4X50RqN/MUmzSd18HqKuX2EhVMl+08u5YhRg+mi0U=
+ on_success: change
+ on_failure: change
+ email:
+ on_success: never
+ on_failure: change
diff --git a/packages/stryker-webpack/.vscode/settings.json b/packages/stryker-webpack/.vscode/settings.json
new file mode 100644
index 0000000000..4580195e13
--- /dev/null
+++ b/packages/stryker-webpack/.vscode/settings.json
@@ -0,0 +1,16 @@
+{
+ "typescript.tsdk": "node_modules/typescript/lib",
+ "files": {
+ "exclude": {
+ ".git": "",
+ ".tscache": "",
+ "**/*.js": {
+ "when": "$(basename).ts"
+ },
+ "**/*.d.ts": true,
+ "**/*.map": {
+ "when": "$(basename)"
+ }
+ }
+ }
+ }
\ No newline at end of file
diff --git a/packages/stryker-webpack/.vscode/tasks.json b/packages/stryker-webpack/.vscode/tasks.json
new file mode 100644
index 0000000000..61112faee9
--- /dev/null
+++ b/packages/stryker-webpack/.vscode/tasks.json
@@ -0,0 +1,25 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "type": "npm",
+ "script": "build",
+ "problemMatcher": []
+ },
+ {
+ "type": "npm",
+ "script": "test",
+ "problemMatcher": []
+ },
+ {
+ "type": "typescript",
+ "tsconfig": "tsconfig.json",
+ "option": "watch",
+ "problemMatcher": [
+ "$tsc-watch"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/stryker-webpack/package-lock.json b/packages/stryker-webpack/package-lock.json
deleted file mode 100644
index 5bda234798..0000000000
--- a/packages/stryker-webpack/package-lock.json
+++ /dev/null
@@ -1,266 +0,0 @@
-{
- "name": "stryker-webpack",
- "version": "0.0.1",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "@types/chai": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.0.4.tgz",
- "integrity": "sha512-cvU0HomQ7/aGDQJZsbtJXqBQ7w4J4TqLB0Z/h8mKrpRjfeZEvTbygkfJEb7fWdmwpIeDeFmIVwAEqS0OYuUv3Q==",
- "dev": true
- },
- "@types/mocha": {
- "version": "2.2.43",
- "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-2.2.43.tgz",
- "integrity": "sha512-xNlAmH+lRJdUMXClMTI9Y0pRqIojdxfm7DHsIxoB2iTzu3fnPmSMEN8SsSx0cdwV36d02PWCWaDUoZPDSln+xw==",
- "dev": true
- },
- "assertion-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.0.2.tgz",
- "integrity": "sha1-E8pRXYYgbaC6xm6DTdOX2HWBCUw=",
- "dev": true
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
- },
- "brace-expansion": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.8.tgz",
- "integrity": "sha1-wHshHHyVLsH479Uad+8NHTmQopI=",
- "dev": true,
- "requires": {
- "balanced-match": "1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "browser-stdout": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.0.tgz",
- "integrity": "sha1-81HTKWnTL6XXpVZxVCY9korjvR8=",
- "dev": true
- },
- "chai": {
- "version": "4.1.2",
- "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz",
- "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=",
- "dev": true,
- "requires": {
- "assertion-error": "1.0.2",
- "check-error": "1.0.2",
- "deep-eql": "3.0.1",
- "get-func-name": "2.0.0",
- "pathval": "1.1.0",
- "type-detect": "4.0.3"
- }
- },
- "check-error": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz",
- "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=",
- "dev": true
- },
- "commander": {
- "version": "2.11.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
- "integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
- "dev": true
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
- "debug": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
- "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "deep-eql": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz",
- "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==",
- "dev": true,
- "requires": {
- "type-detect": "4.0.3"
- }
- },
- "diff": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/diff/-/diff-3.3.1.tgz",
- "integrity": "sha512-MKPHZDMB0o6yHyDryUOScqZibp914ksXwAMYMTHj6KO8UeKsRYNJD3oNCKjTqZon+V488P7N/HzXF8t7ZR95ww==",
- "dev": true
- },
- "escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
- "dev": true
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
- },
- "get-func-name": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz",
- "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=",
- "dev": true
- },
- "glob": {
- "version": "7.1.2",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz",
- "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==",
- "dev": true,
- "requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.3",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
- }
- },
- "growl": {
- "version": "1.10.3",
- "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.3.tgz",
- "integrity": "sha512-hKlsbA5Vu3xsh1Cg3J7jSmX/WaW6A5oBeqzM88oNbCRQFz+zUaXm6yxS4RVytp1scBoJzSYl4YAEOQIt6O8V1Q==",
- "dev": true
- },
- "has-flag": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz",
- "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=",
- "dev": true
- },
- "he": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz",
- "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
- "dev": true
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
- "requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
- "dev": true
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
- "requires": {
- "brace-expansion": "1.1.8"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
- "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
- "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "mocha": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/mocha/-/mocha-4.0.1.tgz",
- "integrity": "sha512-evDmhkoA+cBNiQQQdSKZa2b9+W2mpLoj50367lhy+Klnx9OV8XlCIhigUnn1gaTFLQCa0kdNhEGDr0hCXOQFDw==",
- "dev": true,
- "requires": {
- "browser-stdout": "1.3.0",
- "commander": "2.11.0",
- "debug": "3.1.0",
- "diff": "3.3.1",
- "escape-string-regexp": "1.0.5",
- "glob": "7.1.2",
- "growl": "1.10.3",
- "he": "1.1.1",
- "mkdirp": "0.5.1",
- "supports-color": "4.4.0"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
- "requires": {
- "wrappy": "1.0.2"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
- },
- "pathval": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz",
- "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=",
- "dev": true
- },
- "supports-color": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-4.4.0.tgz",
- "integrity": "sha512-rKC3+DyXWgK0ZLKwmRsrkyHVZAjNkfzeehuFWdGGcqGDTZFH73+RH6S/RDAAxl9GusSjZSUWYLmT9N5pzXFOXQ==",
- "dev": true,
- "requires": {
- "has-flag": "2.0.0"
- }
- },
- "type-detect": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.3.tgz",
- "integrity": "sha1-Dj8mcLRAmbC0bChNE2p+9Jx0wuo=",
- "dev": true
- },
- "typescript": {
- "version": "2.5.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
- "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==",
- "dev": true
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
- }
- }
-}
diff --git a/packages/stryker-webpack/package.json b/packages/stryker-webpack/package.json
index 0ca3d0f13e..31d11078cd 100644
--- a/packages/stryker-webpack/package.json
+++ b/packages/stryker-webpack/package.json
@@ -6,8 +6,10 @@
"main": "src/index.js",
"scripts": {
"build": "tsc",
- "pretest": "tsc",
- "test": "mocha --recursive test/**/*.spec.js"
+ "premocha": "npm run build",
+ "test": "npm run mocha && npm run stryker",
+ "mocha": "nyc --reporter=html --report-dir=reports/coverage --check-coverage --lines 85 --functions 90 --branches 65 mocha \"test/unit/**/*.js\" \"test/integration/**/*.js\"",
+ "stryker": "stryker run"
},
"repository": {
"type": "git",
@@ -35,12 +37,31 @@
"homepage": "https://github.com/Archcry/stryker-webpack#readme",
"devDependencies": {
"@types/chai": "^4.0.4",
+ "@types/memory-fs": "^0.3.0",
"@types/mocha": "^2.2.43",
+ "@types/mz": "0.0.32",
+ "@types/sinon": "^2.3.6",
+ "@types/webpack": "^3.0.13",
"chai": "^4.1.2",
- "mocha": "^4.0.1",
- "typescript": "^2.2.2"
+ "mocha": "^3.5.3",
+ "mz": "^2.7.0",
+ "nyc": "^11.2.1",
+ "sinon": "^4.0.1",
+ "stryker": "^0.13.0",
+ "stryker-cli": "^0.1.3",
+ "stryker-html-reporter": "^0.11.0",
+ "stryker-mocha-framework": "^0.6.1",
+ "stryker-mocha-runner": "^0.9.1",
+ "stryker-typescript": "^0.3.0",
+ "typescript": "^2.5.0"
},
"peerDependencies": {
+ "webpack": "^3.7.1",
+ "stryker-api": "^0.11.0"
+ },
+ "dependencies": {
+ "memory-fs": "^0.4.1",
+ "stryker-api": "^0.11.0",
"webpack": "^3.7.1"
}
}
diff --git a/packages/stryker-webpack/src/.gitkeep b/packages/stryker-webpack/src/.gitkeep
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/stryker-webpack/src/WebpackTranspiler.ts b/packages/stryker-webpack/src/WebpackTranspiler.ts
new file mode 100644
index 0000000000..7f9233e1f0
--- /dev/null
+++ b/packages/stryker-webpack/src/WebpackTranspiler.ts
@@ -0,0 +1,71 @@
+import {TranspilerOptions, Transpiler, TranspileResult, FileLocation} from "stryker-api/transpile";
+import {File, TextFile} from "stryker-api/core";
+import {Config} from "stryker-api/config"
+import WebpackCompiler from "./compiler/WebpackCompiler";
+import { FileSystem } from "./helpers/FsWrapper";
+import * as path from "path";
+import {Configuration} from "webpack";
+
+// TODO: Fix types for memory-fs
+import MemoryFileSystem = require("memory-fs");
+
+class WebpackTranspiler implements Transpiler {
+ private _config: Config;
+ private _compiler: WebpackCompiler;
+
+ public constructor(options: TranspilerOptions) {
+ this._config = options.config;
+ const fs = new MemoryFileSystem() as FileSystem;
+
+ // Temporarily clone the config object so that it is no longer readonly
+ // TODO: Create a ConfigEditor plugin to do this in the future.
+ const webpackConfig = Object.assign({}, this._config.webpackConfig);
+
+ this._compiler = new WebpackCompiler(this.createWebpackConfig(webpackConfig), fs);
+ }
+
+ private createWebpackConfig(config: Configuration): Configuration {
+ return config || {
+ entry: [path.resolve("index.js")],
+ output: {
+ path: "/out",
+ filename: "bundle.js",
+ }
+ }
+ }
+
+ public async transpile(files: Array): Promise {
+ try {
+ await this._compiler.replace(files as Array);
+
+ const compileResult = await this._compiler.emit();
+
+ return this.createSuccessResult(compileResult);
+ } catch (err) {
+ return this.createErrorResult(`${err.name}: ${err.message}`);
+ }
+ }
+
+ private createErrorResult(error: string): TranspileResult {
+ return {
+ error: error,
+ outputFiles: []
+ };
+
+ }
+
+ private createSuccessResult(outPutFiles: File[]): TranspileResult {
+ return {
+ error: null,
+ outputFiles: outPutFiles
+ };
+ }
+
+ public getMappedLocation(sourceFileLocation: FileLocation): FileLocation {
+ // Waiting for a decision on how this is going to be implemented in the future
+ // Return a "Method nog implemented" error for now.
+ throw new Error("Method not implemented.");
+ }
+}
+
+export default WebpackTranspiler;
\ No newline at end of file
diff --git a/packages/stryker-webpack/src/compiler/Webpack.ts b/packages/stryker-webpack/src/compiler/Webpack.ts
new file mode 100644
index 0000000000..4b69d94126
--- /dev/null
+++ b/packages/stryker-webpack/src/compiler/Webpack.ts
@@ -0,0 +1,2 @@
+import * as webpack from "webpack";
+export default webpack;
\ No newline at end of file
diff --git a/packages/stryker-webpack/src/compiler/WebpackCompiler.ts b/packages/stryker-webpack/src/compiler/WebpackCompiler.ts
new file mode 100644
index 0000000000..5298444595
--- /dev/null
+++ b/packages/stryker-webpack/src/compiler/WebpackCompiler.ts
@@ -0,0 +1,78 @@
+import {FileKind, TextFile} from "stryker-api/core";
+import FsWrapper, {FileSystem} from "../helpers/FsWrapper";
+import {Compiler, Configuration} from "webpack";
+import webpack from "./Webpack";
+import * as path from "path";
+
+export default class WebpackCompiler {
+ private _compiler: Compiler;
+ private _fsWrapper: FsWrapper;
+ private _outPath: string;
+ private _bundleFileName: string;
+
+ public constructor(webpackConfig: Configuration, fs: FileSystem) {
+ this._fsWrapper = new FsWrapper(fs);
+ this._compiler = this.createCompiler(webpackConfig, fs);
+ this._outPath = "/out";
+ this._bundleFileName = "bundle.js";
+ }
+
+ private createCompiler(webpackConfig: Configuration, fileSystem: FileSystem): Compiler {
+ // Force cache to on in the webpack configuration to make compilation go faster
+ webpackConfig.cache = true;
+
+ // Declare as any here to avoid errors when setting filesystem
+ const compiler: any = webpack(webpackConfig);
+
+ // Setting filesystem to provided fs so compilation can be done in memory
+ compiler.inputFileSystem = fileSystem;
+ compiler.outputFileSystem = fileSystem;
+ compiler.resolvers.normal.fileSystem = fileSystem;
+ compiler.resolvers.context.fileSystem = fileSystem;
+
+ return compiler as Compiler;
+ }
+
+ public async replace(files: Array): Promise {
+ for(let file of files) {
+ await this.writeToFs(file);
+ }
+ }
+
+ private async writeToFs(file: TextFile): Promise {
+ // Create the directory
+ await this._fsWrapper.mkdirp(path.dirname(file.name));
+
+ // Write the file to the filesystem
+ await this._fsWrapper.writeFile(file.name, file.content);
+ }
+
+ public async emit(): Promise> {
+ await this.compile();
+
+ const compileResult: string = await this._fsWrapper.readFile(path.join(this._outPath, this._bundleFileName));
+
+ return new Array({
+ content: compileResult,
+ name: this._bundleFileName,
+ mutated: true, // TODO: change this to the correct value
+ kind: FileKind.Text,
+ transpiled: true,
+ included: true
+ });
+ }
+
+ private compile(): Promise {
+ return new Promise((resolve, reject) => {
+ this._compiler.run((err, stats) => {
+ if(err) {
+ reject(err);
+ } else if(stats.hasErrors()) {
+ reject(Error(stats.toString("errors-only")));
+ } else {
+ resolve(stats);
+ }
+ });
+ });
+ }
+}
\ No newline at end of file
diff --git a/packages/stryker-webpack/src/helpers/FsWrapper.ts b/packages/stryker-webpack/src/helpers/FsWrapper.ts
new file mode 100644
index 0000000000..172adcddb7
--- /dev/null
+++ b/packages/stryker-webpack/src/helpers/FsWrapper.ts
@@ -0,0 +1,73 @@
+import * as path from "path";
+import {Stats} from "fs";
+
+class FsWrapper {
+ static readonly NO_SUCH_DIRECTORY_ENTRY = "ENOENT";
+ static readonly FILE_ALREADY_EXISTS = "EEXIST";
+
+ private _fs: FileSystem;
+
+ public constructor(fs: FileSystem) {
+ this._fs = fs;
+ }
+
+ public readFile(path: string): Promise {
+ return new Promise((resolve, reject) => {
+ this._fs.readFile(path, "utf8", (err: Error, result: string) => err ? reject(err) : resolve(result));
+ })
+ }
+
+ public writeFile(path: string, content: string): Promise {
+ return new Promise((resolve, reject) => {
+ this._fs.writeFile(path, content, (err: Error) => err ? reject(err) : resolve());
+ });
+ }
+
+ public async mkdirp(directoryPath: string): Promise {
+ try {
+ await this.mkdir(directoryPath);
+ } catch (error) {
+ if(error.code === FsWrapper.NO_SUCH_DIRECTORY_ENTRY) {
+ // Recursively move down the tree until we find a dir that exists
+ await this.mkdirp(path.dirname(directoryPath));
+
+ // Bubble back up and create every directory
+ await this.mkdirp(directoryPath);
+ } else if(error.code === FsWrapper.FILE_ALREADY_EXISTS) {
+ const stats = await this.stat(directoryPath);
+
+ // Throw an error when the required directory turns out to be a file
+ if (!stats.isDirectory()) {
+ throw error;
+ }
+ } else {
+ // Throw all other errors
+ throw error;
+ }
+ }
+ }
+
+ private mkdir(path: string): Promise {
+ return new Promise((resolve, reject) => {
+ this._fs.mkdir(path, {}, (err: Error) => err ? reject(err) : resolve());
+ });
+ }
+
+ private stat(path: string): Promise {
+ return new Promise((resolve, reject) => {
+ this._fs.stat(path, (err: Error, result: any) => err ? reject(err) : resolve(result));
+ });
+ }
+}
+
+export interface FileSystem {
+ mkdir(path: string, optArgs: {}, callback: (err: Error, result: any) => void): void
+
+ stat(path: string, callback: (err: Error, result: Stats) => void): void
+
+ writeFile(path: string, content: string, callback: (err: Error) => void): void
+
+ readFile(path: string, options: string, callback: (err: Error, content: string) => void): void
+}
+
+export default FsWrapper;
\ No newline at end of file
diff --git a/packages/stryker-webpack/src/index.ts b/packages/stryker-webpack/src/index.ts
index e013bdb5f8..a4c8cf2afc 100644
--- a/packages/stryker-webpack/src/index.ts
+++ b/packages/stryker-webpack/src/index.ts
@@ -1,7 +1,4 @@
-class Main {
- public hello() {
- return "Hello World!";
- }
-}
+import { TranspilerFactory } from 'stryker-api/transpile';
+import WebpackTranspiler from "./WebpackTranspiler";
-export default Main;
\ No newline at end of file
+TranspilerFactory.instance().register('webpack', WebpackTranspiler);
\ No newline at end of file
diff --git a/packages/stryker-webpack/stryker.conf.js b/packages/stryker-webpack/stryker.conf.js
new file mode 100644
index 0000000000..dc703dc626
--- /dev/null
+++ b/packages/stryker-webpack/stryker.conf.js
@@ -0,0 +1,19 @@
+module.exports = function(config) {
+ config.set({
+ files: [
+ '!src/**/*.ts',
+ '!**/*.d.ts',
+ '!test/integration/**/*.ts',
+ { pattern: 'src/**/*.ts', included: false, mutated: true },
+ { pattern: 'testResources/**/*.js', included: false, mutated: false, transpiled: false }
+ ],
+ testRunner: "mocha",
+ testFramework: "mocha",
+ mutator: "typescript",
+ transpilers: ["typescript"],
+ reporter: ["clear-text", "progress", "html"],
+ tsconfigFile: "tsconfig.json",
+ coverageAnalysis: "off",
+ logLevel: "info"
+ });
+};
diff --git a/packages/stryker-webpack/test/helpers/executeJs.ts b/packages/stryker-webpack/test/helpers/executeJs.ts
new file mode 100644
index 0000000000..778bc55add
--- /dev/null
+++ b/packages/stryker-webpack/test/helpers/executeJs.ts
@@ -0,0 +1,15 @@
+import {exec} from "child_process";
+
+const execute = (code: string): Promise<{stdout: any, stderr: any}> => {
+ return new Promise((resolve, reject) => {
+ exec(`node -e '${code}'`, (err, stdout, stderr) => {
+ if(err) {
+ reject(err)
+ } else {
+ resolve({stdout, stderr});
+ }
+ });
+ });
+}
+
+export default execute;
\ No newline at end of file
diff --git a/packages/stryker-webpack/test/helpers/mockInterfaces.ts b/packages/stryker-webpack/test/helpers/mockInterfaces.ts
new file mode 100644
index 0000000000..12298e9bd6
--- /dev/null
+++ b/packages/stryker-webpack/test/helpers/mockInterfaces.ts
@@ -0,0 +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 } };
+}
\ No newline at end of file
diff --git a/packages/stryker-webpack/test/helpers/producers.ts b/packages/stryker-webpack/test/helpers/producers.ts
new file mode 100644
index 0000000000..501dd0ceef
--- /dev/null
+++ b/packages/stryker-webpack/test/helpers/producers.ts
@@ -0,0 +1,47 @@
+import {Configuration, Stats} from "webpack";
+import {FileSystem} from "../../src/helpers/FsWrapper"
+import {FileKind, TextFile} from "stryker-api/core";
+import {WebpackCompilerMock} from "./mockInterfaces";
+
+export function createFakeFileSystem(): FileSystem {
+ return {
+ readFile: () => {},
+ writeFile: () => {},
+ mkdir: () => {},
+ stat: () => {}
+ };
+}
+
+export function createFakeWebpackConfig(): Configuration {
+ return {
+ entry: ["index.js"],
+ output: {
+ path: "/out",
+ filename: "bundle.js",
+ }
+ };
+}
+
+export function createTextFile(name: string): TextFile {
+ return {
+ name: name,
+ content: 'c = a^2 + b^2',
+ mutated: true,
+ included: true,
+ transpiled: true,
+ kind: FileKind.Text
+ };
+}
+
+export function createWebpackMock(): WebpackCompilerMock {
+ return {
+ run: (callback: (err: Error, stats: Stats) => void) => {},
+ inputFileSystem: { fileSystem: { } },
+ outputFileSystem: { fileSystem: { } },
+ resolvers: {
+ normal: { fileSystem: {} },
+ context: { fileSystem: {} }
+ }
+ }
+}
+
diff --git a/packages/stryker-webpack/test/index.spec.ts b/packages/stryker-webpack/test/index.spec.ts
deleted file mode 100644
index b45eb51b21..0000000000
--- a/packages/stryker-webpack/test/index.spec.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { expect } from "chai";
-import Main from "../src/index";
-
-describe("index.js", () => {
- let main: Main;
-
- beforeEach(() => {
- main = new Main();
- });
-
- it("should return \"Hello World\" when the hello function is called", () => {
- expect(main.hello()).to.equal("Hello World!");
- });
-});
diff --git a/packages/stryker-webpack/test/integration/ownDogFoodSpec.ts b/packages/stryker-webpack/test/integration/ownDogFoodSpec.ts
new file mode 100644
index 0000000000..8c13cb5a61
--- /dev/null
+++ b/packages/stryker-webpack/test/integration/ownDogFoodSpec.ts
@@ -0,0 +1,102 @@
+import * as fs from "mz/fs";
+import {TextFile, FileKind} from "stryker-api/core";
+import {Config} from "stryker-api/config";
+import {TranspileResult, TranspilerOptions} from "stryker-api/transpile";
+import * as path from "path";
+import {expect} from "chai";
+import WebpackTranspiler from "../../src/WebpackTranspiler";
+import Transpiler from "stryker-api/src/transpile/Transpiler";
+import execute from "../helpers/executeJs";
+
+describe("WebpackTranspiler", () => {
+ const sampleProjectLocation: string = path.resolve(__dirname, "../../testResources/sampleProject");
+
+ let files: Array = [];
+ let webPackTranspiler: Transpiler;
+
+ beforeEach(async () => {
+ webPackTranspiler = new WebpackTranspiler(createStrykerConfig());
+
+ files = await fetchTextFiles(sampleProjectLocation);
+ });
+
+ it("should have an array with files", () => {
+ expect(files).to.be.an("array").that.is.not.empty;
+ });
+
+ it("should return a bundle file when the transpiler is called", async () => {
+ try {
+ const result: TranspileResult = await webPackTranspiler.transpile(files);
+ const outFile: TextFile = result.outputFiles[0] as TextFile;
+
+ outFile.content = outFile.content.replace(/'/g, '"')
+
+ const {stdout} = await execute(outFile.content);
+ expect(stdout).to.equal('[ 2, 1, 1, 4, 4, 1, 6, 9, 1, 8, 16, 1 ]\n');
+ } catch(err) {
+ throw new Error(err);
+ }
+ });
+
+ it("should return an error when the entry file cannot be resolved", async () => {
+ files = files.filter((file) => {
+ return file.name !== path.join(sampleProjectLocation, "index.js");
+ });
+
+ const result: TranspileResult = await webPackTranspiler.transpile(files);
+
+ expect(result.error).to.not.be.null;
+ expect(result.outputFiles).to.be.an("array").that.is.empty;
+ });
+
+ async function fetchTextFiles(dir: string, textFileArray?: Array): Promise> {
+ const results = await fs.readdir(dir);
+ textFileArray = textFileArray || [];
+
+ for(let key in results) {
+ if(results.hasOwnProperty(key)) {
+ const result = path.join(dir, results[key]);
+ const stats: fs.Stats = await fs.stat(result);
+
+ if(stats.isDirectory()) {
+ await fetchTextFiles(result, textFileArray);
+ } else {
+ textFileArray.push(await createTextFile(result));
+ }
+ }
+ }
+
+ return textFileArray;
+ }
+
+ async function createTextFile(fileName: string): Promise {
+ return {
+ name: fileName,
+ content: await fs.readFile(fileName, "utf8"),
+ included: true,
+ mutated: true,
+ transpiled: true,
+ kind: FileKind.Text
+ }
+ }
+
+ function createStrykerConfig(): TranspilerOptions {
+ const config: Config = new Config;
+
+ config.set(Object.assign(config, {
+ webpackConfig : {
+ entry: [path.resolve(path.resolve(sampleProjectLocation, "index.js"))],
+ output: {
+ path: "/out",
+ filename: "bundle.js",
+ }
+ }
+ }));
+
+ return {
+ config: config,
+ keepSourceMaps: false
+ }
+ }
+});
+
diff --git a/packages/stryker-webpack/test/unit/FsWrapperSpec.ts b/packages/stryker-webpack/test/unit/FsWrapperSpec.ts
new file mode 100644
index 0000000000..586c5d362f
--- /dev/null
+++ b/packages/stryker-webpack/test/unit/FsWrapperSpec.ts
@@ -0,0 +1,202 @@
+import FsWrapper from "../../src/helpers/FsWrapper";
+import * as sinon from "sinon";
+import { assert, expect } from "chai";
+import * as fs from "fs";
+import * as path from "path";
+
+describe("FsWrapper", () => {
+ let fsWrapper: FsWrapper;
+ let fsStubs: FsStubs;
+ let sandbox: sinon.SinonSandbox;
+
+ const fakeErr: Error = new Error("err");
+ const exampleFileContent = "Hello World!";
+
+ beforeEach(() => {
+ sandbox = sinon.sandbox.create();
+ fsStubs = createFsStubs();
+ fsWrapper = new FsWrapper(fs);
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ describe("readFile", () => {
+ it("should return a promise which resolves with \"" + exampleFileContent + "\"", async () => {
+ try {
+ const content: string = await fsWrapper.readFile("/path/to/file");
+
+ expect(content).to.equal(exampleFileContent);
+ } catch (err) { }
+ });
+
+ it("should call readFile on the given fs with path and \"utf8\"", async () => {
+ try {
+ const path: string = "/path/to/file";
+
+ await fsWrapper.readFile(path);
+
+ assert(fsStubs.readFile.calledWith(path, "utf8"));
+ } catch (err) { }
+ });
+
+ it("should reject with an error when the readFile function on the given fs returns an error", async () => {
+ fsStubs.readFile.callsArgWith(2, fakeErr);
+
+ try {
+ await fsWrapper.readFile("asdf");
+
+ assert(false, "Function should throw an error!");
+ } catch (err) {
+ console.log(err);
+
+ expect(err).to.equal(fakeErr);
+ }
+ });
+ });
+
+ describe("writeFile", () => {
+ it("should call writeFile on the given fs with path and content", async () => {
+ const examplePath: string = "/path/to/file";
+
+ try {
+ await fsWrapper.writeFile(examplePath, exampleFileContent);
+
+ assert(fsStubs.writeFile.calledWith(examplePath, exampleFileContent));
+ } catch (err) { }
+ });
+
+ it("should reject with an error when writeFile on the given fs returns with an error", async () => {
+ fsStubs.writeFile.callsArgWith(2, fakeErr);
+
+ try {
+ await fsWrapper.writeFile("asdf", "asdf");
+
+ assert(false, "Function should throw an error!");
+ } catch (err) {
+ expect(err).to.equal(fakeErr);
+ }
+ });
+ });
+
+ describe("mkdirp", () => {
+ const examplePath: string = "path/to/directory/and/file";
+ let pathElementCounter: number;
+
+ beforeEach(() => {
+ pathElementCounter = 0;
+ fsStubs.mkdir.callsFake((requestedPath: string, options: {}, callback: Function) => {
+ const elements: Array = requestedPath.split(path.sep);
+
+ if (elements.length > pathElementCounter + 1) {
+ callback(new FileSystemError({ code: "ENOENT" }));
+ } else if (elements.length === pathElementCounter) {
+ callback(new FileSystemError({ code: "EEXIST" }));
+ } else {
+ pathElementCounter++;
+
+ callback();
+ }
+ });
+ });
+
+ it("should call the mkdir function on the given fs with the given path", async () => {
+ await fsWrapper.mkdirp(examplePath);
+
+ assert(fsStubs.mkdir.calledWith(examplePath));
+ });
+
+ it("should bubble down and back up again", async () => {
+ const callCount: number = (examplePath.split(path.sep).length * 2) - 1;
+
+ await fsWrapper.mkdirp(examplePath);
+
+ assert(fsStubs.mkdir.callCount === callCount);
+ });
+
+ it("should reject with an error when a file exists in the given path", async () => {
+ pathElementCounter = examplePath.split(path.sep).length;
+
+ try {
+ await fsWrapper.mkdirp(examplePath);
+
+ assert(false, "Function should throw an error!");
+ } catch (err) {
+ expect(err.code).to.equal("EEXIST");
+ }
+ });
+
+ it("should not reject with an error when the directory already exists in the given path", async () => {
+ const examplePathArray: Array = examplePath.split(path.sep);
+
+ // Pop the last element, in this case "file" so isDirectory in the stat function will return true
+ examplePathArray.pop();
+
+ pathElementCounter = examplePathArray.length;
+
+ try {
+ await fsWrapper.mkdirp(examplePathArray.join(path.sep));
+
+ assert(true);
+ } catch (err) {
+ assert(false, "Function should not throw an error!");
+ }
+ });
+
+ it("should throw an error when an unknown error is thrown by mkdir", async () => {
+ fsStubs.mkdir.throws(new FileSystemError({code: "UNKNOWN"}));
+
+ try {
+ await fsWrapper.mkdirp("path/to/file");
+
+ assert(false, "Function should throw an error!");
+ } catch(err) {
+ assert(fsStubs.stat.notCalled);
+ expect(err.code).to.equal("UNKNOWN");
+ }
+ });
+
+ it("should throw an error when stat rejects with an error", async () => {
+ fsStubs.mkdir.throws(new FileSystemError({code: "EEXIST"}));
+
+ fsStubs.stat.callsArgWith(1, fakeErr, null);
+
+ try {
+ await fsWrapper.mkdirp("path/to/file");
+
+ assert(false, "Function should throw an error!");
+ } catch(err) {
+ expect(err).to.equal(fakeErr);
+ }
+ });
+ });
+
+ function createFsStubs(): FsStubs {
+ return {
+ readFile: sandbox.stub(fs, "readFile").callsArgWith(2, null, exampleFileContent),
+ writeFile: sandbox.stub(fs, "writeFile").callsArgWith(2, null),
+ stat: sandbox.stub(fs, "stat").callsFake((subject: string, callback: Function) => callback(null, {
+ isDirectory: () => path.basename(subject) !== "file"
+ })),
+ mkdir: sandbox.stub(fs, "mkdir")
+ };
+ }
+});
+
+class FileSystemError extends Error {
+ public code: string;
+
+ public constructor(err: { code: string }) {
+ super("err");
+
+ this.code = err.code;
+ }
+}
+
+interface FsStubs {
+ readFile: sinon.SinonStub;
+ writeFile: sinon.SinonStub;
+ stat: sinon.SinonStub;
+ mkdir: sinon.SinonStub;
+}
\ No newline at end of file
diff --git a/packages/stryker-webpack/test/unit/WebpackCompilerSpec.ts b/packages/stryker-webpack/test/unit/WebpackCompilerSpec.ts
new file mode 100644
index 0000000000..2af9ba33b1
--- /dev/null
+++ b/packages/stryker-webpack/test/unit/WebpackCompilerSpec.ts
@@ -0,0 +1,142 @@
+import {assert, expect} from "chai";
+import * as sinon from "sinon";
+import {createFakeFileSystem, createFakeWebpackConfig, createTextFile, createWebpackMock} from "../helpers/producers";
+import {WebpackCompilerMock} from "../helpers/mockInterfaces";
+import FsWrapper, * as fsWrapper from "../../src/helpers/FsWrapper";
+import WebpackCompiler from "../../src/compiler/WebpackCompiler";
+import {TextFile, FileKind} from "stryker-api/core";
+import * as path from "path";
+import * as webpack from "../../src/compiler/Webpack";
+import {Configuration} from "webpack";
+
+describe("WebpackCompiler", () => {
+ let webpackCompiler: WebpackCompiler;
+ let sandbox: sinon.SinonSandbox;
+ let fsWrapperStubs: FsWrapperStubs;
+ let webpackCompilerMock: WebpackCompilerMock;
+
+ let fakeTextFileArray: Array = createFakeTextFileArray();
+ let fakeWebpackConfig: Configuration = createFakeWebpackConfig();
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+
+ fsWrapperStubs = sinon.createStubInstance(FsWrapper);
+ webpackCompilerMock = createWebpackMock();
+
+ sandbox.stub(webpack, "default").returns(webpackCompilerMock);
+ sandbox.stub(fsWrapper, "default").returns(fsWrapperStubs);
+
+ webpackCompiler = new WebpackCompiler(fakeWebpackConfig, createFakeFileSystem());
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ describe("construct", () => {
+ it("should have a compiler with the cache flag set to true", () => {
+ expect(fakeWebpackConfig.cache).to.equal(true);
+ });
+ });
+
+ describe("replace", () => {
+ it("should call the mkdirp function on the fsWrapper with the basedir of the given file", async () => {
+ await webpackCompiler.replace(fakeTextFileArray);
+
+ fakeTextFileArray.forEach((textFile, index) => {
+ assert(fsWrapperStubs.mkdirp.getCall(index).calledWith(path.dirname(textFile.name)));
+ });
+ });
+
+ it("should call the writeFile function on the fsWrapper with the given file", async () => {
+ await webpackCompiler.replace(fakeTextFileArray);
+
+ fakeTextFileArray.forEach((textFile, index) => {
+ assert(fsWrapperStubs.writeFile.getCall(index).calledWith(textFile.name));
+ });
+ });
+ });
+
+ describe("emit", () => {
+ let webpackRunStub: sinon.SinonStub;
+
+ beforeEach(() => {
+ webpackRunStub = sandbox.stub(webpackCompilerMock, "run").callsArgWith(0, null, { hasErrors: () => false });
+ });
+
+ it("should call the run function on the webpack compiler", async () => {
+ await webpackCompiler.emit();
+
+ assert(webpackRunStub.calledOnce);
+ });
+
+ it("should call the readFile function on the fsWrapper with the bundle path", async () => {
+ await webpackCompiler.emit();
+
+ assert(fsWrapperStubs.readFile.calledWith("/out/bundle.js"));
+ });
+
+ it("should return a new TextFile array with the bundle in it", async () => {
+ const content: string = "Hello World!";
+ fsWrapperStubs.readFile.resolves(content);
+
+ const files: Array = await webpackCompiler.emit();
+
+ expect(files).to.deep.equal([{
+ name: "bundle.js",
+ content: content,
+ mutated: true,
+ included: true,
+ transpiled: true,
+ kind: FileKind.Text
+ }]);
+ });
+
+ it("should return an error when the webpack compiler fails to compile", async () => {
+ const fakeError: string = "fakeError";
+ webpackRunStub.callsArgWith(0, new Error(fakeError));
+
+ try {
+ await webpackCompiler.emit();
+
+ assert(false, "Function should throw an error!");
+ } catch(err) {
+ expect(err.name).to.equal("Error");
+ expect(err.message).to.equal(fakeError);
+ }
+ });
+
+ it("should return a string representation of the error when the compiler has errors", async () => {
+ const fakeError: string = "fakeError";
+ webpackRunStub.callsArgWith(0, null, {
+ hasErrors: () => true,
+ toString: () => fakeError
+ });
+
+ try {
+ await webpackCompiler.emit();
+
+ assert(false, "Function should throw an error!");
+ } catch(err) {
+ expect(err.name).to.equal("Error");
+ expect(err.message).to.equal(fakeError);
+ }
+ });
+ });
+
+ function createFakeTextFileArray(): Array {
+ return [
+ createTextFile("path/to/awesome/directory/file1"),
+ createTextFile("path/to/awesome/directory/file2"),
+ createTextFile("path/to/awesome/directory/file3"),
+ createTextFile("path/to/awesome/directory/file4")
+ ];
+ }
+});
+
+interface FsWrapperStubs {
+ readFile: sinon.SinonStub;
+ writeFile: sinon.SinonStub;
+ mkdirp: sinon.SinonStub;
+}
\ No newline at end of file
diff --git a/packages/stryker-webpack/test/unit/WebpackTranspilerSpec.ts b/packages/stryker-webpack/test/unit/WebpackTranspilerSpec.ts
new file mode 100644
index 0000000000..e354ca866b
--- /dev/null
+++ b/packages/stryker-webpack/test/unit/WebpackTranspilerSpec.ts
@@ -0,0 +1,91 @@
+import {expect, assert} from "chai";
+import {Config} from "stryker-api/config";
+import {TranspileResult} from "stryker-api/transpile";
+import {Position, TextFile} from "stryker-api/core";
+import {createTextFile} from "../helpers/producers";
+import * as sinon from "sinon";
+import WebpackTranspiler from "../../src/WebpackTranspiler";
+import WebpackCompiler, * as webpackCompiler from "../../src/compiler/WebpackCompiler";
+
+describe("WebpackTranspiler", () => {
+ const sandbox: sinon.SinonSandbox = sinon.createSandbox();
+ let webpackTranspiler: WebpackTranspiler;
+ let webpackCompilerService: WebpackCompilerStubs;
+
+ let fakeFileArray: Array;
+
+ beforeEach(() => {
+ webpackCompilerService = sinon.createStubInstance(WebpackCompiler);
+
+ sandbox.stub(webpackCompiler, 'default').returns(webpackCompilerService);
+
+ webpackTranspiler = new WebpackTranspiler({config: new Config, keepSourceMaps: false});
+
+ fakeFileArray = [
+ createTextFile("test"),
+ createTextFile("works")
+ ];
+ });
+
+ afterEach(() => {
+ sandbox.restore();
+ });
+
+ describe("transpile", () => {
+ it("should call the \"replace\" function on the \"webpackCompiler\"", async () => {
+ await webpackTranspiler.transpile(fakeFileArray);
+
+ assert(webpackCompilerService.replace.calledWith(fakeFileArray));
+ });
+
+ it("should call the \"emit\" function on the \"webpackCompiler\"", async () => {
+ await webpackTranspiler.transpile([]);
+
+ assert(webpackCompilerService.emit.calledOnce);
+ });
+
+ it("should send a successResponse when finished", async () => {
+ const fakeEmitFiles: Array = [createTextFile("bundle.js")];
+
+ webpackCompilerService.emit.resolves(fakeEmitFiles);
+
+ const result: TranspileResult = await webpackTranspiler.transpile(fakeFileArray);
+
+ expect(result.outputFiles).to.deep.equal(fakeEmitFiles);
+ expect(result.error).to.be.null;
+ });
+
+ it("should send a errorResponse when the webpackCompiler throws an error", async () => {
+ const fakeError: string = "fakeError";
+
+ webpackCompilerService.emit.throwsException(Error(fakeError));
+
+ const result: TranspileResult = await webpackTranspiler.transpile(fakeFileArray);
+
+ expect(result.outputFiles).to.be.an("array").that.is.empty;
+ expect(result.error).to.equal(`Error: ${fakeError}`);
+ });
+ });
+
+ describe("getMappedLocation", () => {
+ it("should throw an error informing the user the function is not implemented", () => {
+ const position: Position = {
+ line: 0,
+ column: 0
+ };
+
+ const fileLocation: { fileName: string, start: Position, end: Position } = {
+ fileName: "test",
+ start: position,
+ end: position
+ }
+
+ expect(webpackTranspiler.getMappedLocation.bind(this, fileLocation)).to.throw(Error, "Method not implemented.");
+ });
+ })
+});
+
+interface WebpackCompilerStubs {
+ replace: sinon.SinonStub;
+ emit: sinon.SinonStub;
+}
\ No newline at end of file
diff --git a/packages/stryker-webpack/testResources/sampleProject/index.js b/packages/stryker-webpack/testResources/sampleProject/index.js
new file mode 100644
index 0000000000..9064816636
--- /dev/null
+++ b/packages/stryker-webpack/testResources/sampleProject/index.js
@@ -0,0 +1,13 @@
+const sum = require("./lib/sum");
+const square = require("./lib/square");
+const divide = require("./lib/divide");
+
+const answerArray = [];
+
+[1,2,3,4].map(function(number) {
+ answerArray.push(sum(number, number));
+ answerArray.push(square(number, number));
+ answerArray.push(divide(number, number));
+});
+
+console.log(answerArray);
\ No newline at end of file
diff --git a/packages/stryker-webpack/testResources/sampleProject/lib/divide.js b/packages/stryker-webpack/testResources/sampleProject/lib/divide.js
new file mode 100644
index 0000000000..476a1bc63c
--- /dev/null
+++ b/packages/stryker-webpack/testResources/sampleProject/lib/divide.js
@@ -0,0 +1,3 @@
+module.exports = function(number) {
+ return number / number;
+};
\ No newline at end of file
diff --git a/packages/stryker-webpack/testResources/sampleProject/lib/square.js b/packages/stryker-webpack/testResources/sampleProject/lib/square.js
new file mode 100644
index 0000000000..19760a9764
--- /dev/null
+++ b/packages/stryker-webpack/testResources/sampleProject/lib/square.js
@@ -0,0 +1,3 @@
+module.exports = function(number) {
+ return number * number;
+};
\ No newline at end of file
diff --git a/packages/stryker-webpack/testResources/sampleProject/lib/sum.js b/packages/stryker-webpack/testResources/sampleProject/lib/sum.js
new file mode 100644
index 0000000000..12428ac561
--- /dev/null
+++ b/packages/stryker-webpack/testResources/sampleProject/lib/sum.js
@@ -0,0 +1,3 @@
+module.exports = function(number) {
+ return number + number;
+};
\ No newline at end of file