From 288b61d766249bc0ffd173ed5e9fe6292815c41b Mon Sep 17 00:00:00 2001 From: Gleb Bahmutov Date: Mon, 6 Apr 2020 15:17:44 -0400 Subject: [PATCH 1/2] feat: set flag from plugins task that they were registered (#180) * feat: set flag from plugins task that they were registered BREAKING CHANGE: the task sets an environment variable and needs to return its config. The new registration thus looks like this ```js // your plugins file module.exports = (on, config) => { require('cypress/code-coverage/task')(on, config) // IMPORTANT to return the config object // with the any changed environment variables return config } ``` Support code can check variable `Cypress.env('codeCoverageTasksRegistered')` before calling `cy.task` * update readme * update examples * add small plugins file * update readme * add example using plugins and support * add new example to CI * no need to start server in the example --- .circleci/config.yml | 30 ++++ README.md | 42 ++++- cypress/plugins/index.js | 5 +- .../before-all-visit/cypress/plugins/index.js | 5 +- .../cypress/plugins/index.js | 5 +- examples/same-folder/plugins.js | 3 +- .../support-files/cypress/plugins/index.js | 3 +- examples/ts-example/cypress/plugins/index.js | 3 +- examples/use-plugins-and-support/README.md | 5 + examples/use-plugins-and-support/cypress.json | 5 + .../cypress/integration/spec.js | 18 +++ examples/use-plugins-and-support/index.html | 4 + .../main-instrumented.js | 146 ++++++++++++++++++ examples/use-plugins-and-support/main.js | 3 + .../use-plugins-and-support/package-lock.json | 4 + examples/use-plugins-and-support/package.json | 8 + plugins.js | 13 ++ support.js | 27 +++- task.js | 29 +++- 19 files changed, 341 insertions(+), 17 deletions(-) create mode 100644 examples/use-plugins-and-support/README.md create mode 100644 examples/use-plugins-and-support/cypress.json create mode 100644 examples/use-plugins-and-support/cypress/integration/spec.js create mode 100644 examples/use-plugins-and-support/index.html create mode 100644 examples/use-plugins-and-support/main-instrumented.js create mode 100644 examples/use-plugins-and-support/main.js create mode 100644 examples/use-plugins-and-support/package-lock.json create mode 100644 examples/use-plugins-and-support/package.json create mode 100644 plugins.js diff --git a/.circleci/config.yml b/.circleci/config.yml index c2a1fa2a1..58e7a99cb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -251,6 +251,35 @@ workflows: node ../../scripts/only-covered main.js working_directory: examples/support-files + - cypress/run: + attach-workspace: true + name: example-use-plugins-and-support + requires: + - cypress/install + # there are no jobs to follow this one + # so no need to save the workspace files (saves time) + no-workspace: true + command: npx cypress run --project examples/use-plugins-and-support + # store screenshots and videos + store_artifacts: true + post-steps: + - run: cat examples/use-plugins-and-support/.nyc_output/out.json + # 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/use-plugins-and-support/coverage + # make sure the examples captures 100% of code + - run: + command: npx nyc report --check-coverage true --lines 100 + working_directory: examples/use-plugins-and-support + - run: + name: Check code coverage 📈 + command: | + node ../../scripts/check-coverage main.js + node ../../scripts/only-covered main.js + working_directory: examples/use-plugins-and-support + - publish: filters: branches: @@ -266,3 +295,4 @@ workflows: - example-ts-example - example-same-folder - example-support-files + - example-use-plugins-and-support diff --git a/README.md b/README.md index b65e42dc0..dbf0c5329 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,10 @@ Register tasks in your `cypress/plugins/index.js` file ```js module.exports = (on, config) => { - on('task', require('@cypress/code-coverage/task')) + require('@cypress/code-coverage/task')(on, config) + // IMPORTANT to return the config object + // with the any changed environment variables + return config } ``` @@ -109,8 +112,9 @@ Put the following in `cypress/plugins/index.js` file to use `.babelrc` file ```js module.exports = (on, config) => { - on('task', require('@cypress/code-coverage/task')) + require('@cypress/code-coverage/task')(on, config) on('file:preprocessor', require('@cypress/code-coverage/use-babelrc')) + return config } ``` @@ -122,11 +126,12 @@ If you cannot use `.babelrc` for some reason (maybe it is used by other tools?), ```js module.exports = (on, config) => { - on('task', require('@cypress/code-coverage/task')) + require('@cypress/code-coverage/task')(on, config) on( 'file:preprocessor', require('@cypress/code-coverage/use-browserify-istanbul') ) + return config } ``` @@ -349,6 +354,37 @@ npm run dev:no:coverage - [bahmutov/code-coverage-subfolder-example](https://github.com/bahmutov/code-coverage-subfolder-example) shows how to instrument `app` folder using `nyc instrument` as a separate step before running E2E tests - [bahmutov/docker-with-cypress-included-code-coverage-example](https://github.com/bahmutov/docker-with-cypress-included-code-coverage-example) runs tests inside pre-installed Cypress using [cypress/included:x.y.z](https://github.com/cypress-io/cypress-docker-images/tree/master/included) Docker image and reports code coverage. +## Migrations + +### v2 to v3 + +Change the plugins file `cypress/plugins/index.js` + +```js +// BEFORE +module.exports = (on, config) => { + on('task', require('@cypress/code-coverage/task')) +} +// AFTER +module.exports = (on, config) => { + require('@cypress/code-coverage/task')(on, config) + // IMPORTANT to return the config object + // with the any changed environment variables + return config +} +``` + +**Tip:** we include [plugins.js](plugins.js) file you can point at from your code in simple cases. From your `cypress.json` file: + +```json +{ + "pluginsFile": "node_modules/@cypress/code-coverage/plugins", + "supportFile": "node_modules/@cypress/code-coverage/support" +} +``` + +See [examples/use-plugins-and-support](examples/use-plugins-and-support) + ## Debugging This plugin uses [debug](https://github.com/visionmedia/debug) module to output additional logging messages from its [task.js](task.js) file. This can help with debugging errors while saving code coverage or reporting. In order to see these messages, run Cypress from the terminal with environment variable `DEBUG=code-coverage`. Example using Unix syntax to set the variable: diff --git a/cypress/plugins/index.js b/cypress/plugins/index.js index f45185fc4..689350e64 100644 --- a/cypress/plugins/index.js +++ b/cypress/plugins/index.js @@ -1,5 +1,5 @@ module.exports = (on, config) => { - on('task', require('../../task')) + require('../../task')(on, config) // also use .babelrc file when bundling spec files // to get the code coverage from unit tests @@ -9,4 +9,7 @@ module.exports = (on, config) => { // or use browserify and just push babel-plugin-istanbul // directory to the list of babelify plugins // on('file:preprocessor', require('../../use-browserify-istanbul')) + + // IMPORTANT to return the config object with changed environment variable + return config } diff --git a/examples/before-all-visit/cypress/plugins/index.js b/examples/before-all-visit/cypress/plugins/index.js index 172deda45..a7fb752b7 100644 --- a/examples/before-all-visit/cypress/plugins/index.js +++ b/examples/before-all-visit/cypress/plugins/index.js @@ -1,3 +1,6 @@ module.exports = (on, config) => { - on('task', require('../../../../task')) + require('../../../../task')(on, config) + // IMPORTANT to return the config object + // with the any changed environment variables + return config } diff --git a/examples/before-each-visit/cypress/plugins/index.js b/examples/before-each-visit/cypress/plugins/index.js index 172deda45..a7fb752b7 100644 --- a/examples/before-each-visit/cypress/plugins/index.js +++ b/examples/before-each-visit/cypress/plugins/index.js @@ -1,3 +1,6 @@ module.exports = (on, config) => { - on('task', require('../../../../task')) + require('../../../../task')(on, config) + // IMPORTANT to return the config object + // with the any changed environment variables + return config } diff --git a/examples/same-folder/plugins.js b/examples/same-folder/plugins.js index 723dd9e11..2df3f068a 100644 --- a/examples/same-folder/plugins.js +++ b/examples/same-folder/plugins.js @@ -1,4 +1,5 @@ module.exports = (on, config) => { - on('task', require('../../task')) + require('../../task')(on, config) on('file:preprocessor', require('../../use-babelrc')) + return config } diff --git a/examples/support-files/cypress/plugins/index.js b/examples/support-files/cypress/plugins/index.js index 42aa38ea8..b17c48db1 100644 --- a/examples/support-files/cypress/plugins/index.js +++ b/examples/support-files/cypress/plugins/index.js @@ -1,4 +1,5 @@ module.exports = (on, config) => { - on('task', require('../../../../task')) + require('../../../../task')(on, config) on('file:preprocessor', require('../../../../use-babelrc')) + return config } diff --git a/examples/ts-example/cypress/plugins/index.js b/examples/ts-example/cypress/plugins/index.js index 172deda45..fa838f18c 100644 --- a/examples/ts-example/cypress/plugins/index.js +++ b/examples/ts-example/cypress/plugins/index.js @@ -1,3 +1,4 @@ module.exports = (on, config) => { - on('task', require('../../../../task')) + require('../../../../task')(on, config) + return config } diff --git a/examples/use-plugins-and-support/README.md b/examples/use-plugins-and-support/README.md new file mode 100644 index 000000000..0f1251b3b --- /dev/null +++ b/examples/use-plugins-and-support/README.md @@ -0,0 +1,5 @@ +# example: use-plugins-and-support + +Using included plugins and support files + +See [cypress.json](cypress.json) file diff --git a/examples/use-plugins-and-support/cypress.json b/examples/use-plugins-and-support/cypress.json new file mode 100644 index 000000000..3681940e8 --- /dev/null +++ b/examples/use-plugins-and-support/cypress.json @@ -0,0 +1,5 @@ +{ + "pluginsFile": "../../plugins", + "supportFile": "../../support", + "fixturesFolder": false +} diff --git a/examples/use-plugins-and-support/cypress/integration/spec.js b/examples/use-plugins-and-support/cypress/integration/spec.js new file mode 100644 index 000000000..401618d11 --- /dev/null +++ b/examples/use-plugins-and-support/cypress/integration/spec.js @@ -0,0 +1,18 @@ +/// +describe('coverage information', () => { + beforeEach(() => { + cy.visit('index.html') + }) + + it('calls add', () => { + cy.window() + .invoke('add', 2, 3) + .should('equal', 5) + }) + + it('calls sub', () => { + cy.window() + .invoke('sub', 2, 3) + .should('equal', -1) + }) +}) diff --git a/examples/use-plugins-and-support/index.html b/examples/use-plugins-and-support/index.html new file mode 100644 index 000000000..b6691c8af --- /dev/null +++ b/examples/use-plugins-and-support/index.html @@ -0,0 +1,4 @@ + + Page body + + diff --git a/examples/use-plugins-and-support/main-instrumented.js b/examples/use-plugins-and-support/main-instrumented.js new file mode 100644 index 000000000..0550e9bb7 --- /dev/null +++ b/examples/use-plugins-and-support/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/use-plugins-and-support/main.js b/examples/use-plugins-and-support/main.js new file mode 100644 index 000000000..5dd69be2f --- /dev/null +++ b/examples/use-plugins-and-support/main.js @@ -0,0 +1,3 @@ +window.add = (a, b) => a + b + +window.sub = (a, b) => a - b diff --git a/examples/use-plugins-and-support/package-lock.json b/examples/use-plugins-and-support/package-lock.json new file mode 100644 index 000000000..6ec504937 --- /dev/null +++ b/examples/use-plugins-and-support/package-lock.json @@ -0,0 +1,4 @@ +{ + "name": "example-before-each-visit", + "lockfileVersion": 1 +} diff --git a/examples/use-plugins-and-support/package.json b/examples/use-plugins-and-support/package.json new file mode 100644 index 000000000..c56dbb885 --- /dev/null +++ b/examples/use-plugins-and-support/package.json @@ -0,0 +1,8 @@ +{ + "name": "use-plugins-and-support", + "description": "Using included plugins and support files", + "devDependencies": {}, + "scripts": { + "cy:open": "../../node_modules/.bin/cypress open" + } +} diff --git a/plugins.js b/plugins.js new file mode 100644 index 000000000..0ba5ce57f --- /dev/null +++ b/plugins.js @@ -0,0 +1,13 @@ +// common Cypress plugin file you can point at to have the +// code coverage tasks registered correctly. From your "cypress.json" file +// { +// "pluginsFile": "@cypress/code-coverage/plugins", +// "supportFile": "@cypress/code-coverage/support" +// } +// +module.exports = (on, config) => { + require('./task')(on, config) + // IMPORTANT to return the config object + // with the any changed environment variables + return config +} diff --git a/support.js b/support.js index 0f71ca4f1..fc2c052de 100644 --- a/support.js +++ b/support.js @@ -75,13 +75,7 @@ const filterSpecsFromCoverage = totalCoverage => { return coverage } -// to disable code coverage commands and save time -// pass environment variable coverage=false -// cypress run --env coverage=false -// see https://on.cypress.io/environment-variables -if (Cypress.env('coverage') === false) { - console.log('Skipping code coverage hooks') -} else { +const registerHooks = () => { let windowCoverageObjects const hasE2ECoverage = () => Boolean(windowCoverageObjects.length) @@ -206,3 +200,22 @@ if (Cypress.env('coverage') === false) { }) }) } + +// to disable code coverage commands and save time +// pass environment variable coverage=false +// cypress run --env coverage=false +// see https://on.cypress.io/environment-variables +if (Cypress.env('coverage') === false) { + console.log('Skipping code coverage hooks') +} else if (Cypress.env('codeCoverageTasksRegistered') !== true) { + // register a hook just to log a message + before(() => { + logMessage(` + ⚠️ Code coverage tasks were not registered by the plugins file. + See [support issue](https://github.com/cypress-io/code-coverage/issues/179) + for possible workarounds. + `) + }) +} else { + registerHooks() +} diff --git a/task.js b/task.js index b597c909a..206548c36 100644 --- a/task.js +++ b/task.js @@ -37,7 +37,7 @@ function saveCoverage(coverage) { writeFileSync(nycFilename, JSON.stringify(coverage, null, 2)) } -module.exports = { +const tasks = { /** * Clears accumulated code coverage information. * @@ -133,3 +133,30 @@ module.exports = { return nyc.report().then(returnReportFolder) } } + +/** + * Registers code coverage collection and reporting tasks. + * Sets an environment variable to tell the browser code that it can + * send the coverage. + * @example + ``` + // your plugins file + module.exports = (on, config) => { + require('cypress/code-coverage/task')(on, config) + // IMPORTANT to return the config object + // with the any changed environment variables + return config + } + ``` +*/ +function registerCodeCoverageTasks(on, config) { + on('task', tasks) + + // set a variable to let the hooks running in the browser + // know that they can send coverage commands + config.env.codeCoverageTasksRegistered = true + + return config +} + +module.exports = registerCodeCoverageTasks From 57bbc6b02177dcbcaa4c72929d0f8bd4cf92420f Mon Sep 17 00:00:00 2001 From: Cory Armbrecht Date: Mon, 6 Apr 2020 15:18:52 -0400 Subject: [PATCH 2/2] Removed Performance Warning, fixed by PR #98 (#178) Removed Performance Warning, which was fixed by PR #98 --- README.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/README.md b/README.md index dbf0c5329..5cdc0009c 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,6 @@ > Saves the code coverage collected during Cypress tests -**⚠️ Performance Warning** -This plugin will slow down your tests. There will be more web application JavaScript code to execute due to instrumentation, and there will be code coverage information to merge and save after each test. Track issue [#26](https://github.com/cypress-io/code-coverage/issues/26) for current progress. - ## Install ```shell