diff --git a/e2e/package-lock.json b/e2e/package-lock.json index e3ee9745af..7d6be68ac6 100644 --- a/e2e/package-lock.json +++ b/e2e/package-lock.json @@ -515,6 +515,23 @@ "@babel/helper-plugin-utils": "^7.8.0" } }, + "@babel/plugin-syntax-flow": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.12.13.tgz", + "integrity": "sha512-J/RYxnlSLXZLVR7wTRsozxKT8qbsx1mNKJzXEEjQ0Kjx1ZACcyHgbanNWNCFtc36IzuWhYWPpvJFFoexoOWFmA==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", + "dev": true + } + } + }, "@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", @@ -731,6 +748,24 @@ "@babel/helper-plugin-utils": "^7.10.1" } }, + "@babel/plugin-transform-flow-strip-types": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-flow-strip-types/-/plugin-transform-flow-strip-types-7.13.0.tgz", + "integrity": "sha512-EXAGFMJgSX8gxWD7PZtW/P6M+z74jpx3wm/+9pn+c2dOawPpBkUX7BrfyPvo6ZpXbgRIEuwgwDb/MGlKvu2pOg==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.13.0", + "@babel/plugin-syntax-flow": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", + "dev": true + } + } + }, "@babel/plugin-transform-for-of": { "version": "7.10.1", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-for-of/-/plugin-transform-for-of-7.10.1.tgz", @@ -1008,6 +1043,24 @@ } } }, + "@babel/preset-flow": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/preset-flow/-/preset-flow-7.12.13.tgz", + "integrity": "sha512-gcEjiwcGHa3bo9idURBp5fmJPcyFPOszPQjztXrOjUE2wWVqc6fIVJPgWPIQksaQ5XZ2HWiRsf2s1fRGVjUtVw==", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.12.13", + "@babel/plugin-transform-flow-strip-types": "^7.12.13" + }, + "dependencies": { + "@babel/helper-plugin-utils": { + "version": "7.13.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.13.0.tgz", + "integrity": "sha512-ZPafIPSwzUlAoWT8DKs1W2VyF2gOWthGd5NGFMsBcMMol+ZhK+EQY/e6V96poa6PA/Bh+C9plWN0hXO1uB8AfQ==", + "dev": true + } + } + }, "@babel/runtime": { "version": "7.10.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.10.2.tgz", diff --git a/e2e/package.json b/e2e/package.json index ad4bc39f9e..cfe46a7963 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -8,6 +8,7 @@ "@babel/plugin-proposal-class-properties": "~7.8.3", "@babel/plugin-proposal-pipeline-operator": "~7.8.3", "@babel/preset-env": "~7.8.3", + "@babel/preset-flow": "7.12.13", "@types/node": "^10.12.18", "@types/semver": "~6.2.0", "ajv": "~7.0.2", diff --git a/e2e/test/flow-test-project/.babelrc b/e2e/test/flow-test-project/.babelrc new file mode 100644 index 0000000000..75412a8780 --- /dev/null +++ b/e2e/test/flow-test-project/.babelrc @@ -0,0 +1,16 @@ +{ + "presets": [ + "@babel/preset-flow", + [ + "@babel/preset-env", + { + "targets": { + "edge": "16", + "firefox": "57", + "chrome": "62", + "safari": "11" + } + } + ] + ] +} diff --git a/e2e/test/flow-test-project/package-lock.json b/e2e/test/flow-test-project/package-lock.json new file mode 100644 index 0000000000..3e3fc7d776 --- /dev/null +++ b/e2e/test/flow-test-project/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "flow-jest-project", + "version": "0.0.0", + "lockfileVersion": 1 +} diff --git a/e2e/test/flow-test-project/package.json b/e2e/test/flow-test-project/package.json new file mode 100644 index 0000000000..4c971c297f --- /dev/null +++ b/e2e/test/flow-test-project/package.json @@ -0,0 +1,11 @@ +{ + "name": "flow-jest-project", + "version": "0.0.0", + "private": true, + "description": "A module to test a flow project, see https://flow.org/", + "scripts": { + "test:unit": "jest", + "test": "stryker run", + "posttest": "mocha --no-config --require ../../tasks/ts-node-register.js verify/*.ts" + } +} diff --git a/e2e/test/flow-test-project/src/square.js b/e2e/test/flow-test-project/src/square.js new file mode 100644 index 0000000000..c20087effa --- /dev/null +++ b/e2e/test/flow-test-project/src/square.js @@ -0,0 +1,6 @@ +// @flow +export function square(n: number): number { + const result: Array = []; + const cat = new Set(); + return n * n; +} diff --git a/e2e/test/flow-test-project/src/square.spec.js b/e2e/test/flow-test-project/src/square.spec.js new file mode 100644 index 0000000000..9549ccb346 --- /dev/null +++ b/e2e/test/flow-test-project/src/square.spec.js @@ -0,0 +1,8 @@ +import { square } from './square'; +import { expect } from 'chai'; + +describe('square', () => { + it('should provide 4 when given 2', () => { + expect(square(2)).eq(4); + }); +}); diff --git a/e2e/test/flow-test-project/stryker.conf.json b/e2e/test/flow-test-project/stryker.conf.json new file mode 100644 index 0000000000..af7e8281b0 --- /dev/null +++ b/e2e/test/flow-test-project/stryker.conf.json @@ -0,0 +1,13 @@ +{ + "$schema": "../../node_modules/@stryker-mutator/core/schema/stryker-schema.json", + "reporters": [ + "clear-text", + "event-recorder" + ], + "concurrency": 2, + "commandRunner": { + "command": "npm run test:unit" + }, + "symlinkNodeModules": false, + "fileLogLevel": "info" +} diff --git a/e2e/test/flow-test-project/verify/.babelrc b/e2e/test/flow-test-project/verify/.babelrc new file mode 100644 index 0000000000..75412a8780 --- /dev/null +++ b/e2e/test/flow-test-project/verify/.babelrc @@ -0,0 +1,16 @@ +{ + "presets": [ + "@babel/preset-flow", + [ + "@babel/preset-env", + { + "targets": { + "edge": "16", + "firefox": "57", + "chrome": "62", + "safari": "11" + } + } + ] + ] +} diff --git a/e2e/test/flow-test-project/verify/verify.ts b/e2e/test/flow-test-project/verify/verify.ts new file mode 100644 index 0000000000..985880a601 --- /dev/null +++ b/e2e/test/flow-test-project/verify/verify.ts @@ -0,0 +1,19 @@ +import { expectMetrics } from '../../../helpers'; + +describe('After running stryker on jest-react project', () => { + it('should report expected scores', async () => { + await expectMetrics({ + killed: 2, + ignored: 0, + survived: 1, + mutationScore: 66.67, + }); + /* + -----------|---------|----------|-----------|------------|----------|---------| + File | % score | # killed | # timeout | # survived | # no cov | # error | + -----------|---------|----------|-----------|------------|----------|---------| + All files | 66.67 | 2 | 0 | 1 | 0 | 0 | + square.js | 66.67 | 2 | 0 | 1 | 0 | 0 | + -----------|---------|----------|-----------|------------|----------|---------|*/ + }); +}); diff --git a/packages/instrumenter/src/transformers/babel-transformer.ts b/packages/instrumenter/src/transformers/babel-transformer.ts index e064c69674..a5d7921fff 100644 --- a/packages/instrumenter/src/transformers/babel-transformer.ts +++ b/packages/instrumenter/src/transformers/babel-transformer.ts @@ -36,6 +36,11 @@ export const transformBabel: AstTransformer = ({ ro }, }); if (mutantCollector.hasPlacedMutants(originFileName)) { + const innerComments = root.program.innerComments ?? []; + const leadingComments = root.program.body[0]?.leadingComments ?? []; + if (Array.isArray(leadingComments)) { + instrumentationBabelHeader[0].leadingComments = [...innerComments, ...leadingComments]; + } root.program.body.unshift(...instrumentationBabelHeader); } }; diff --git a/packages/instrumenter/test/integration/transformers.it.spec.ts.snap b/packages/instrumenter/test/integration/transformers.it.spec.ts.snap index ccf57806a4..cccd490ee8 100644 --- a/packages/instrumenter/test/integration/transformers.it.spec.ts.snap +++ b/packages/instrumenter/test/integration/transformers.it.spec.ts.snap @@ -1702,7 +1702,7 @@ Object { "type": "Identifier", }, "innerComments": undefined, - "leadingComments": undefined, + "leadingComments": Array [], "loc": SourceLocation { "end": Position { "column": 1, @@ -6838,7 +6838,7 @@ Object { "type": "Identifier", }, "innerComments": undefined, - "leadingComments": undefined, + "leadingComments": Array [], "loc": SourceLocation { "end": Position { "column": 1, @@ -12024,7 +12024,7 @@ Object { "type": "Identifier", }, "innerComments": undefined, - "leadingComments": undefined, + "leadingComments": Array [], "loc": SourceLocation { "end": Position { "column": 1, diff --git a/packages/instrumenter/test/unit/transformers/babel-transformer.spec.ts b/packages/instrumenter/test/unit/transformers/babel-transformer.spec.ts index 72593b98ac..85b5694c07 100644 --- a/packages/instrumenter/test/unit/transformers/babel-transformer.spec.ts +++ b/packages/instrumenter/test/unit/transformers/babel-transformer.spec.ts @@ -80,10 +80,26 @@ describe('babel-transformer', () => { expect(mutantCollectorMock.markMutantsAsPlaced).calledWith([mutant]); }); - it('should add the global stuff on top', () => { - const ast = createJSAst({ rawContent: 'foo' }); + it('should add the global stuff on top but after comments that are followed by newline', () => { + const ast = createJSAst({ rawContent: '// @flow\n// another comment\n\nconst foo="cat"' }); mutantCollectorMock.hasPlacedMutants.returns(true); transformBabel(ast, mutantCollectorMock, context); + + expect(ast.root.program.body[0].leadingComments![0].value).eq(' @flow'); + expect(ast.root.program.body[0].leadingComments![1].value).eq(' another comment'); + + for (let i = 0; i < instrumentationBabelHeader.length; i++) { + expect(ast.root.program.body[i]).eq(instrumentationBabelHeader[i]); + } + }); + it('should add the global stuff on top but after comments that are followed by a statement', () => { + const ast = createJSAst({ rawContent: '// @flow\n// another comment\nconst foo="cat"' }); + mutantCollectorMock.hasPlacedMutants.returns(true); + transformBabel(ast, mutantCollectorMock, context); + + expect(ast.root.program.body[0].leadingComments![0].value).eq(' @flow'); + expect(ast.root.program.body[0].leadingComments![1].value).eq(' another comment'); + for (let i = 0; i < instrumentationBabelHeader.length; i++) { expect(ast.root.program.body[i]).eq(instrumentationBabelHeader[i]); }