diff --git a/packages/mutator-specification/src/ArrayDeclarationMutatorSpec.ts b/packages/mutator-specification/src/ArrayDeclarationMutatorSpec.ts index 477e97352b..f5d2e2bfb8 100644 --- a/packages/mutator-specification/src/ArrayDeclarationMutatorSpec.ts +++ b/packages/mutator-specification/src/ArrayDeclarationMutatorSpec.ts @@ -17,9 +17,11 @@ 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', () => { @@ -27,8 +29,14 @@ export default function ArrayDeclarationMutatorSpec(name: string, expectMutation 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)'); }); }); } diff --git a/packages/typescript/src/mutator/ArrayDeclarationMutator.ts b/packages/typescript/src/mutator/ArrayDeclarationMutator.ts index 60c0a04cf9..8b7408b0a2 100644 --- a/packages/typescript/src/mutator/ArrayDeclarationMutator.ts +++ b/packages/typescript/src/mutator/ArrayDeclarationMutator.ts @@ -2,27 +2,33 @@ import * as ts from 'typescript'; import NodeMutator, { NodeReplacement } from './NodeMutator'; -export default class ArrayDeclarationMutator extends NodeMutator { +export default class ArrayDeclarationMutator extends NodeMutator { 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 []; }