From 9dbdd83ad2926f6dfeb67fba0331fb9cda9973ad Mon Sep 17 00:00:00 2001 From: Gleb Bahmutov Date: Tue, 25 Jun 2019 10:17:43 -0400 Subject: [PATCH 1/3] chore: add middleware and merge server-side coverage --- .vscode/settings.json | 3 +++ middleware.js | 9 +++++++++ support.js | 18 ++++++++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 middleware.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..4e9a7cbc --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "workbench.colorTheme": "Tomorrow Night Blue" +} \ No newline at end of file diff --git a/middleware.js b/middleware.js new file mode 100644 index 00000000..560a529b --- /dev/null +++ b/middleware.js @@ -0,0 +1,9 @@ +module.exports = app => { + // expose "GET __coverage__" endpoint that just returns + // global coverage information (if the application has been instrumented) + app.get('/__coverage__', (req, res) => { + res.json({ + coverage: global.__coverage__ || null + }) + }) +} diff --git a/support.js b/support.js index 2acaa6d3..cdb7e179 100644 --- a/support.js +++ b/support.js @@ -21,6 +21,24 @@ afterEach(() => { }) after(() => { + // there might be server-side code coverage information + // we should grab it once after all tests finish + cy.request({ + url: '/__coverage__', + log: false, + failOnStatusCode: false + }) + .then(r => Cypress._.get(r, 'body.coverage', null), { log: false }) + .then(coverage => { + if (!coverage) { + // we did not get code coverage - this is the + // original failed request + return + } + cy.task('combineCoverage', coverage) + }) + + // collect and merge frontend coverage const specFolder = Cypress.config('integrationFolder') const supportFolder = Cypress.config('supportFolder') From 352dfe87f70f3e7314fb27c89012ed40908cf874 Mon Sep 17 00:00:00 2001 From: Gleb Bahmutov Date: Tue, 25 Jun 2019 11:51:26 -0400 Subject: [PATCH 2/3] chore: configure coverage url --- README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ support.js | 3 ++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index a5228bca..c0c78b74 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,58 @@ module.exports = (on, config) => { Now the code coverage from spec files will be combined with end-to-end coverage. +## Instrument backend code + +You can also instrument your server-side code and produce combined coverage report that covers both the backend and frontend code. + +1. Run the server code with instrumentation. The simplest way is to use [nyc](https://github.com/istanbuljs/nyc). If normally you run `node src/server` then to run instrumented version you can do `nyc --silent node src/server`. +2. Add an endpoint that returns collected coverage. If you are using Express, you can simply do + +```js +const express = require('express') +const app = express() +require('@cypress/code-coverage/middleware')(app) +``` + +**Tip:** you can register the endpoint only if there is global code coverage object, and you can exclude the middleware code from the coverage numbers + +```js +// https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md +/* istanbul ignore next */ +if (global.__coverage__) { + require('@cypress/code-coverage/middleware')(app) +} +``` + +If you use Hapi server, define the endpoint yourself and return the object + +```js +// https://github.com/gotwarlost/istanbul/blob/master/ignoring-code-for-coverage.md +/* istanbul ignore next */ +if (global.__coverage__) { + // https://hapijs.com/tutorials/routing?lang=en_US + server.route({ + method: 'GET', + path: '/__coverage__', + handler () { + return { coverage: global.__coverage__ } + } + }) +} +``` + +3. Save the API coverage endpoint in `cypress.json` file to let the plugin know where to call to receive the code coverage data from the server. Place it in `env.codeCoverage` object: + +```json +{ + "env": { + "codeCoverage": { + "url": "http://localhost:3000/__coverage__" + } + } +} +``` + ## Examples - [Cypress code coverage guide](http://on.cypress.io/code-coverage) diff --git a/support.js b/support.js index cdb7e179..e827ca0e 100644 --- a/support.js +++ b/support.js @@ -23,8 +23,9 @@ afterEach(() => { after(() => { // there might be server-side code coverage information // we should grab it once after all tests finish + const url = Cypress._.get(Cypress.env('codeCoverage'), 'url', '/__coverage__') cy.request({ - url: '/__coverage__', + url, log: false, failOnStatusCode: false }) From 8ec63d9a3aa6de9650a0d1874bed69557922910b Mon Sep 17 00:00:00 2001 From: Gleb Bahmutov Date: Tue, 25 Jun 2019 11:58:18 -0400 Subject: [PATCH 3/3] finish docs and add link to the realworld repo --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c0c78b74..f9607218 100644 --- a/README.md +++ b/README.md @@ -115,10 +115,13 @@ if (global.__coverage__) { } ``` +That should be enough - the code coverage from the server will be requested at the end of the test run and merged with the client-side code coverage, producing a combined report + ## Examples - [Cypress code coverage guide](http://on.cypress.io/code-coverage) - [cypress-example-todomvc-redux](https://github.com/cypress-io/cypress-example-todomvc-redux) +- Full frontend + backend code coverage in [bahmutov/realworld](https://github.com/bahmutov/realworld) repo - Read ["Code Coverage by Parcel Bundler"](https://glebbahmutov.com/blog/code-coverage-by-parcel/) blog post - Read ["Combined End-to-end and Unit Test Coverage"](https://glebbahmutov.com/blog/combined-end-to-end-and-unit-test-coverage/)