I have been trying to get Cypress code coverage working with my Angular production project to no avail.
To try and help diagnose it, I have created a minimal implementation project to make sure I wasn't introducing anything weird in the production version, which I don't think I am as the same issue is still happening. It's starting to drive me mad!
I have used a few references and as far as I can see I have the things in place I need to:
As far as I can tell the Angular and Cypress side is all hooked up and am getting output in the .nyc_output folder and a coverage report. However the report is not indicating typescript line coverage or including those stats.
I have seen this but didn't seem to help.
Code Instrumentation (webpack extension + angular.json):
module.exports = {
module: {
rules: [
{
test: /\.(js|ts)$/,
loader: "istanbul-instrumenter-loader",
options: { esModules: true },
enforce: "post",
include: require("path").join(__dirname, "..", "src"),
exclude: [
/\.(e2e|spec)\.ts$/,
/node_modules/,
/(ngfactory|ngstyle)\.js/,
],
},
],
},
};
"serve": {
"builder": "ngx-build-plus:dev-server",
"options": {
"browserTarget": "architecture-testing:build",
"extraWebpackConfig": "./cypress/coverage.webpack.js",
"sourceMap": true
},
"configurations": {
"production": {
"browserTarget": "architecture-testing:build:production"
}
}
}
Cypress appears to be recording and saving coverage:
const registerCodeCoverageTasks = require("@cypress/code-coverage/task");
module.exports = (on, config) => {
registerCodeCoverageTasks(on, config);
return config;
};
out.json appears to have the correct file and code mapping:
package.json (nyc config + deps):
{
"name": "architecture-testing",
"version": "0.0.0",
"scripts": {
"postinstall": "ngcc",
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"test": "ng test",
"lint": "ng lint",
"e2e": "ng e2e",
"precypress": "rimraf .nyc_output coverage",
"cypress": "ng run architecture-testing:cypress-run",
"cypress:open": "cypress open",
"cypress:run": "cypress run"
},
"nyc": {
"extends": "@istanbuljs/nyc-config-typescript",
"all": true,
"exclude": [
"coverage/**",
"cypress/**",
"**/*.spec.ts"
]
},
"private": true,
"dependencies": {
"@angular/animations": "~9.1.9",
"@angular/common": "~9.1.9",
"@angular/compiler": "~9.1.9",
"@angular/core": "~9.1.9",
"@angular/forms": "~9.1.9",
"@angular/platform-browser": "~9.1.9",
"@angular/platform-browser-dynamic": "~9.1.9",
"@angular/router": "~9.1.9",
"rxjs": "~6.5.4",
"tslib": "^1.10.0",
"zone.js": "~0.10.2"
},
"devDependencies": {
"@angular-devkit/build-angular": "~0.901.7",
"@angular/cli": "~9.1.7",
"@angular/compiler-cli": "~9.1.9",
"@briebug/cypress-schematic": "^3.3.0",
"@cypress/code-coverage": "^3.8.1",
"@cypress/webpack-preprocessor": "5.4.1",
"@istanbuljs/nyc-config-typescript": "^1.0.1",
"@types/node": "^12.11.1",
"codelyzer": "^5.1.2",
"cypress": "^4.8.0",
"istanbul-instrumenter-loader": "^3.0.1",
"istanbul-lib-coverage": "^3.0.0",
"ngx-build-plus": "^9.0.6",
"nyc": "^15.1.0",
"rimraf": "^3.0.2",
"source-map-support": "^0.5.19",
"ts-loader": "7.0.5",
"ts-node": "^8.3.0",
"tslint": "~6.1.0",
"typescript": "~3.8.3"
}
}
Spec file:
it('does something', () => {
cy.visit('http://localhost:4200');
cy.get('[data-cy=button-one]').click();
cy.get('[data-cy=button-output]').should('have.text', 'you clicked button 1');
});
Sorry it's so long but I am stuck as to where to go next. Many thanks if you can point me in any direction.
Update based on answer investigation:
Looking at the past versions of the @cypress/code-coverage it appears the issue for me was introduced in v3.3.0 of the plugin. All the versions for v3.2.* were working for me when downgrading my minimal project. After looking at the documentation changes for v3.3.0 the key bit of information in the readme was:
**Note:** if you have `all: true` NYC option set, this plugin will check the produced `.nyc_output/out.json` before generating the final report. If the `out.json` file does not have information for some files that should be there according to `include` list, then an empty placeholder will be included, see [PR 208](https://github.com/cypress-io/code-coverage/pull/208).
My original nyc config was:
"nyc": {
"extends": "@istanbuljs/nyc-config-typescript",
"all": true,
"exclude": [
"coverage/**",
"cypress/**",
"**/*.spec.ts"
]
}
So for some reason even though I have metrics for the files that were being tested in out.json a second "empty placeholder" node was being created and overriding the subsequent report generation. I am guessing that this is potentially a bug or a problem with my setup again so will ask the creators.
I can now see coverage if I change my nyc config to:
"nyc": {
"extends": "@istanbuljs/nyc-config-typescript",
"all": true,
"include": [
"src/**/*.ts"
],
"exclude": [
"coverage/**",
"cypress/**",
"**/*.spec.ts"
]
}
This does mean that if I don't hit a file with testing it won't be included as an empty placeholder as "all": true is no longer present.
Looking at @briebug/[email protected] this doesn't appear to be causing any issues (same happens without using their builder) but has been raised as one here and here.
main.ts
below, the 1st node with metrics has escaped back-slashes in the path, whereas the second has forward-slashes, so they are technically different keys. This explains why the extra nodes appear by the feature added in v3.3.0 [PR 208], but I guess only on Windows since Linux/Mac has forward slashes in the path from the start. – Sexless"cy:open": "set DEBUG=code-coverage & cypress open"
– Sexless