Skip to content

feat: add server-side middleware and merge the backend coverage with frontend #22

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

Merged
merged 3 commits into from
Jun 25, 2019
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"workbench.colorTheme": "Tomorrow Night Blue"
}
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,65 @@ 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__"
}
}
}
```

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/)

Expand Down
9 changes: 9 additions & 0 deletions middleware.js
Original file line number Diff line number Diff line change
@@ -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
})
})
}
19 changes: 19 additions & 0 deletions support.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,25 @@ 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,
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')

Expand Down