Skip to content

Commit

Permalink
fix(babel-transformer): respect top of the file comments/pragma
Browse files Browse the repository at this point in the history
This means that the stryker mutator hooks are no longer just
prepended to the beginning of file. Instead we also add the comments
which means things like flow should behave correctly
  • Loading branch information
swist committed Mar 7, 2021
1 parent 424aca4 commit c0bfb90
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 5 deletions.
53 changes: 53 additions & 0 deletions e2e/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions e2e/package.json
Expand Up @@ -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",
Expand Down
16 changes: 16 additions & 0 deletions 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"
}
}
]
]
}
5 changes: 5 additions & 0 deletions e2e/test/flow-test-project/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions 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"
}
}
6 changes: 6 additions & 0 deletions e2e/test/flow-test-project/src/square.js
@@ -0,0 +1,6 @@
// @flow
export function square(n: number): number {
const result: Array<?number> = [];
const cat = new Set<string>();
return n * n;
}
8 changes: 8 additions & 0 deletions 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);
});
});
13 changes: 13 additions & 0 deletions 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"
}
16 changes: 16 additions & 0 deletions 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"
}
}
]
]
}
19 changes: 19 additions & 0 deletions 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 |
-----------|---------|----------|-----------|------------|----------|---------|*/
});
});
5 changes: 5 additions & 0 deletions packages/instrumenter/src/transformers/babel-transformer.ts
Expand Up @@ -36,6 +36,11 @@ export const transformBabel: AstTransformer<AstFormat.JS | AstFormat.TS> = ({ 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);
}
};
Expand Up @@ -1702,7 +1702,7 @@ Object {
"type": "Identifier",
},
"innerComments": undefined,
"leadingComments": undefined,
"leadingComments": Array [],
"loc": SourceLocation {
"end": Position {
"column": 1,
Expand Down Expand Up @@ -6838,7 +6838,7 @@ Object {
"type": "Identifier",
},
"innerComments": undefined,
"leadingComments": undefined,
"leadingComments": Array [],
"loc": SourceLocation {
"end": Position {
"column": 1,
Expand Down Expand Up @@ -12024,7 +12024,7 @@ Object {
"type": "Identifier",
},
"innerComments": undefined,
"leadingComments": undefined,
"leadingComments": Array [],
"loc": SourceLocation {
"end": Position {
"column": 1,
Expand Down
Expand Up @@ -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]);
}
Expand Down

0 comments on commit c0bfb90

Please sign in to comment.