From 69dc06f100631882a59f989d8451169609cd3499 Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 11:58:14 -0500 Subject: [PATCH 1/7] feat: support multiple servers from which to gather code coverage --- README.md | 12 +++++++++ support.js | 71 +++++++++++++++++++++++++++++------------------------- 2 files changed, 50 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 8aed8d04..18d9643b 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 any 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..1e172ce8 100644 --- a/support.js +++ b/support.js @@ -66,13 +66,8 @@ const registerHooks = () => { windowCoverageObjects = [] const saveCoverageObject = (win) => { - // if the application code has been instrumented, then the app iframe "win.__coverage__" will be available, - // in addition, accessing win.__coverage__ can throw when testing cross-origin code, - // because we don't control the cross-origin code, we can safely return - let applicationSourceCoverage - try { - applicationSourceCoverage = win?.__coverage__ - } catch {} + // if application code has been instrumented, the app iframe "window" has an object + const applicationSourceCoverage = win.__coverage__ if (!applicationSourceCoverage) { return } @@ -148,39 +143,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) + } } }) From fafd03b54f0ab4dae2bdc65011661d448b2a3fd8 Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 12:00:23 -0500 Subject: [PATCH 2/7] Update support.js --- support.js | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/support.js b/support.js index 1e172ce8..384adafc 100644 --- a/support.js +++ b/support.js @@ -66,8 +66,13 @@ const registerHooks = () => { windowCoverageObjects = [] const saveCoverageObject = (win) => { - // if application code has been instrumented, the app iframe "window" has an object - const applicationSourceCoverage = win.__coverage__ + // if the application code has been instrumented, then the app iframe "win.__coverage__" will be available, + // in addition, accessing win.__coverage__ can throw when testing cross-origin code, + // because we don't control the cross-origin code, we can safely return + let applicationSourceCoverage + try { + applicationSourceCoverage = win?.__coverage__ + } catch {} if (!applicationSourceCoverage) { return } From 6542bcb483891830c30c1244ab05ca3e325676d5 Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 12:10:08 -0500 Subject: [PATCH 3/7] Update README.md Co-authored-by: Jennifer Shehane --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 18d9643b..0129a30c 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ if (global.__coverage__) { } ``` -Or if you have multiple servers from which you are wanting to gather code coverage, you can pass any array to `url as well: +Or if you have multiple servers from which you are wanting to gather code coverage, you can pass any array to `url` as well: ```json { From de64a01aae983482626feab2ae694557a4bb2b19 Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 12:33:07 -0500 Subject: [PATCH 4/7] Update README.md Co-authored-by: Matt Schile --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0129a30c..75f5ede6 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ if (global.__coverage__) { } ``` -Or if you have multiple servers from which you are wanting to gather code coverage, you can pass any array to `url` as well: +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 { From ab61cf60cd67fd27c8a234fa770bd11fcb166c7b Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 13:01:55 -0500 Subject: [PATCH 5/7] multiple backends --- .circleci/config.yml | 1 + test-apps/multiple-backends/.babelrc | 3 +++ test-apps/multiple-backends/README.md | 3 +++ test-apps/multiple-backends/cypress.config.js | 17 +++++++++++++++ .../multiple-backends/cypress/e2e/spec.cy.js | 6 ++++++ .../cypress/plugins/index.js | 4 ++++ .../multiple-backends/cypress/support/e2e.js | 1 + test-apps/multiple-backends/package.json | 13 ++++++++++++ test-apps/multiple-backends/server/index.html | 3 +++ .../multiple-backends/server/server-3003.js | 21 +++++++++++++++++++ .../multiple-backends/server/server-3004.js | 21 +++++++++++++++++++ 11 files changed, 93 insertions(+) create mode 100644 test-apps/multiple-backends/.babelrc create mode 100644 test-apps/multiple-backends/README.md create mode 100644 test-apps/multiple-backends/cypress.config.js create mode 100644 test-apps/multiple-backends/cypress/e2e/spec.cy.js create mode 100644 test-apps/multiple-backends/cypress/plugins/index.js create mode 100644 test-apps/multiple-backends/cypress/support/e2e.js create mode 100644 test-apps/multiple-backends/package.json create mode 100644 test-apps/multiple-backends/server/index.html create mode 100644 test-apps/multiple-backends/server/server-3003.js create mode 100644 test-apps/multiple-backends/server/server-3004.js diff --git a/.circleci/config.yml b/.circleci/config.yml index e34ea056..3b6a40a0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -152,6 +152,7 @@ workflows: - exclude-files - frontend - fullstack + - multiple-backend - one-spec - same-folder - support-files 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..0953706b --- /dev/null +++ b/test-apps/multiple-backends/server/index.html @@ -0,0 +1,3 @@ + + test backend + 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}!`)) From 1315524b9077c053a579f26cc9360e1c798587ad Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 13:02:42 -0500 Subject: [PATCH 6/7] multiple backends --- test-apps/multiple-backends/server/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test-apps/multiple-backends/server/index.html b/test-apps/multiple-backends/server/index.html index 0953706b..6491726a 100644 --- a/test-apps/multiple-backends/server/index.html +++ b/test-apps/multiple-backends/server/index.html @@ -1,3 +1,3 @@ - test backend + test multiple backends From 0062d73c3a48475de024b141c169faa52f246d01 Mon Sep 17 00:00:00 2001 From: Ryan Manuel Date: Mon, 16 Sep 2024 13:09:31 -0500 Subject: [PATCH 7/7] fix build --- .circleci/config.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 3b6a40a0..f02bf8e4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -152,7 +152,7 @@ workflows: - exclude-files - frontend - fullstack - - multiple-backend + - multiple-backends - one-spec - same-folder - support-files @@ -180,6 +180,7 @@ workflows: - test-exclude-files - test-frontend - test-fullstack + - test-multiple-backends - test-one-spec - test-same-folder - test-support-files