Skip to content

Commit

Permalink
feat(TypeScript mutator): mutate Array constructor calls without the …
Browse files Browse the repository at this point in the history
…new keyword (#1903)

fixes #1902
  • Loading branch information
Chris Brody authored and simondel committed Dec 1, 2019
1 parent e4c0366 commit aecd944
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 11 deletions.
12 changes: 10 additions & 2 deletions packages/mutator-specification/src/ArrayDeclarationMutatorSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,26 @@ export default function ArrayDeclarationMutatorSpec(name: string, expectMutation
expectMutation('[]', '["Stryker was here"]');
});

it('should mutate filled array literals as empty arrays', () => {
it('should mutate filled Array constructor calls as empty arrays', () => {
expectMutation('new Array(a, 1 + 1)', 'new Array()');
expectMutation("new Array('val')", 'new Array()');
expectMutation("Array('val')", 'Array()');
expectMutation('Array(a, 1 + 1)', 'Array()');
});

it('should not mutate other new expressions', () => {
expectMutation('new Object(21, 2)');
expectMutation('new Arrays(21, 2)');
});

it('should mutate empty array literals as a filled array', () => {
it('should mutate empty array constructor call as a filled array', () => {
expectMutation('new Array()', 'new Array([])');
expectMutation('Array()', 'Array([])');
});

it('should not mutate other function call expressions', () => {
expectMutation('window.Array(21, 2)');
expectMutation('window["Array"](21, 2)');
});
});
}
24 changes: 15 additions & 9 deletions packages/typescript/src/mutator/ArrayDeclarationMutator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,33 @@ import * as ts from 'typescript';

import NodeMutator, { NodeReplacement } from './NodeMutator';

export default class ArrayDeclarationMutator extends NodeMutator<ts.ArrayLiteralExpression | ts.NewExpression> {
export default class ArrayDeclarationMutator extends NodeMutator<ts.ArrayLiteralExpression | ts.CallExpression | ts.NewExpression> {
public name = 'ArrayDeclaration';

public guard(node: ts.Node): node is ts.ArrayLiteralExpression | ts.NewExpression {
return node.kind === ts.SyntaxKind.ArrayLiteralExpression || node.kind === ts.SyntaxKind.NewExpression;
public guard(node: ts.Node): node is ts.ArrayLiteralExpression | ts.CallExpression | ts.NewExpression {
return (
node.kind === ts.SyntaxKind.ArrayLiteralExpression || node.kind === ts.SyntaxKind.CallExpression || node.kind === ts.SyntaxKind.NewExpression
);
}

protected identifyReplacements(node: ts.ArrayLiteralExpression | ts.NewExpression, sourceFile: ts.SourceFile): NodeReplacement[] {
protected identifyReplacements(
node: ts.ArrayLiteralExpression | ts.CallExpression | ts.NewExpression,
sourceFile: ts.SourceFile
): NodeReplacement[] {
if (node.kind === ts.SyntaxKind.ArrayLiteralExpression) {
if (node.elements.length) {
return [{ node, replacement: '[]' }];
} else {
return [{ node, replacement: '["Stryker was here"]' }];
}
} else if (node.kind === ts.SyntaxKind.CallExpression && node.expression.kind !== ts.SyntaxKind.Identifier) {
// extra guard in case of a function call expression
return [];
} else {
if (node.expression.getFullText(sourceFile).trim() === 'Array') {
if (node.arguments && node.arguments.length) {
return [{ node, replacement: 'new Array()' }];
} else {
return [{ node, replacement: 'new Array([])' }];
}
const newPrefix = node.kind === ts.SyntaxKind.NewExpression ? 'new ' : '';
const mutatedCallArgs = node.arguments && node.arguments.length ? '' : '[]';
return [{ node, replacement: `${newPrefix}Array(${mutatedCallArgs})` }];
} else {
return [];
}
Expand Down

0 comments on commit aecd944

Please sign in to comment.