-
Notifications
You must be signed in to change notification settings - Fork 240
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
Error: No tests were executed when using aliased signal inputs in an Angular 17 application #4771
Comments
After some debugging I've confirmed the error is due a mutation introduced by Stryker in the source code, before the ivy compiler parses and bind the template with the controller. When ran through Stryker the original component is modified to introduce the mutations as expected, this includes the object with the input options which contains the alias. Because of the mutations introduced there, the compiler seems to be unable to parse it and fails leading the tests to fail as well. Original componentimport { Component, input } from '@angular/core';
@Component({
selector: 'app-new-input',
standalone: true,
template: ``,
styles: ``
})
export class NewInputComponent {
aliasedInput = input<string | undefined>(undefined, { alias: 'aliased-input' });
splitInput(): string[] {
return (this.aliasedInput() ?? '').trim().split(/\s+/);
}
} Modified component// @ts-nocheck
function stryNS_9fa48() {
var g = typeof globalThis === 'object' && globalThis && globalThis.Math === Math && globalThis || new Function("return this")();
var ns = g.__stryker__ || (g.__stryker__ = {});
if (ns.activeMutant === undefined && g.process && g.process.env && g.process.env.__STRYKER_ACTIVE_MUTANT__) {
ns.activeMutant = g.process.env.__STRYKER_ACTIVE_MUTANT__;
}
function retrieveNS() {
return ns;
}
stryNS_9fa48 = retrieveNS;
return retrieveNS();
}
stryNS_9fa48();
function stryCov_9fa48() {
var ns = stryNS_9fa48();
var cov = ns.mutantCoverage || (ns.mutantCoverage = {
static: {},
perTest: {}
});
function cover() {
var c = cov.static;
if (ns.currentTestId) {
c = cov.perTest[ns.currentTestId] = cov.perTest[ns.currentTestId] || {};
}
var a = arguments;
for (var i = 0; i < a.length; i++) {
c[a[i]] = (c[a[i]] || 0) + 1;
}
}
stryCov_9fa48 = cover;
cover.apply(null, arguments);
}
function stryMutAct_9fa48(id) {
var ns = stryNS_9fa48();
function isActive(id) {
if (ns.activeMutant === id) {
if (ns.hitCount !== void 0 && ++ns.hitCount > ns.hitLimit) {
throw new Error('Stryker: Hit count limit reached (' + ns.hitCount + ')');
}
return true;
}
return false;
}
stryMutAct_9fa48 = isActive;
return isActive(id);
}
import { Component, input } from '@angular/core';
@Component({
selector: 'app-new-input',
standalone: true,
template: ``,
styles: ``
})
export class NewInputComponent {
aliasedInput = input<string | undefined>(undefined, stryMutAct_9fa48("4") ? {} : (stryCov_9fa48("4"), {
alias: stryMutAct_9fa48("5") ? "" : (stryCov_9fa48("5"), 'aliased-input')
}));
splitInput(): string[] {
if (stryMutAct_9fa48("6")) {
{}
} else {
stryCov_9fa48("6");
return stryMutAct_9fa48("7") ? (this.aliasedInput() ?? '').split(/\s+/) : (stryCov_9fa48("7"), (stryMutAct_9fa48("8") ? this.aliasedInput() && '' : (stryCov_9fa48("8"), this.aliasedInput() ?? (stryMutAct_9fa48("9") ? "Stryker was here!" : (stryCov_9fa48("9"), '')))).trim().split(stryMutAct_9fa48("11") ? /\S+/ : stryMutAct_9fa48("10") ? /\s/ : (stryCov_9fa48("10", "11"), /\s+/)));
}
}
} I believe this wasn't a problem using the annotations because they are excluded from being mutated here. This means aliased inputs and outputs using annotations are safe from being modified, but their recently introduced signal counterparts, input and model, are not since they use an object parameter inside the function call. Now, I'm not sure where to add this exclusion since it is only necessary to support Angular, my best guess is the karma-runner package, although there is recent support for jest too. In the meantime I've made a custom ignore plugin to avoid introducing mutations in objects inside functions named input or model so Stryker can be used with these, it can be seen in action here. import { declareValuePlugin, PluginKind } from '@stryker-mutator/api/plugin';
export const strykerPlugins = [declareValuePlugin(PluginKind.Ignore, 'angular.signal-model-input-options', {
shouldIgnore(path) {
const inputOrModelExpression = path.findParent((path) =>
path.isCallExpression() &&
path.node.callee.type === 'Identifier' &&
(path.node.callee.name === 'input' || path.node.callee.name === 'model'),
);
if (path.isObjectExpression() && inputOrModelExpression != null) {
return 'Angular signal or model input options cannot be mutated as that causes issues with the ivy compiler.';
}
},
})]; |
Wow, great find and workaround, @kal-rein. I'm very glad to see you were able to use the new 'ignore-plugin' type. These kinds of use cases were exactly why I added it. StrykerJS should ideally come with an |
I can give it a try in the upcoming days and implement this workaround as a plugin when the option Also, I'm not experienced with AST and there is a problem that I don't know how to address in my workaround. If the user were to define another function named |
I see that this ignorer plugin would be shipped with {
"ignorers": ["angular"]
} So configure it for jest would be the same. Btw, don't focus on jest too much; as of today, jest support is still experimental, see https://stryker-mutator.io/docs/stryker-js/guides/angular/#angular-with-experimental-jest-support.
This plugin would only be active for Angular projects, so I think See https://astexplorer.net/#/gist/76889bb1091d862b334873658a938f4f/5802b6615a5b6b01e908f32553afc15e1ad70708 for an example. |
Summary
While testing an Angular component or directive, if an input is aliased and uses the new signal inputs introduced in Angular 16 Stryker fails to run any test, even those outside the affected element. But, if the same element is tested using an annotated input instead of a signal input Stryker runs all tests correctly, including the affected element.
This is a strange behavior because both inputs works correctly while testing directly using
ng test
.I've prepared a repository where the problem can be reproduced here. The latest commit includes both inputs and fails to run Stryker while this commit includes only the annotated input and runs correctly.
Stryker config
Test runner config
Stryker environment
Test runner environment
ng test
Your Environment
Add stryker.log
stryker.log
The text was updated successfully, but these errors were encountered: