diff --git a/.circleci/config.yml b/.circleci/config.yml index e34ea056..f02bf8e4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -152,6 +152,7 @@ workflows: - exclude-files - frontend - fullstack + - multiple-backends - one-spec - same-folder - support-files @@ -179,6 +180,7 @@ workflows: - test-exclude-files - test-frontend - test-fullstack + - test-multiple-backends - test-one-spec - test-same-folder - test-support-files diff --git a/README.md b/README.md index 8aed8d04..75f5ede6 100644 --- a/README.md +++ b/README.md @@ -214,6 +214,18 @@ if (global.__coverage__) { } ``` +Or if you have multiple servers from which you are wanting to gather code coverage, you can pass an array to `url` as well: + +```json +{ + "env": { + "codeCoverage": { + "url": ["http://localhost:3000/__coverage__", "http://localhost:3001/__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. ### expectBackendCoverageOnly diff --git a/support.js b/support.js index 81697ca3..384adafc 100644 --- a/support.js +++ b/support.js @@ -148,39 +148,49 @@ const registerHooks = () => { // we can only request server-side code coverage // if we are running end-to-end tests, // otherwise where do we send the request? - const url = Cypress._.get( + const captureUrls = Cypress._.get( Cypress.env('codeCoverage'), 'url', '/__coverage__' ) - cy.request({ - url, - log: false, - failOnStatusCode: false - }) - .then((r) => { - return Cypress._.get(r, 'body.coverage', null) + function captureCoverage(url, suffix = '') { + cy.request({ + url, + log: false, + failOnStatusCode: false }) - .then((coverage) => { - if (!coverage) { - // we did not get code coverage - this is the - // original failed request - const expectBackendCoverageOnly = Cypress._.get( - Cypress.env('codeCoverage'), - 'expectBackendCoverageOnly', - false - ) - if (expectBackendCoverageOnly) { - throw new Error( - `Expected to collect backend code coverage from ${url}` + .then((r) => { + return Cypress._.get(r, 'body.coverage', null) + }) + .then((coverage) => { + if (!coverage) { + // we did not get code coverage - this is the + // original failed request + const expectBackendCoverageOnly = Cypress._.get( + Cypress.env('codeCoverage'), + 'expectBackendCoverageOnly', + false ) - } else { - // we did not really expect to collect the backend code coverage - return + if (expectBackendCoverageOnly) { + throw new Error( + `Expected to collect backend code coverage from ${url}` + ) + } else { + // we did not really expect to collect the backend code coverage + return + } } - } - sendCoverage(coverage, 'backend') - }) + sendCoverage(coverage, `backend${suffix}`) + }) + } + + if (Array.isArray(captureUrls)) { + for (const [index, url] of captureUrls.entries()) { + captureCoverage(url, `_${index}`) + } + } else { + captureCoverage(captureUrls) + } } }) diff --git a/test-apps/multiple-backends/.babelrc b/test-apps/multiple-backends/.babelrc new file mode 100644 index 00000000..7a016cf8 --- /dev/null +++ b/test-apps/multiple-backends/.babelrc @@ -0,0 +1,3 @@ +{ + "plugins": ["istanbul"] +} diff --git a/test-apps/multiple-backends/README.md b/test-apps/multiple-backends/README.md new file mode 100644 index 00000000..001cfdc7 --- /dev/null +++ b/test-apps/multiple-backends/README.md @@ -0,0 +1,3 @@ +# example: multiple backends + +> Getting code coverage from multiple backends diff --git a/test-apps/multiple-backends/cypress.config.js b/test-apps/multiple-backends/cypress.config.js new file mode 100644 index 00000000..76207cb9 --- /dev/null +++ b/test-apps/multiple-backends/cypress.config.js @@ -0,0 +1,17 @@ +const { defineConfig } = require('cypress') + +module.exports = defineConfig({ + fixturesFolder: false, + env: { + codeCoverage: { + url: ['http://localhost:3003/__coverage__', 'http://localhost:3004/__coverage__'], + expectBackendCoverageOnly: true, + }, + }, + e2e: { + setupNodeEvents(on, config) { + return require('./cypress/plugins/index.js')(on, config) + }, + baseUrl: 'http://localhost:3003', + }, +}) diff --git a/test-apps/multiple-backends/cypress/e2e/spec.cy.js b/test-apps/multiple-backends/cypress/e2e/spec.cy.js new file mode 100644 index 00000000..f21eb970 --- /dev/null +++ b/test-apps/multiple-backends/cypress/e2e/spec.cy.js @@ -0,0 +1,6 @@ +/// +it('has multiple backends with code coverage', () => { + cy.visit('/') + cy.request('/hello') + cy.request('http://localhost:3004/world') +}) diff --git a/test-apps/multiple-backends/cypress/plugins/index.js b/test-apps/multiple-backends/cypress/plugins/index.js new file mode 100644 index 00000000..101311b6 --- /dev/null +++ b/test-apps/multiple-backends/cypress/plugins/index.js @@ -0,0 +1,4 @@ +module.exports = (on, config) => { + require('@cypress/code-coverage/task')(on, config) + return config +} diff --git a/test-apps/multiple-backends/cypress/support/e2e.js b/test-apps/multiple-backends/cypress/support/e2e.js new file mode 100644 index 00000000..cc6040de --- /dev/null +++ b/test-apps/multiple-backends/cypress/support/e2e.js @@ -0,0 +1 @@ +import '@cypress/code-coverage/support' diff --git a/test-apps/multiple-backends/package.json b/test-apps/multiple-backends/package.json new file mode 100644 index 00000000..fbfae352 --- /dev/null +++ b/test-apps/multiple-backends/package.json @@ -0,0 +1,13 @@ +{ + "name": "example-multiple-backends", + "description": "Code coverage for multiple backends", + "private": true, + "scripts": { + "cy:run": "cypress run", + "start": "nyc --silent node server/server-3003 & nyc --silent node server/server-3004", + "pretest": "rimraf .nyc_output .cache coverage dist", + "test": "start-test \"3003|3004\" cy:run", + "coverage:verify": "npx nyc report --check-coverage true --lines 100", + "coverage:check-files": "check-coverage server-3003.js server-3004.js && only-covered server-3003.js server-3004.js" + } +} diff --git a/test-apps/multiple-backends/server/index.html b/test-apps/multiple-backends/server/index.html new file mode 100644 index 00000000..6491726a --- /dev/null +++ b/test-apps/multiple-backends/server/index.html @@ -0,0 +1,3 @@ + + test multiple backends + diff --git a/test-apps/multiple-backends/server/server-3003.js b/test-apps/multiple-backends/server/server-3003.js new file mode 100644 index 00000000..df0faed0 --- /dev/null +++ b/test-apps/multiple-backends/server/server-3003.js @@ -0,0 +1,21 @@ +const express = require('express') +const app = express() +const port = 3003 + +// if there is code coverage information +// then expose an endpoint that returns it +/* istanbul ignore next */ +if (global.__coverage__) { + console.log('have code coverage, will add middleware for express') + console.log(`to fetch: GET :${port}/__coverage__`) + require('@cypress/code-coverage/middleware/express')(app) +} + +app.use(express.static(__dirname)) + +app.get('/hello', (req, res) => { + console.log('sending hello') + res.send('Hello') +}) + +app.listen(port, () => console.log(`Example app listening on port ${port}!`)) diff --git a/test-apps/multiple-backends/server/server-3004.js b/test-apps/multiple-backends/server/server-3004.js new file mode 100644 index 00000000..414d72e7 --- /dev/null +++ b/test-apps/multiple-backends/server/server-3004.js @@ -0,0 +1,21 @@ +const express = require('express') +const app = express() +const port = 3004 + +// if there is code coverage information +// then expose an endpoint that returns it +/* istanbul ignore next */ +if (global.__coverage__) { + console.log('have code coverage, will add middleware for express') + console.log(`to fetch: GET :${port}/__coverage__`) + require('@cypress/code-coverage/middleware/express')(app) +} + +app.use(express.static(__dirname)) + +app.get('/world', (req, res) => { + console.log('sending world') + res.send('World!') +}) + +app.listen(port, () => console.log(`Example app listening on port ${port}!`))