diff --git a/examples/multiple-backends/README.md b/examples/multiple-backends/README.md
new file mode 100644
index 00000000..b10305d7
--- /dev/null
+++ b/examples/multiple-backends/README.md
@@ -0,0 +1,22 @@
+# example: multiple-backends
+
+> Combined code coverage from multiple backend processes, and e2e and unit tests
+
+This example runs instrumented server code on two processes, that serves instrumented frontend code, and instruments the unit tests on the fly. The final report combines all 4 sources of information.
+
+To run
+
+```sh
+$ npm run dev
+```
+
+You should see messages from the plugin when it saves each coverage object
+
+
+
+In the produced report, you should see
+
+- `server1/server.js` coverage for backend
+- `server2/server.js` coverage for backend
+- `main.js` coverage from end-to-end tests
+- `string-utils.js` coverage from unit tests
diff --git a/examples/multiple-backends/cypress.json b/examples/multiple-backends/cypress.json
new file mode 100644
index 00000000..e99370d8
--- /dev/null
+++ b/examples/multiple-backends/cypress.json
@@ -0,0 +1,9 @@
+{
+ "fixturesFolder": false,
+ "baseUrl": "http://localhost:3003",
+ "env": {
+ "codeCoverage": {
+ "url": "http://localhost:3003/__coverage__,http://localhost:3004/__coverage__"
+ }
+ }
+}
diff --git a/examples/multiple-backends/cypress/integration/spec.js b/examples/multiple-backends/cypress/integration/spec.js
new file mode 100644
index 00000000..900a7ff2
--- /dev/null
+++ b/examples/multiple-backends/cypress/integration/spec.js
@@ -0,0 +1,26 @@
+///
+
+// load extra files to instrument on the fly
+const { reverse } = require('../../string-utils')
+
+it('uses frontend code and calls backend', () => {
+ cy.visit('/')
+ cy.contains('Page body').should('be.visible')
+
+ cy.window()
+ .invoke('add', 2, 3)
+ .should('equal', 5)
+
+ cy.window()
+ .invoke('sub', 2, 3)
+ .should('equal', -1)
+
+ cy.log('**backend request**')
+ cy.request('/hello')
+
+ cy.log('**other backend request**')
+ cy.request('http://localhost:3004/goodbye')
+
+ cy.log('**unit test**')
+ expect(reverse('Hello')).to.equal('olleH')
+})
diff --git a/examples/multiple-backends/cypress/plugins/index.js b/examples/multiple-backends/cypress/plugins/index.js
new file mode 100644
index 00000000..e459c811
--- /dev/null
+++ b/examples/multiple-backends/cypress/plugins/index.js
@@ -0,0 +1,6 @@
+module.exports = (on, config) => {
+ require('../../../../task')(on, config)
+ // instrument loaded spec files (and the application code loaded from them)
+ on('file:preprocessor', require('../../../../use-browserify-istanbul'))
+ return config
+}
diff --git a/examples/multiple-backends/cypress/support/index.js b/examples/multiple-backends/cypress/support/index.js
new file mode 100644
index 00000000..dd60efa2
--- /dev/null
+++ b/examples/multiple-backends/cypress/support/index.js
@@ -0,0 +1 @@
+import '../../../../support'
diff --git a/examples/multiple-backends/images/multiple-backends.png b/examples/multiple-backends/images/multiple-backends.png
new file mode 100644
index 00000000..020b8237
Binary files /dev/null and b/examples/multiple-backends/images/multiple-backends.png differ
diff --git a/examples/multiple-backends/main.js b/examples/multiple-backends/main.js
new file mode 100644
index 00000000..5dd69be2
--- /dev/null
+++ b/examples/multiple-backends/main.js
@@ -0,0 +1,3 @@
+window.add = (a, b) => a + b
+
+window.sub = (a, b) => a - b
diff --git a/examples/multiple-backends/package.json b/examples/multiple-backends/package.json
new file mode 100644
index 00000000..779b0f12
--- /dev/null
+++ b/examples/multiple-backends/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "example-multiple-backends",
+ "description": "Combined code coverage from multiple backend code bases, and e2e and unit tests",
+ "devDependencies": {
+ "npm-run-all": "^4.1.5"
+ },
+ "scripts": {
+ "start": "npm-run-all -p start:server1 start:server2",
+ "start:server1": "../../node_modules/.bin/nyc --silent node server1/server",
+ "start:server2": "../../node_modules/.bin/nyc --silent node server2/server",
+ "cy:open": "../../node_modules/.bin/cypress open",
+ "dev": "../../node_modules/.bin/start-test 3003 cy:open",
+ "report": "../../node_modules/.bin/nyc report --reporter text"
+ }
+}
diff --git a/examples/multiple-backends/server1/index.html b/examples/multiple-backends/server1/index.html
new file mode 100644
index 00000000..b6691c8a
--- /dev/null
+++ b/examples/multiple-backends/server1/index.html
@@ -0,0 +1,4 @@
+
+ Page body
+
+
diff --git a/examples/multiple-backends/server1/main-instrumented.js b/examples/multiple-backends/server1/main-instrumented.js
new file mode 100644
index 00000000..0550e9bb
--- /dev/null
+++ b/examples/multiple-backends/server1/main-instrumented.js
@@ -0,0 +1,146 @@
+function cov_6k5v991cn() {
+ var path = 'main.js'
+ var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
+ var global = new Function('return this')()
+ var gcv = '__coverage__'
+ var coverageData = {
+ path: 'main.js',
+ statementMap: {
+ '0': {
+ start: {
+ line: 1,
+ column: 0
+ },
+ end: {
+ line: 1,
+ column: 28
+ }
+ },
+ '1': {
+ start: {
+ line: 1,
+ column: 23
+ },
+ end: {
+ line: 1,
+ column: 28
+ }
+ },
+ '2': {
+ start: {
+ line: 3,
+ column: 0
+ },
+ end: {
+ line: 3,
+ column: 28
+ }
+ },
+ '3': {
+ start: {
+ line: 3,
+ column: 23
+ },
+ end: {
+ line: 3,
+ column: 28
+ }
+ }
+ },
+ fnMap: {
+ '0': {
+ name: '(anonymous_0)',
+ decl: {
+ start: {
+ line: 1,
+ column: 13
+ },
+ end: {
+ line: 1,
+ column: 14
+ }
+ },
+ loc: {
+ start: {
+ line: 1,
+ column: 23
+ },
+ end: {
+ line: 1,
+ column: 28
+ }
+ },
+ line: 1
+ },
+ '1': {
+ name: '(anonymous_1)',
+ decl: {
+ start: {
+ line: 3,
+ column: 13
+ },
+ end: {
+ line: 3,
+ column: 14
+ }
+ },
+ loc: {
+ start: {
+ line: 3,
+ column: 23
+ },
+ end: {
+ line: 3,
+ column: 28
+ }
+ },
+ line: 3
+ }
+ },
+ branchMap: {},
+ s: {
+ '0': 0,
+ '1': 0,
+ '2': 0,
+ '3': 0
+ },
+ f: {
+ '0': 0,
+ '1': 0
+ },
+ b: {},
+ _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
+ hash: 'd384017ecd51a8d90283ba0dec593332209519de'
+ }
+ var coverage = global[gcv] || (global[gcv] = {})
+
+ if (!coverage[path] || coverage[path].hash !== hash) {
+ coverage[path] = coverageData
+ }
+
+ var actualCoverage = coverage[path]
+
+ cov_6k5v991cn = function() {
+ return actualCoverage
+ }
+
+ return actualCoverage
+}
+
+cov_6k5v991cn()
+cov_6k5v991cn().s[0]++
+
+window.add = (a, b) => {
+ cov_6k5v991cn().f[0]++
+ cov_6k5v991cn().s[1]++
+ return a + b
+}
+
+cov_6k5v991cn().s[2]++
+
+window.sub = (a, b) => {
+ cov_6k5v991cn().f[1]++
+ cov_6k5v991cn().s[3]++
+ return a - b
+}
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
diff --git a/examples/multiple-backends/server1/server.js b/examples/multiple-backends/server1/server.js
new file mode 100644
index 00000000..7e4786e7
--- /dev/null
+++ b/examples/multiple-backends/server1/server.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('../../../middleware/express')(app)
+}
+
+app.use(express.static(__dirname))
+
+app.get('/hello', (req, res) => {
+ console.log('sending hello world')
+ res.send('Hello World!')
+})
+
+app.listen(port, () => console.log(`Example app listening on port ${port}!`))
diff --git a/examples/multiple-backends/server2/main-instrumented.js b/examples/multiple-backends/server2/main-instrumented.js
new file mode 100644
index 00000000..0550e9bb
--- /dev/null
+++ b/examples/multiple-backends/server2/main-instrumented.js
@@ -0,0 +1,146 @@
+function cov_6k5v991cn() {
+ var path = 'main.js'
+ var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
+ var global = new Function('return this')()
+ var gcv = '__coverage__'
+ var coverageData = {
+ path: 'main.js',
+ statementMap: {
+ '0': {
+ start: {
+ line: 1,
+ column: 0
+ },
+ end: {
+ line: 1,
+ column: 28
+ }
+ },
+ '1': {
+ start: {
+ line: 1,
+ column: 23
+ },
+ end: {
+ line: 1,
+ column: 28
+ }
+ },
+ '2': {
+ start: {
+ line: 3,
+ column: 0
+ },
+ end: {
+ line: 3,
+ column: 28
+ }
+ },
+ '3': {
+ start: {
+ line: 3,
+ column: 23
+ },
+ end: {
+ line: 3,
+ column: 28
+ }
+ }
+ },
+ fnMap: {
+ '0': {
+ name: '(anonymous_0)',
+ decl: {
+ start: {
+ line: 1,
+ column: 13
+ },
+ end: {
+ line: 1,
+ column: 14
+ }
+ },
+ loc: {
+ start: {
+ line: 1,
+ column: 23
+ },
+ end: {
+ line: 1,
+ column: 28
+ }
+ },
+ line: 1
+ },
+ '1': {
+ name: '(anonymous_1)',
+ decl: {
+ start: {
+ line: 3,
+ column: 13
+ },
+ end: {
+ line: 3,
+ column: 14
+ }
+ },
+ loc: {
+ start: {
+ line: 3,
+ column: 23
+ },
+ end: {
+ line: 3,
+ column: 28
+ }
+ },
+ line: 3
+ }
+ },
+ branchMap: {},
+ s: {
+ '0': 0,
+ '1': 0,
+ '2': 0,
+ '3': 0
+ },
+ f: {
+ '0': 0,
+ '1': 0
+ },
+ b: {},
+ _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
+ hash: 'd384017ecd51a8d90283ba0dec593332209519de'
+ }
+ var coverage = global[gcv] || (global[gcv] = {})
+
+ if (!coverage[path] || coverage[path].hash !== hash) {
+ coverage[path] = coverageData
+ }
+
+ var actualCoverage = coverage[path]
+
+ cov_6k5v991cn = function() {
+ return actualCoverage
+ }
+
+ return actualCoverage
+}
+
+cov_6k5v991cn()
+cov_6k5v991cn().s[0]++
+
+window.add = (a, b) => {
+ cov_6k5v991cn().f[0]++
+ cov_6k5v991cn().s[1]++
+ return a + b
+}
+
+cov_6k5v991cn().s[2]++
+
+window.sub = (a, b) => {
+ cov_6k5v991cn().f[1]++
+ cov_6k5v991cn().s[3]++
+ return a - b
+}
+//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
diff --git a/examples/multiple-backends/server2/server.js b/examples/multiple-backends/server2/server.js
new file mode 100644
index 00000000..a94fb354
--- /dev/null
+++ b/examples/multiple-backends/server2/server.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('../../../middleware/express')(app)
+}
+
+app.use(express.static(__dirname))
+
+app.get('/goodbye', (req, res) => {
+ console.log('sending goodby world')
+ res.send('Boodbye World!')
+})
+
+app.listen(port, () => console.log(`Example service listening on port ${port}!`))
diff --git a/examples/multiple-backends/string-utils.js b/examples/multiple-backends/string-utils.js
new file mode 100644
index 00000000..0d14f807
--- /dev/null
+++ b/examples/multiple-backends/string-utils.js
@@ -0,0 +1,10 @@
+// reverses a string
+const reverse = s => {
+ return s
+ .split('')
+ .reverse()
+ .join('')
+}
+module.exports = {
+ reverse
+}
diff --git a/support.js b/support.js
index c6458da4..4cd90f96 100644
--- a/support.js
+++ b/support.js
@@ -160,27 +160,34 @@ 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 urls = Cypress._.get(
Cypress.env('codeCoverage'),
'url',
'/__coverage__'
)
- cy.request({
- url,
- log: false,
- failOnStatusCode: false
- })
- .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
- return
- }
- sendCoverage(coverage, 'backend')
+ .split(',')
+ .map(u => u.trim())
+ urls.map(url => {
+ logMessage(`Requesting coverage for **${url}**`)
+ cy.request({
+ url,
+ log: false,
+ failOnStatusCode: false
})
+ .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
+ logMessage(`No coverage for: **${url}**`)
+ return
+ }
+ sendCoverage(coverage, `backend: ${url}`)
+ })
+ })
+ logMessage('Finished collecting coverage')
}
})