Skip to content

Commit

Permalink
feat: add code coverage merge command (#25)
Browse files Browse the repository at this point in the history
* working on merging code coverage

* copy found coverage files

* working

* ci job

* merge in the right example folder

* use rimraf

* update readme

* try using newer node via executor

* use slim

* use browser image?

* hmm

* add report

* add report script
  • Loading branch information
bahmutov authored Apr 2, 2023
1 parent b40ecbd commit 89cb2d0
Show file tree
Hide file tree
Showing 15 changed files with 433 additions and 14 deletions.
32 changes: 31 additions & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
version: 2.1
orbs:
# https://github.com/cypress-io/circleci-orb
cypress: cypress-io/cypress@2 # used to run e2e tests
cypress: cypress-io/cypress@2.2.0 # used to run e2e tests
node: circleci/[email protected] # used to publish new NPM version
win: circleci/windows@2 # run a test job on Windows

Expand Down Expand Up @@ -126,6 +126,36 @@ workflows:
command: npm run check
working_directory: examples/backend

- cypress/run:
name: merge coverage
executor: cypress/browsers-chrome99-ff97
requires:
- cypress/install
# grab the workspace created by cypress/install job
attach-workspace: true
working_directory: examples/merge-coverage
command: 'npm run cy:run1 && npm run report && npm run cy:run2 && npm run report'

# there are no jobs to follow this one
# so no need to save the workspace files (saves time)
no-workspace: true
post-steps:
- run:
command: npm run merge
working_directory: examples/merge-coverage
# store the created coverage report folder
# you can click on it in the CircleCI UI
# to see live static HTML site
- store_artifacts:
path: examples/merge-coverage/coverage
- run:
command: npm run report
working_directory: examples/merge-coverage
- run:
name: Check code coverage 📈
command: npm run check
working_directory: examples/merge-coverage

- cypress/run:
name: fullstack coverage
requires:
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ cypress-coverage/
examples/*/cypress/videos
examples/*/cypress/screenshots
yarn.lock
cc1
cc2
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,31 @@ npm run dev
npm run dev:no:coverage
```

## Merge coverage reports

When running tests in parallel, you can download all coverage folders into one top folder and then merge the coverage and regenerate new combined report using `cc-merge` command included in this module.

For example, if this is the top folder with downloaded coverage subfolders:

```
project/
reports/
one/
coverage-final.json
two/
coverage-final.json
three/
coverage-final.json
```

Use this command to combine all `coverage-final.json` files

```
npx cc-merge reports
```

The coverage will be written into `.nyc_output` folder (which is automatically cleared) and the reports will be written into `coverage` folder.

## Links

- 🎓 Take my course [Testing The Swag Store](https://cypress.tips/courses/swag-store) that shows e2e and component code coverage
Expand Down
63 changes: 63 additions & 0 deletions bin/cc-merge.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env node

const debug = require('debug')('code-coverage')

// finds all "coverage-final.json" files in the give folder
// and merges them into a single "cc-merged/out.json" file
// Use:
// npx cc-merge <top coverage folder>
const topCoverageFolder = process.argv[2]
if (!topCoverageFolder) {
console.error('use: npx cc-merge <top coverage folder>')
process.exit(1)
}

const coverageFilename = 'coverage-final.json'
debug('looking for %s files in %s', coverageFilename, topCoverageFolder)

const fs = require('fs')
const rimraf = require('rimraf')
const path = require('path')
const globby = require('globby')
const allFiles = globby.sync(`**/${coverageFilename}`, {
absolute: true,
cwd: topCoverageFolder,
})
debug('found %d coverage files', allFiles.length)
debug(allFiles.join(','))

if (!allFiles.length) {
console.error(
'Could not find any %s files in the folder %s',
coverageFilename,
topCoverageFolder,
)
process.exit(1)
}

const nycOutputFolder = '.nyc_output'
if (fs.existsSync(nycOutputFolder)) {
debug('deleting the existing folder %s', nycOutputFolder)
rimraf.nativeSync(nycOutputFolder)
}

if (!fs.existsSync(nycOutputFolder)) {
debug('creating folder %s', nycOutputFolder)
fs.mkdirSync(nycOutputFolder)
}
allFiles.forEach((filename, k) => {
const outputFilename = path.join(nycOutputFolder, `c-${k + 1}.json`)
fs.copyFileSync(filename, outputFilename)
debug('%d: copied %s to %s', k + 1, filename, outputFilename)
})

const { getNycOptions } = require('../task-utils')

const processWorkingDirectory = process.cwd()
const nycReportOptions = getNycOptions(processWorkingDirectory)
debug('calling NYC reporter with options %o', nycReportOptions)
debug('current working directory is %s', processWorkingDirectory)
const NYC = require('nyc')
const nyc = new NYC(nycReportOptions)

nyc.report()
3 changes: 3 additions & 0 deletions examples/merge-coverage/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": ["istanbul"]
}
3 changes: 3 additions & 0 deletions examples/merge-coverage/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# example: merge-coverage

Run each spec one by one, producing partial report. Then use cc-merge to merge them into a single combined report, and then produce the final report.
18 changes: 18 additions & 0 deletions examples/merge-coverage/cypress.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
const { defineConfig } = require('cypress')

module.exports = defineConfig({
e2e: {
env: {
coverage: {
exclude: '**/cypress/**',
},
},
fixturesFolder: false,
video: false,
// We've imported your old cypress plugins here.
// You may want to clean this up later by importing these.
setupNodeEvents(on, config) {
return require('./cypress/plugins/index.js')(on, config)
},
},
})
6 changes: 6 additions & 0 deletions examples/merge-coverage/cypress/e2e/add.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference types="cypress" />
import { add } from '../../src/math'

it('adds two numbers', () => {
expect(add(2, 3)).to.equal(5)
})
6 changes: 6 additions & 0 deletions examples/merge-coverage/cypress/e2e/sub.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/// <reference types="cypress" />
import { sub } from '../../src/math'

it('subtracts two numbers', () => {
expect(sub(2, 3)).to.equal(-1)
})
5 changes: 5 additions & 0 deletions examples/merge-coverage/cypress/plugins/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module.exports = (on, config) => {
require('../../../../task')(on, config)
on('file:preprocessor', require('../../../../use-babelrc'))
return config
}
1 change: 1 addition & 0 deletions examples/merge-coverage/cypress/support/e2e.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import '../../../../support'
15 changes: 15 additions & 0 deletions examples/merge-coverage/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "example-merge-coverage",
"description": "Merge partial code coverage reports",
"scripts": {
"cy:open": "../../node_modules/.bin/cypress open",
"cy:run1": "npm run clean && rm -rf cc1 && ../../node_modules/.bin/cypress run --spec cypress/e2e/add.cy.js && mv coverage cc1",
"cy:run2": "npm run clean && rm -rf cc2 && ../../node_modules/.bin/cypress run --spec cypress/e2e/sub.cy.js && mv coverage cc2",
"check:covered": "../../node_modules/.bin/check-coverage --from coverage/coverage-final.json math.js",
"check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json math.js",
"check": "npm run check:covered && npm run check:extras",
"clean": "rm -rf .nyc_output && rm -rf coverage",
"merge": "DEBUG=code-coverage ../../bin/cc-merge.js .",
"report": "../../node_modules/.bin/nyc report"
}
}
19 changes: 19 additions & 0 deletions examples/merge-coverage/src/math.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/**
* Adds two numbers together
* @param {number} a
* @param {number} b
*/
export const add = (a, b) => {
console.log('add %d to %d', a, b)
return a + b
}

/**
* subtracts numbers
* @param {number} a
* @param {number} b
*/
export const sub = (a, b) => {
console.log('sub %d to %d', a, b)
return a - b
}
Loading

0 comments on commit 89cb2d0

Please sign in to comment.