-
Notifications
You must be signed in to change notification settings - Fork 251
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(JavaScript mutator): Add stryker-javascript-mutator package (#467)
* Add stryker-javascript-mutator package that is compatible with modern JavaScript features such as arrow functions. * Deprecate the `ES5Mutator` * Remove `UnaryNotMutator` as it is already present in the `PrefixUnaryExpressionMutator` * Make the `ConditionalExpressionMutator` also mutate to `true` Fixes #429
- Loading branch information
Showing
60 changed files
with
1,082 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
**/* | ||
!*.d.ts | ||
!bin/** | ||
!src/** | ||
src/**/*.map | ||
src/**/*.ts | ||
!src/**/*.d.ts | ||
!readme.md | ||
!LICENSE | ||
!CHANGELOG.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
{ | ||
"version": "0.2.0", | ||
"configurations": [ | ||
{ | ||
"type": "node", | ||
"request": "launch", | ||
"name": "Unit tests", | ||
"program": "${workspaceRoot}/../../node_modules/mocha/bin/_mocha", | ||
"args": [ | ||
"-u", | ||
"tdd", | ||
"--timeout", | ||
"999999", | ||
"--colors", | ||
"${workspaceRoot}/test/helpers/**/*.js", | ||
"${workspaceRoot}/test/unit/**/*.js" | ||
], | ||
"internalConsoleOptions": "openOnSessionStart" | ||
}, { | ||
"type": "node", | ||
"request": "launch", | ||
"name": "Integration tests", | ||
"program": "${workspaceRoot}/../../node_modules/mocha/bin/_mocha", | ||
"args": [ | ||
"-u", | ||
"tdd", | ||
"--timeout", | ||
"999999", | ||
"--colors", | ||
"${workspaceRoot}/test/helpers/**/*.js", | ||
"${workspaceRoot}/test/integration/**/*.js" | ||
], | ||
"internalConsoleOptions": "openOnSessionStart" | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"typescript.tsdk": "../../node_modules/typescript/lib", | ||
"files.exclude": { | ||
".git": true, | ||
".tscache": true, | ||
"**/*.js": { | ||
"when": "$(basename).ts" | ||
}, | ||
"**/*.d.ts": true, | ||
"**/*.map": { | ||
"when": "$(basename)" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
{ | ||
// See https://go.microsoft.com/fwlink/?LinkId=733558 | ||
// for the documentation about the tasks.json format | ||
"version": "2.0.0", | ||
"tasks": [ | ||
{ | ||
"taskName": "tsc-watch", | ||
"type": "shell", | ||
"command": "npm start", | ||
"problemMatcher": "$tsc-watch", | ||
"isBackground": true, | ||
"group": { | ||
"kind": "build", | ||
"isDefault": true | ||
} | ||
} | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
[![Build Status](https://travis-ci.org/stryker-mutator/stryker.svg?branch=master)](https://travis-ci.org/stryker-mutator/stryker) | ||
[![NPM](https://img.shields.io/npm/dm/stryker-javascript-mutator.svg)](https://www.npmjs.com/package/stryker-javascript-mutator) | ||
[![Node version](https://img.shields.io/node/v/stryker-javascript-mutator.svg)](https://img.shields.io/node/v/stryker-javascript-mutator.svg) | ||
[![Gitter](https://badges.gitter.im/stryker-mutator/stryker.svg)](https://gitter.im/stryker-mutator/stryker?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) | ||
[![BCH compliance](https://bettercodehub.com/edge/badge/stryker-mutator/stryker)](https://bettercodehub.com/) | ||
|
||
![Stryker](https://github.com/stryker-mutator/stryker/raw/master/stryker-80x80.png) | ||
|
||
# Stryker JavaScript mutator | ||
|
||
A mutator that supports JavaScript for [Stryker](https://stryker-mutator.github.io), the JavaScript Mutation testing framework. This plugin does not transpile any code. The code that the stryker-javascript-mutator gets should be executable in your environment (i.e. the stryker-javascript-mutator does not add support for Babel projects). | ||
|
||
## Quickstart | ||
|
||
First, install Stryker itself (you can follow the [quickstart on the website](http://stryker-mutator.github.io/quickstart.html)) | ||
|
||
Next, install this package: | ||
|
||
```bash | ||
npm install --save-dev stryker-javascript-mutator | ||
``` | ||
|
||
Now open up your stryker.conf.js file and add the following components: | ||
|
||
```javascript | ||
mutator: 'javascript', | ||
``` | ||
|
||
Now give it a go: | ||
|
||
```bash | ||
$ stryker run | ||
``` | ||
|
||
### JavaScript Mutator | ||
|
||
The `JavaScript Mutator` is a plugin to mutate JavaScript code. This is done using Babel without any plugins. | ||
|
||
See [test code](https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-javascript-mutator/test/unit/mutator) to know which mutations are supported. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
{ | ||
"name": "stryker-javascript-mutator", | ||
"version": "0.1.0", | ||
"description": "A plugin for javascript projects using Stryker", | ||
"main": "src/index.js", | ||
"scripts": { | ||
"start": "tsc -w", | ||
"clean": "rimraf \"+(test|src)/**/*+(.d.ts|.js|.map)\" reports", | ||
"prebuild": "npm run clean", | ||
"build": "tsc -p .", | ||
"postbuild": "tslint -p tsconfig.json", | ||
"test": "nyc --reporter=html --report-dir=reports/coverage --check-coverage --lines 85 --functions 90 --branches 60 mocha \"test/helpers/**/*.js\" \"test/unit/**/*.js\" " | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/stryker-mutator/stryker" | ||
}, | ||
"engines": { | ||
"node": ">=4" | ||
}, | ||
"keywords": [ | ||
"stryker", | ||
"stryker-plugin", | ||
"javascript", | ||
"stryker-mutator" | ||
], | ||
"bugs": { | ||
"url": "https://github.com/stryker-mutator/stryker/issues" | ||
}, | ||
"author": "Simon de Lang <[email protected]>", | ||
"contributors": [ | ||
"Nico Jansen <[email protected]>", | ||
"Niek te Grootenhuis <[email protected]>", | ||
"Thomas Peters <[email protected]>", | ||
"Sander Koenders <[email protected]>" | ||
], | ||
"homepage": "https://github.com/stryker-mutator/stryker/tree/master/packages/stryker-javascript-mutator#readme", | ||
"license": "Apache-2.0", | ||
"dependencies": { | ||
"babel-core": "^6.26.0", | ||
"babel-generator": "^6.26.0", | ||
"babylon": "^6.18.0", | ||
"log4js": "^1.1.1", | ||
"tslib": "^1.8.0" | ||
}, | ||
"devDependencies": { | ||
"@types/babel-generator": "^6.25.1", | ||
"@types/babylon": "^6.16.2", | ||
"stryker-api": "^0.11.0", | ||
"stryker-mutator-specification": "^0.1.0" | ||
}, | ||
"peerDependencies": { | ||
"stryker-api": "^0.11.0", | ||
"typescript": "^2.5.3" | ||
} | ||
} |
65 changes: 65 additions & 0 deletions
65
packages/stryker-javascript-mutator/src/JavaScriptMutator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
import * as babel from 'babel-core'; | ||
import { getLogger } from 'log4js'; | ||
import { Mutator, Mutant } from 'stryker-api/mutant'; | ||
import { File, FileKind, TextFile } from 'stryker-api/core'; | ||
import { Config } from 'stryker-api/config'; | ||
import BabelParser from './helpers/BabelParser'; | ||
import copy from './helpers/copy'; | ||
import NodeMutatorFactory from './NodeMutatorFactory'; | ||
import NodeMutator from './mutators/NodeMutator'; | ||
|
||
function defaultMutators(): NodeMutator[] { | ||
return NodeMutatorFactory.instance().knownNames().map(name => NodeMutatorFactory.instance().create(name, undefined)); | ||
} | ||
|
||
export default class JavaScriptMutator implements Mutator { | ||
private log = getLogger(JavaScriptMutator.name); | ||
|
||
constructor(config: Config, private mutators: NodeMutator[] = defaultMutators()) { | ||
} | ||
|
||
public mutate(inputFiles: File[]): Mutant[] { | ||
const mutants: Mutant[] = []; | ||
|
||
inputFiles.filter(i => i.kind === FileKind.Text && i.mutated).forEach((file: TextFile) => { | ||
const ast = BabelParser.getAst(file.content); | ||
const baseAst = copy(ast, true); | ||
BabelParser.removeUseStrict(baseAst); | ||
|
||
BabelParser.getNodes(ast).forEach(node => { | ||
this.mutators.forEach(mutator => { | ||
let mutatedNodes = mutator.mutate(node, copy); | ||
|
||
if (mutatedNodes) { | ||
const newMutants = this.generateMutants(mutatedNodes, baseAst, file, mutator.name); | ||
newMutants.forEach(mutant => mutants.push(mutant)); | ||
} | ||
}); | ||
}); | ||
}); | ||
|
||
return mutants; | ||
} | ||
|
||
private generateMutants(nodes: babel.types.Node[], ast: babel.types.File, file: TextFile, mutatorName: string) { | ||
const mutants: Mutant[] = []; | ||
|
||
nodes.forEach(node => { | ||
const replacement = BabelParser.generateCode(ast, node); | ||
if (replacement) { | ||
const range: [number, number] = [node.start, node.end]; | ||
|
||
const mutant = { | ||
mutatorName, | ||
fileName: file.name, | ||
range, | ||
replacement | ||
}; | ||
this.log.trace(`Generated mutant for mutator ${mutatorName} in file ${file.name} with replacement code "${replacement}"`); | ||
mutants.push(mutant); | ||
} | ||
}); | ||
|
||
return mutants; | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
packages/stryker-javascript-mutator/src/NodeMutatorFactory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { Factory } from 'stryker-api/core'; | ||
import NodeMutator from './mutators/NodeMutator'; | ||
|
||
namespace NodeMutatorFactory { | ||
/** | ||
* Represents a Factory for TestFrameworks. | ||
*/ | ||
class NodeMutatorFactory extends Factory<void, NodeMutator> { | ||
constructor() { | ||
super('nodeMutator'); | ||
} | ||
} | ||
const nodeMutatorFactoryInstance = new NodeMutatorFactory(); | ||
|
||
/** | ||
* Returns the current instance of the MutatorFactory. | ||
*/ | ||
export function instance() { | ||
return <Factory<void, NodeMutator>>nodeMutatorFactoryInstance; | ||
} | ||
} | ||
|
||
export default NodeMutatorFactory; |
40 changes: 40 additions & 0 deletions
40
packages/stryker-javascript-mutator/src/helpers/BabelParser.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import * as babel from 'babel-core'; | ||
import * as babylon from 'babylon'; | ||
import generate from 'babel-generator'; | ||
import { NodePath } from 'babel-traverse'; | ||
|
||
export default class BabelParser { | ||
static getAst(code: string): babel.types.File { | ||
return babylon.parse(code); | ||
} | ||
|
||
static getNodes(ast: babel.types.File): babel.types.Node[] { | ||
const nodes: babel.types.Node[] = []; | ||
|
||
babel.traverse(ast, { | ||
enter(path: NodePath<babel.types.Node>) { | ||
const node = path.node; | ||
Object.freeze(node); | ||
nodes.push(node); | ||
} | ||
}); | ||
|
||
return nodes; | ||
} | ||
|
||
static generateCode(ast: babel.types.File, node: babel.Node) { | ||
ast.program.body = [node as any]; | ||
return generate(ast).code; | ||
} | ||
|
||
static removeUseStrict(ast: babel.types.File) { | ||
if (ast.program.directives) { | ||
const directives = ast.program.directives; | ||
directives.forEach((directive, index) => { | ||
if (directive.value.value === 'use strict') { | ||
directives.splice(index, 1); | ||
} | ||
}); | ||
} | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
packages/stryker-javascript-mutator/src/helpers/NodeGenerator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { types } from 'babel-core'; | ||
|
||
export default class NodeGenerator { | ||
static createBooleanLiteralNode(originalNode: types.Node, value: boolean): types.BooleanLiteral { | ||
return { | ||
start: originalNode.start, | ||
end: originalNode.end, | ||
loc: originalNode.loc, | ||
type: 'BooleanLiteral', | ||
value: value | ||
}; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import * as _ from 'lodash'; | ||
|
||
export default <T>(obj: T, deep?: boolean) => { | ||
if (deep) { | ||
return _.cloneDeep(obj); | ||
} else { | ||
return _.clone(obj); | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { MutatorFactory } from 'stryker-api/mutant'; | ||
import JavaScriptMutator from './JavaScriptMutator'; | ||
require('./mutators'); | ||
|
||
MutatorFactory.instance().register('javascript', JavaScriptMutator); |
21 changes: 21 additions & 0 deletions
21
packages/stryker-javascript-mutator/src/mutators/ArrayLiteralMutator.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { types } from 'babel-core'; | ||
import NodeMutator from './NodeMutator'; | ||
|
||
/** | ||
* Represents a mutator which can remove the content of an array's elements. | ||
*/ | ||
export default class ArrayLiteralMutator implements NodeMutator { | ||
name = 'ArrayLiteral'; | ||
|
||
mutate(node: types.Node, copy: <T extends types.Node>(obj: T, deep?: boolean) => T): void | types.Node[] { | ||
let nodes: types.Node[] = []; | ||
|
||
if (types.isArrayExpression(node) && node.elements.length > 0) { | ||
let mutatedNode = copy(node); | ||
mutatedNode.elements = []; | ||
nodes.push(mutatedNode); | ||
} | ||
|
||
return nodes; | ||
} | ||
} |
Oops, something went wrong.