Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(instrumenter): support typescript constructors with code before super() #4757

Merged
merged 1 commit into from
Feb 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 15 additions & 10 deletions packages/instrumenter/src/mutators/block-statement-mutator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,11 @@ function isEmpty(path: NodePath<babel.types.BlockStatement>) {
* @see https://github.com/stryker-mutator/stryker-js/issues/2474
*/
function isInvalidConstructorBody(blockStatement: NodePath<babel.types.BlockStatement>): boolean {
return !!(
return Boolean(
blockStatement.parentPath.isClassMethod() &&
blockStatement.parentPath.node.kind === 'constructor' &&
(containsTSParameterProperties(blockStatement.parentPath) || containsInitializedClassProperties(blockStatement.parentPath)) &&
hasSuperExpressionOnFirstLine(blockStatement)
blockStatement.parentPath.node.kind === 'constructor' &&
(containsTSParameterProperties(blockStatement.parentPath) || containsInitializedClassProperties(blockStatement.parentPath)) &&
hasSuperExpression(blockStatement),
);
}

Expand All @@ -62,10 +62,15 @@ function containsInitializedClassProperties(constructor: NodePath<babel.types.Cl
);
}

function hasSuperExpressionOnFirstLine(constructor: NodePath<babel.types.BlockStatement>): boolean {
return (
types.isExpressionStatement(constructor.node.body[0]) &&
types.isCallExpression(constructor.node.body[0].expression) &&
types.isSuper(constructor.node.body[0].expression.callee)
);
function hasSuperExpression(constructor: NodePath<babel.types.BlockStatement>): boolean {
let hasSuper = false;
constructor.traverse({
Super(path) {
if (path.parentPath.isCallExpression()) {
path.stop();
hasSuper = true;
}
},
});
return hasSuper;
}
5 changes: 1 addition & 4 deletions packages/instrumenter/src/transformers/babel-transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,10 +144,7 @@ export const transformBabel: AstTransformer<ScriptFormat> = (
*/
function collectMutants(path: NodePath) {
return [...mutate(path)]
.map((mutable) => {
const mutant = mutantCollector.collect(originFileName, path.node, mutable, offset);
return mutant;
})
.map((mutable) => mutantCollector.collect(originFileName, path.node, mutable, offset))
.filter((mutant) => !mutant.ignoreReason);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe(sut.name, () => {
expectJSMutation(sut, 'class Foo { constructor() { bar(); } }', 'class Foo { constructor() {} }');
});

it('should mutate a constructor with (typescript) parameter properties', () => {
it('should mutate a constructor with (typescript) parameter properties without a `super()` call', () => {
expectJSMutation(sut, 'class Foo { constructor(private baz: string) { bar(); } }', 'class Foo { constructor(private baz: string) {} }');
});

Expand All @@ -59,16 +59,26 @@ describe(sut.name, () => {

/**
* @see https://github.com/stryker-mutator/stryker-js/issues/2314
* @see https://github.com/stryker-mutator/stryker-js/issues/4744
*/
it('should not mutate a constructor containing a super call and has (typescript) parameter properties', () => {
expectJSMutation(sut, 'class Foo extends Bar { constructor(private baz: string) { super(); } }');
expectJSMutation(
sut,
'class Foo extends Bar { constructor(private baz: string) { const errorBody: Body = { message: `msg: ${baz}` }; super(errorBody); } }',
);
});

/**
* @see https://github.com/stryker-mutator/stryker-js/issues/2474
* @see https://github.com/stryker-mutator/stryker-js/issues/4744
*/
it('should not mutate a constructor containing a super call and contains initialized properties', () => {
expectJSMutation(sut, 'class Foo extends Bar { private baz = "qux"; constructor() { super(); } }');
expectJSMutation(
sut,
'class Foo extends Bar { private baz = "qux"; constructor() { const errorBody: Body = { message: `msg: ${baz}` }; super(errorBody); } }',
);
});
});
});
12 changes: 12 additions & 0 deletions packages/instrumenter/testResources/instrumenter/super-call.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,15 @@ export class InjectionError extends TypedInjectError {
super(`Could not ${describeInjectAction(path[0])} ${path.map(name).join(' -> ')}. Cause: ${cause.message}`);
}
}

// See https://github.com/stryker-mutator/stryker-js/issues/4744
export class UniqueKeyFailedError<T> extends UnprocessableEntityException {
constructor(public readonly fields: ReadonlyArray<keyof T & string>) {
const errorBody: UnprocessableEntityBody<T> = {
status: 'uniqueness_failed',
fields,
};
super(errorBody);
console.log(`${this.message} created`);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,17 @@ export class InjectionError extends TypedInjectError {
constructor(public readonly path: InjectionTarget[], public readonly cause: Error) {
super(stryMutAct_9fa48(\\"0\\") ? \`\` : (stryCov_9fa48(\\"0\\"), \`Could not \${describeInjectAction(path[0])} \${path.map(name).join(stryMutAct_9fa48(\\"1\\") ? \\"\\" : (stryCov_9fa48(\\"1\\"), ' -> '))}. Cause: \${cause.message}\`));
}
}

// See https://github.com/stryker-mutator/stryker-js/issues/4744
export class UniqueKeyFailedError<T> extends UnprocessableEntityException {
constructor(public readonly fields: ReadonlyArray<keyof T & string>) {
const errorBody: UnprocessableEntityBody<T> = stryMutAct_9fa48(\\"2\\") ? {} : (stryCov_9fa48(\\"2\\"), {
status: stryMutAct_9fa48(\\"3\\") ? \\"\\" : (stryCov_9fa48(\\"3\\"), 'uniqueness_failed'),
fields
});
super(errorBody);
console.log(stryMutAct_9fa48(\\"4\\") ? \`\` : (stryCov_9fa48(\\"4\\"), \`\${this.message} created\`));
}
}"
`;