├── .babelrc
├── cypress
├── support
│ └── e2e.js
├── unit.js
├── about.js
├── about.html
├── fixtures
│ ├── example.json
│ └── coverage.json
├── index.html
├── app.js
├── e2e
│ ├── minimatch.cy.js
│ ├── calculator.cy.js
│ ├── calculator-po.js
│ ├── spec.cy.js
│ ├── combine.cy.js
│ ├── merge.cy.js
│ └── filtering.cy.js
├── plugins
│ └── index.js
├── calculator
│ ├── utils.js
│ ├── index.html
│ ├── styles.css
│ └── app.js
└── README.md
├── examples
├── all-files
│ ├── README.md
│ ├── cypress
│ │ ├── support
│ │ │ ├── e2e.js
│ │ │ └── commands.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── .babelrc
│ ├── main.js
│ ├── second.js
│ ├── not-covered.js
│ ├── index.html
│ ├── cypress.config.js
│ └── package.json
├── no-files
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ ├── e2e.js
│ │ │ └── commands.js
│ │ ├── integration
│ │ │ └── spec.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── main.js
│ ├── README.md
│ ├── index.html
│ ├── cypress.config.js
│ └── package.json
├── one-spec
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── integration
│ │ │ ├── spec-b.js
│ │ │ └── spec-a.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── README.md
│ ├── main.js
│ ├── index.html
│ ├── package.json
│ ├── cypress.config.js
│ └── main-instrumented.js
├── backend
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── integration
│ │ │ └── spec.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── server
│ │ ├── index.html
│ │ └── server.js
│ ├── README.md
│ ├── cypress.config.js
│ └── package.json
├── exclude-files
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ ├── e2e.js
│ │ │ └── commands.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── main.js
│ ├── README.md
│ ├── second.js
│ ├── index.html
│ ├── cypress.config.js
│ └── package.json
├── filter-files
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ ├── index.js
│ │ │ └── commands.js
│ │ ├── integration
│ │ │ └── spec.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── main.js
│ ├── README.md
│ ├── cypress.json
│ ├── index.html
│ └── package.json
├── multiple-files
│ ├── cypress
│ │ ├── support
│ │ │ ├── e2e.js
│ │ │ └── commands.js
│ │ ├── integration
│ │ │ ├── first.js
│ │ │ └── second.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── .babelrc
│ ├── src
│ │ ├── first.js
│ │ ├── second.js
│ │ └── third.js
│ ├── second.html
│ ├── index.html
│ ├── README.md
│ ├── cypress.config.js
│ └── package.json
├── placeholders
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ └── index.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ ├── spec-a.js
│ │ │ └── spec-b.js
│ ├── src
│ │ ├── a.js
│ │ └── b.js
│ ├── cypress.config.js
│ └── package.json
├── same-folder
│ ├── .babelrc
│ ├── support
│ │ └── support.js
│ ├── main.js
│ ├── README.md
│ ├── index.html
│ ├── unit-utils.js
│ ├── plugins.js
│ ├── cypress
│ │ └── fixtures
│ │ │ └── example.json
│ ├── package.json
│ ├── cypress.config.js
│ ├── spec.js
│ └── main-instrumented.js
├── support-files
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ ├── e2e.js
│ │ │ └── commands.js
│ │ ├── integration
│ │ │ └── spec.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── README.md
│ ├── main.js
│ ├── index.html
│ ├── cypress.config.js
│ └── package.json
├── ts-example
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── calc.ts
│ ├── index.html
│ ├── main.ts
│ ├── README.md
│ ├── cypress.config.js
│ └── package.json
├── unit-tests-js
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── README.md
│ ├── src
│ │ └── utils
│ │ │ └── misc.js
│ ├── package.json
│ └── cypress.config.js
├── use-webpack
│ ├── .babelrc
│ ├── cypress.json
│ ├── src
│ │ ├── calc.js
│ │ └── index.js
│ ├── cypress
│ │ ├── integration
│ │ │ └── spec.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── README.md
│ ├── package.json
│ └── webpack.config.js
├── fullstack
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── main.js
│ ├── server
│ │ ├── index.html
│ │ ├── server.js
│ │ └── main-instrumented.js
│ ├── images
│ │ └── fullstack.png
│ ├── string-utils.js
│ ├── README.md
│ ├── cypress.config.js
│ └── package.json
├── merge-coverage
│ ├── .babelrc
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── e2e
│ │ │ ├── sub.cy.js
│ │ │ └── add.cy.js
│ │ └── plugins
│ │ │ └── index.js
│ ├── README.md
│ ├── src
│ │ └── math.js
│ ├── cypress.config.js
│ └── package.json
├── unit-tests-ts
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── integration
│ │ │ └── spec.ts
│ │ └── plugins
│ │ │ └── index.js
│ ├── .babelrc
│ ├── tsconfig.json
│ ├── src
│ │ └── utils
│ │ │ └── misc.ts
│ ├── README.md
│ ├── package.json
│ └── cypress.config.js
├── before-all-visit
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── main.js
│ ├── index.html
│ ├── package-lock.json
│ ├── README.md
│ ├── cypress.config.js
│ ├── package.json
│ └── main-instrumented.js
├── before-each-visit
│ ├── cypress
│ │ ├── support
│ │ │ └── e2e.js
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── integration
│ │ │ └── spec.js
│ ├── main.js
│ ├── index.html
│ ├── package-lock.json
│ ├── README.md
│ ├── cypress.config.js
│ ├── package.json
│ └── main-instrumented.js
├── component
│ ├── src
│ │ ├── calc.js
│ │ ├── Hello.cy.jsx
│ │ └── Hello.jsx
│ ├── cc.json
│ ├── cypress
│ │ └── support
│ │ │ ├── component.js
│ │ │ └── component-index.html
│ ├── package.json
│ └── cypress.config.js
├── docker-paths
│ ├── app
│ │ ├── main.js
│ │ ├── second.js
│ │ └── index.html
│ ├── images
│ │ ├── file.png
│ │ └── files.png
│ ├── cypress.json
│ ├── cypress
│ │ └── integration
│ │ │ └── spec.js
│ ├── package.json
│ └── README.md
└── use-plugins-and-support
│ ├── main.js
│ ├── index.html
│ ├── package-lock.json
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ └── integration
│ │ └── spec.js
│ ├── package.json
│ └── main-instrumented.js
├── images
├── gui.png
├── summary.png
├── warning.png
├── coverage.jpg
├── expect-backend.png
├── instrumented-code.png
└── window-coverage-object.png
├── .nycrc.json
├── .prettierrc.json
├── plugins.js
├── cypress-backend.json
├── .gitignore
├── renovate.json
├── middleware
├── express.js
├── hapi.js
└── nextjs.js
├── use-browserify-istanbul.js
├── use-babelrc.js
├── cypress.config.ts
├── .github
└── workflows
│ ├── badges.yml
│ └── ci.yml
├── LICENSE.md
├── plugin.js
├── bin
└── cc-merge.js
├── common-utils.js
├── src
└── utils.js
├── package.json
├── support-utils.js
├── task.js
├── support.js
└── task-utils.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../support'
2 |
--------------------------------------------------------------------------------
/examples/all-files/README.md:
--------------------------------------------------------------------------------
1 | # example: all files
2 |
--------------------------------------------------------------------------------
/cypress/unit.js:
--------------------------------------------------------------------------------
1 | export const add = (a, b) => a + b
2 |
--------------------------------------------------------------------------------
/examples/all-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/examples/no-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/no-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/examples/one-spec/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/all-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/backend/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/exclude-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/exclude-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/examples/filter-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/filter-files/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/examples/multiple-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/examples/one-spec/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/placeholders/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/same-folder/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/support-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/support-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/examples/ts-example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/use-webpack/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/backend/server/index.html:
--------------------------------------------------------------------------------
1 |
2 | test backend
3 |
4 |
--------------------------------------------------------------------------------
/examples/fullstack/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/merge-coverage/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/multiple-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/examples/ts-example/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/before-all-visit/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/before-each-visit/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/merge-coverage/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/multiple-files/src/first.js:
--------------------------------------------------------------------------------
1 | console.log('this is the first script')
2 |
--------------------------------------------------------------------------------
/examples/multiple-files/src/second.js:
--------------------------------------------------------------------------------
1 | console.log('this is the second script')
2 |
--------------------------------------------------------------------------------
/examples/multiple-files/src/third.js:
--------------------------------------------------------------------------------
1 | console.log('this is the third script')
2 |
--------------------------------------------------------------------------------
/examples/placeholders/cypress/support/index.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 |
--------------------------------------------------------------------------------
/examples/one-spec/README.md:
--------------------------------------------------------------------------------
1 | # example: one-spec
2 |
3 | Only running a single spec
4 |
--------------------------------------------------------------------------------
/examples/same-folder/support/support.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | import '../../../support'
3 |
--------------------------------------------------------------------------------
/examples/component/src/calc.js:
--------------------------------------------------------------------------------
1 | export const add = (a, b) => {
2 | return a + b
3 | }
4 |
--------------------------------------------------------------------------------
/examples/all-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/backend/README.md:
--------------------------------------------------------------------------------
1 | # example: backend
2 |
3 | > Getting code coverage from backend
4 |
--------------------------------------------------------------------------------
/examples/fullstack/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/no-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/one-spec/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/support-files/README.md:
--------------------------------------------------------------------------------
1 | # example: support-files
2 |
3 | Filtering out support files
4 |
--------------------------------------------------------------------------------
/images/gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/gui.png
--------------------------------------------------------------------------------
/examples/exclude-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/filter-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/no-files/README.md:
--------------------------------------------------------------------------------
1 | # example: no-files
2 |
3 | All tests are skipped, no code coverage
4 |
--------------------------------------------------------------------------------
/examples/same-folder/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/support-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/ts-example/calc.ts:
--------------------------------------------------------------------------------
1 | export const add = (a: number, b: number) => {
2 | return a + b
3 | }
4 |
--------------------------------------------------------------------------------
/images/summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/summary.png
--------------------------------------------------------------------------------
/images/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/warning.png
--------------------------------------------------------------------------------
/examples/before-all-visit/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/before-each-visit/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/component/cc.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "code-coverage",
3 | "main": "../../../../support.js"
4 | }
5 |
--------------------------------------------------------------------------------
/examples/docker-paths/app/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/examples/ts-example/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/coverage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/coverage.jpg
--------------------------------------------------------------------------------
/examples/same-folder/README.md:
--------------------------------------------------------------------------------
1 | # example: same-folder
2 |
3 | Check if test files are correctly filtered out
4 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/.nycrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": ["support-utils.js", "task-utils.js"],
3 | "reporter": ["html", "text"]
4 | }
5 |
--------------------------------------------------------------------------------
/images/expect-backend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/expect-backend.png
--------------------------------------------------------------------------------
/cypress/about.js:
--------------------------------------------------------------------------------
1 | document
2 | .getElementById('content')
3 | .appendChild(document.createTextNode('Est. 2019'))
4 |
--------------------------------------------------------------------------------
/examples/all-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/examples/filter-files/README.md:
--------------------------------------------------------------------------------
1 | # example: filter-files
2 |
3 | Testing explicit filtering of files from code coverage
4 |
--------------------------------------------------------------------------------
/examples/filter-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/examples/multiple-files/second.html:
--------------------------------------------------------------------------------
1 |
2 | Second page
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/no-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/examples/same-folder/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/images/instrumented-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/instrumented-code.png
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "tabWidth": 2,
4 | "semi": false,
5 | "singleQuote": true
6 | }
7 |
--------------------------------------------------------------------------------
/examples/before-all-visit/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/before-all-visit/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-before-all-visit",
3 | "lockfileVersion": 1
4 | }
5 |
--------------------------------------------------------------------------------
/examples/before-each-visit/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/before-each-visit/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-before-each-visit",
3 | "lockfileVersion": 1
4 | }
5 |
--------------------------------------------------------------------------------
/examples/exclude-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/examples/fullstack/server/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/multiple-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/examples/placeholders/src/a.js:
--------------------------------------------------------------------------------
1 | export const myFunc = () => {
2 | const a = 10
3 | const b = 20
4 | return a + b
5 | }
6 |
--------------------------------------------------------------------------------
/examples/support-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '../../../../support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/examples/same-folder/unit-utils.js:
--------------------------------------------------------------------------------
1 | export const reverse = s =>
2 | s
3 | .split('')
4 | .reverse()
5 | .join('')
6 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/README.md:
--------------------------------------------------------------------------------
1 | # example: unit-tests-js
2 |
3 | Examples that only run unit tests written using JavaScript
4 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-before-each-visit",
3 | "lockfileVersion": 1
4 | }
5 |
--------------------------------------------------------------------------------
/images/window-coverage-object.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/images/window-coverage-object.png
--------------------------------------------------------------------------------
/cypress/about.html:
--------------------------------------------------------------------------------
1 |
2 | About
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"],
3 | "presets": [
4 | "@babel/preset-typescript"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/examples/docker-paths/images/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/examples/docker-paths/images/file.png
--------------------------------------------------------------------------------
/examples/docker-paths/images/files.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/examples/docker-paths/images/files.png
--------------------------------------------------------------------------------
/examples/fullstack/images/fullstack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bahmutov/cypress-code-coverage/HEAD/examples/fullstack/images/fullstack.png
--------------------------------------------------------------------------------
/examples/docker-paths/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "pluginsFile": "../../plugins",
3 | "supportFile": "../../support",
4 | "fixturesFolder": false
5 | }
6 |
--------------------------------------------------------------------------------
/examples/placeholders/src/b.js:
--------------------------------------------------------------------------------
1 | export const anotherFunction = () => {
2 | const hello = 'hello'
3 | return hello.split('').reverse().join('')
4 | }
5 |
--------------------------------------------------------------------------------
/examples/ts-example/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/examples/component/cypress/support/component.js:
--------------------------------------------------------------------------------
1 | import 'code-coverage'
2 | import { mount } from 'cypress/react18'
3 |
4 | Cypress.Commands.add('mount', mount)
5 |
--------------------------------------------------------------------------------
/examples/filter-files/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('works', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 | })
6 |
--------------------------------------------------------------------------------
/examples/no-files/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | it.skip('works', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 | })
6 |
--------------------------------------------------------------------------------
/examples/support-files/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('works', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 | })
6 |
--------------------------------------------------------------------------------
/examples/exclude-files/README.md:
--------------------------------------------------------------------------------
1 | # example: exclude files
2 |
3 | Exclude some files from final coverage report by using `nyc` object in [package.json](package.json) file.
4 |
--------------------------------------------------------------------------------
/examples/multiple-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | First page
3 | Second page
4 |
5 |
6 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/README.md:
--------------------------------------------------------------------------------
1 | # example: use-plugins-and-support
2 |
3 | Using included plugins and support files
4 |
5 | See [cypress.json](cypress.json) file
6 |
--------------------------------------------------------------------------------
/examples/backend/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('has backend code coverage', () => {
3 | cy.visit('/')
4 | cy.request('/hello')
5 | })
6 |
--------------------------------------------------------------------------------
/examples/one-spec/cypress/integration/spec-b.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('spec b', () => {
3 | // should not run
4 | throw new Error('Spec b should not run')
5 | })
6 |
--------------------------------------------------------------------------------
/examples/multiple-files/README.md:
--------------------------------------------------------------------------------
1 | # example: multiple files
2 |
3 | The application has multiple pages which include multiple source files. The coverage increases as we run multiple specs.
4 |
--------------------------------------------------------------------------------
/examples/same-folder/plugins.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../task')(on, config)
3 | on('file:preprocessor', require('../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
6 |
--------------------------------------------------------------------------------
/examples/merge-coverage/cypress/e2e/sub.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | import { sub } from '../../src/math'
3 |
4 | it('subtracts two numbers', () => {
5 | expect(sub(2, 3)).to.equal(-1)
6 | })
7 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "module": "commonjs",
5 | "moduleResolution": "node"
6 | },
7 | "files": ["src/utils/misc.ts"]
8 | }
9 |
--------------------------------------------------------------------------------
/examples/use-webpack/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:5000",
3 | "supportFile": "../../support",
4 | "fixturesFolder": false,
5 | "viewportHeight": 400,
6 | "viewportWidth": 400
7 | }
8 |
--------------------------------------------------------------------------------
/plugins.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('./task')(on, config)
3 | // IMPORTANT to return the config object
4 | // with the any changed environment variables
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/examples/backend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/fullstack/string-utils.js:
--------------------------------------------------------------------------------
1 | // reverses a string
2 | const reverse = s => {
3 | return s
4 | .split('')
5 | .reverse()
6 | .join('')
7 | }
8 | module.exports = {
9 | reverse
10 | }
11 |
--------------------------------------------------------------------------------
/cypress/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | About
4 |
5 | Test page
6 | Open the DevTools to see console messages
7 |
8 |
9 |
--------------------------------------------------------------------------------
/examples/filter-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/merge-coverage/README.md:
--------------------------------------------------------------------------------
1 | # example: merge-coverage
2 |
3 | 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.
4 |
--------------------------------------------------------------------------------
/examples/one-spec/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/same-folder/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/examples/use-webpack/src/calc.js:
--------------------------------------------------------------------------------
1 | export const add = (a, b) => {
2 | return a + b
3 | }
4 |
5 | export const reverse = s => {
6 | return s
7 | .split('')
8 | .reverse()
9 | .join('')
10 | }
11 |
--------------------------------------------------------------------------------
/examples/before-all-visit/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/examples/before-each-visit/cypress/fixtures/example.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Using fixtures to represent data",
3 | "email": "hello@cypress.io",
4 | "body": "Fixtures are a great way to mock data for responses to routes"
5 | }
--------------------------------------------------------------------------------
/examples/exclude-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/merge-coverage/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../plugin')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/support-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/examples/multiple-files/cypress/integration/first.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('first page', () => {
3 | it('loads', () => {
4 | cy.visit('/').should('have.property', '__coverage__')
5 | })
6 | })
7 |
--------------------------------------------------------------------------------
/examples/multiple-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../plugin')(on, config)
3 | on('file:preprocessor', require('../../../../use-babelrc'))
4 |
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/examples/one-spec/index.html:
--------------------------------------------------------------------------------
1 |
2 | One spec
3 | Page body
4 | cypress.tips
5 |
6 |
7 |
--------------------------------------------------------------------------------
/examples/all-files/second.js:
--------------------------------------------------------------------------------
1 | // this file should be excluded from the final coverage numbers
2 | // using "nyc.exclude" list in package.json
3 | window.reverse = s =>
4 | s
5 | .split('')
6 | .reverse()
7 | .join('')
8 |
--------------------------------------------------------------------------------
/examples/exclude-files/second.js:
--------------------------------------------------------------------------------
1 | // this file should be excluded from the final coverage numbers
2 | // using "nyc.exclude" list in package.json
3 | window.reverse = s =>
4 | s
5 | .split('')
6 | .reverse()
7 | .join('')
8 |
--------------------------------------------------------------------------------
/examples/docker-paths/app/second.js:
--------------------------------------------------------------------------------
1 | // this file should be excluded from the final coverage numbers
2 | // using "nyc.exclude" list in package.json
3 | window.reverse = s =>
4 | s
5 | .split('')
6 | .reverse()
7 | .join('')
8 |
--------------------------------------------------------------------------------
/examples/merge-coverage/cypress/e2e/add.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | import { add } from '../../src/math'
3 |
4 | it('adds two numbers', () => {
5 | expect(add(1, 3)).to.equal(4)
6 | expect(add(2, 3)).to.equal(5)
7 | })
8 |
--------------------------------------------------------------------------------
/examples/multiple-files/cypress/integration/second.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('second page', () => {
3 | it('loads', () => {
4 | cy.visit('/second.html').should('have.property', '__coverage__')
5 | })
6 | })
7 |
--------------------------------------------------------------------------------
/cypress-backend.json:
--------------------------------------------------------------------------------
1 | {
2 | "baseUrl": "http://localhost:3003",
3 | "integrationFolder": "cypress/test-backend",
4 | "env": {
5 | "codeCoverage": {
6 | "url": "http://localhost:3003/__coverage__"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/cypress/app.js:
--------------------------------------------------------------------------------
1 | import { map } from 'lodash'
2 |
3 | const list = [{ name: 'joe' }, { name: 'mary' }]
4 | const names = map(list, 'name')
5 | if (true) {
6 | console.log('just names', names)
7 | } else {
8 | console.error('never reached')
9 | }
10 |
--------------------------------------------------------------------------------
/examples/all-files/not-covered.js:
--------------------------------------------------------------------------------
1 | // this file is NOT included from "index.html"
2 | // thus it is not instrumented and not included
3 | // in the final code coverage numbers
4 | function throwsError() {
5 | throw new Error('NO')
6 | }
7 | throwsError()
8 |
--------------------------------------------------------------------------------
/examples/before-all-visit/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | // IMPORTANT to return the config object
4 | // with the any changed environment variables
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/examples/before-each-visit/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | // IMPORTANT to return the config object
4 | // with the any changed environment variables
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/examples/no-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../plugin')(on, config)
3 | // do not instrument the spec files
4 | // on('file:preprocessor', require('../../../../use-babelrc'))
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | cypress/videos
3 | cypress/screenshots
4 | coverage/
5 | .nyc_output/
6 | dist/
7 | .cache/
8 | .vscode/
9 | cypress-coverage/
10 | examples/*/cypress/videos
11 | examples/*/cypress/screenshots
12 | yarn.lock
13 | cc1
14 | cc2
15 |
--------------------------------------------------------------------------------
/examples/component/src/Hello.cy.jsx:
--------------------------------------------------------------------------------
1 | import Hello from './Hello'
2 |
3 | describe('Hello', () => {
4 | it('shows the greeting', () => {
5 | cy.mount( )
6 | cy.contains('Hello, World').should('include.text', 'sum is 5')
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/examples/filter-files/cypress.json:
--------------------------------------------------------------------------------
1 | {
2 | "fixturesFolder": false,
3 | "pluginsFile": "cypress/plugins/index.js",
4 | "baseUrl": "http://localhost:1234",
5 | "env": {
6 | "coverage": {
7 | "exclude": "support/commands.js"
8 | }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/placeholders/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | // instrument the specs and any source files loaded from specs
4 | on('file:preprocessor', require('../../../../use-babelrc'))
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/examples/all-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | // instrument the specs and any source files loaded from specs
4 | on('file:preprocessor', require('../../../../use-babelrc'))
5 |
6 | return config
7 | }
8 |
--------------------------------------------------------------------------------
/examples/component/src/Hello.jsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { add } from './calc'
3 |
4 | const sum = add(2, 3)
5 |
6 | const Hello = ({ greeting }) => (
7 |
8 | Hello, {greeting}! sum is {sum}
9 |
10 | )
11 |
12 | export default Hello
13 |
--------------------------------------------------------------------------------
/examples/fullstack/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../../../task')(on, config)
3 | // instrument loaded spec files (and the application code loaded from them)
4 | on('file:preprocessor', require('../../../../use-browserify-istanbul'))
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/examples/placeholders/cypress/integration/spec-a.js:
--------------------------------------------------------------------------------
1 | ///
2 | // this spec only loads "src/a" module
3 | import { myFunc } from '../../src/a.js'
4 | describe('spec-a', () => {
5 | it('exercises src/a.js', () => {
6 | expect(myFunc(), 'always returns 30').to.equal(30)
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/src/utils/misc.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Very simple check, returns true for arrays as well
3 | */
4 | export function isObject(object: any) {
5 | return object != null && typeof object === 'object'
6 | }
7 |
8 | /**
9 | * Adds two numbers together
10 | */
11 | export const add = (a: number, b: number) => a + b
12 |
--------------------------------------------------------------------------------
/examples/docker-paths/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('docker-paths', () => {
3 | it('works', () => {
4 | cy.visit('dist/index.html')
5 | cy.contains('Page body')
6 |
7 | cy.window()
8 | .invoke('reverse', 'super')
9 | .should('equal', 'repus')
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/README.md:
--------------------------------------------------------------------------------
1 | # example: unit-tests-ts
2 |
3 | Examples that only run unit tests to test code written in TypeScript
4 |
5 | **NOT WORKING** seems the TS code goes through `tsify` plugin, and does not pass via Istanbul plugin no matter what I try. See [issue #361](https://github.com/cypress-io/code-coverage/issues/361).
6 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "automerge": true,
6 | "major": {
7 | "automerge": false
8 | },
9 | "prHourlyLimit": 2,
10 | "updateNotScheduled": false,
11 | "timezone": "America/New_York",
12 | "schedule": [
13 | "every weekend"
14 | ],
15 | "masterIssue": true
16 | }
17 |
--------------------------------------------------------------------------------
/middleware/express.js:
--------------------------------------------------------------------------------
1 | // for Express.js
2 | module.exports = app => {
3 | // expose "GET __coverage__" endpoint that just returns
4 | // global coverage information (if the application has been instrumented)
5 | app.get('/__coverage__', (req, res) => {
6 | res.json({
7 | coverage: global.__coverage__ || null
8 | })
9 | })
10 | }
11 |
--------------------------------------------------------------------------------
/examples/no-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/use-browserify-istanbul.js:
--------------------------------------------------------------------------------
1 | const browserify = require('@cypress/browserify-preprocessor')
2 |
3 | const options = browserify.defaultOptions
4 | // transform[1][1] is "babelify"
5 | // so we just add our code instrumentation plugin to the list
6 | options.browserifyOptions.transform[1][1].plugins.push('babel-plugin-istanbul')
7 | module.exports = browserify(options)
8 |
--------------------------------------------------------------------------------
/examples/filter-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/support-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/src/utils/misc.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Very simple check, returns true for arrays as well
3 | */
4 | export function isObject(object) {
5 | return object != null && typeof object === 'object'
6 | }
7 |
8 | /**
9 | * Adds two numbers together
10 | * @param {number} a
11 | * @param {number} b
12 | */
13 | export const add = (a, b) => a + b
14 |
--------------------------------------------------------------------------------
/examples/placeholders/cypress/integration/spec-b.js:
--------------------------------------------------------------------------------
1 | ///
2 | // this spec loads "src/b" module
3 | import { anotherFunction } from '../../src/b.js'
4 | describe('spec-b', () => {
5 | it('exercises src/b.js', () => {
6 | expect(anotherFunction(), 'always returns hello backwards').to.equal(
7 | 'olleh'
8 | )
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/examples/all-files/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('works', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 |
6 | cy.window()
7 | .invoke('reverse', 'super')
8 | .should('equal', 'repus')
9 |
10 | // application's code should be instrumented
11 | cy.window().should('have.property', '__coverage__')
12 | })
13 |
--------------------------------------------------------------------------------
/use-babelrc.js:
--------------------------------------------------------------------------------
1 | const browserify = require('@cypress/browserify-preprocessor')
2 |
3 | // TODO check if there is .babelrc file
4 | // if not, maybe create one?
5 |
6 | // Tells Cypress to use .babelrc when bundling spec code
7 | const options = browserify.defaultOptions
8 | options.browserifyOptions.transform[1][1].babelrc = true
9 | module.exports = browserify(options)
10 |
--------------------------------------------------------------------------------
/examples/ts-example/main.ts:
--------------------------------------------------------------------------------
1 | import { add } from './calc'
2 |
3 | const sub = (a: number, b: number) => {
4 | return a - b
5 | }
6 |
7 | function abs(x: number) {
8 | if (x >= 0) {
9 | return x
10 | } else {
11 | return -x
12 | }
13 | }
14 |
15 | // @ts-ignore
16 | window.add = add
17 | // @ts-ignore
18 | window.sub = sub
19 | // @ts-ignore
20 | window.abs = abs
21 |
--------------------------------------------------------------------------------
/examples/component/cypress/support/component-index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Components App
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | specPattern: 'cypress/integration/spec.js',
8 | supportFile: '../../support.js',
9 | setupNodeEvents(on, config) {
10 | return require('../../plugins')(on, config)
11 | },
12 | },
13 | })
14 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | import { isObject, add } from '../../src/utils/misc'
3 |
4 | describe('unit tests', () => {
5 | it('adds two numbers', () => {
6 | expect(add(2, 3)).to.equal(5)
7 | })
8 |
9 | it('checks for object', () => {
10 | expect(isObject({}), '{}').to.be.true
11 | expect(isObject([]), '[]').to.be.true
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/cypress/integration/spec.ts:
--------------------------------------------------------------------------------
1 | ///
2 | import { isObject, add } from '../../src/utils/misc'
3 |
4 | describe('unit tests', () => {
5 | it('adds two numbers', () => {
6 | expect(add(2, 3)).to.equal(5)
7 | })
8 |
9 | it('checks for object', () => {
10 | expect(isObject({}), '{}').to.be.true
11 | expect(isObject([]), '[]').to.be.true
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/examples/one-spec/cypress/integration/spec-a.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('spec a', () => {
3 | cy.visit('index.html')
4 | cy.contains('Page body')
5 |
6 | cy.window().invoke('add', 2, 3).should('equal', 5)
7 |
8 | cy.window().invoke('sub', 2, 3).should('equal', -1)
9 | })
10 |
11 | it('goes to another origin', () => {
12 | cy.visit('index.html')
13 | cy.get('a[title="another site"]').click()
14 | })
15 |
--------------------------------------------------------------------------------
/examples/ts-example/README.md:
--------------------------------------------------------------------------------
1 | # example: ts-example
2 |
3 | Code coverage for TypeScript code. See [nyc TS support](https://github.com/istanbuljs/nyc#typescript-projects) docs too.
4 |
5 | Code is instrumented on the fly using [.babelrc](.babelrc) and Parcel to run it.
6 |
7 | ## Use
8 |
9 | - start the server and Cypress with `npm run dev`
10 | - run the Cypress tests
11 | - look at the generated reports in folder `coverage`
12 |
--------------------------------------------------------------------------------
/examples/filter-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-filter-files",
3 | "description": "Filtering out files from the code coverage",
4 | "devDependencies": {
5 | "@babel/core": "7.9.0"
6 | },
7 | "scripts": {
8 | "start": "../../node_modules/.bin/parcel serve index.html",
9 | "cy:open": "../../node_modules/.bin/cypress open",
10 | "dev": "../../node_modules/.bin/start-test 1234 cy:open"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/docker-paths/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-docker-paths",
3 | "private": true,
4 | "version": "1.0.0",
5 | "description": "Instrumented files in Docker container are found locally",
6 | "main": "index.js",
7 | "scripts": {
8 | "cy:open": "../../node_modules/.bin/cypress open",
9 | "cy:run": "../../node_modules/.bin/cypress run"
10 | },
11 | "keywords": [],
12 | "author": "",
13 | "license": "ISC"
14 | }
15 |
--------------------------------------------------------------------------------
/middleware/hapi.js:
--------------------------------------------------------------------------------
1 | // for Hapi.js
2 | module.exports = server => {
3 | // expose "GET __coverage__" endpoint that just returns
4 | // global coverage information (if the application has been instrumented)
5 |
6 | // https://hapijs.com/tutorials/routing?lang=en_US
7 | server.route({
8 | method: 'GET',
9 | path: '/__coverage__',
10 | handler () {
11 | return { coverage: global.__coverage__ }
12 | }
13 | })
14 | }
15 |
--------------------------------------------------------------------------------
/examples/before-each-visit/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('coverage information', () => {
3 | beforeEach(() => {
4 | cy.visit('index.html')
5 | })
6 |
7 | it('calls add', () => {
8 | cy.window()
9 | .invoke('add', 2, 3)
10 | .should('equal', 5)
11 | })
12 |
13 | it('calls sub', () => {
14 | cy.window()
15 | .invoke('sub', 2, 3)
16 | .should('equal', -1)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('coverage information', () => {
3 | beforeEach(() => {
4 | cy.visit('index.html')
5 | })
6 |
7 | it('calls add', () => {
8 | cy.window()
9 | .invoke('add', 2, 3)
10 | .should('equal', 5)
11 | })
12 |
13 | it('calls sub', () => {
14 | cy.window()
15 | .invoke('sub', 2, 3)
16 | .should('equal', -1)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/examples/before-all-visit/README.md:
--------------------------------------------------------------------------------
1 | # example: before-all-visit
2 |
3 | Code coverage example where the `cy.visit` happens in `before` hook
4 |
5 | Code was instrumented with
6 |
7 | ```shell
8 | npx nyc instrument --compact false main.js > main-instrumented.js
9 | ```
10 |
11 | and then removed absolute folder paths, leaving just relative path `main.js` in the produced file.
12 |
13 | The code uses custom coverage report command in [package.json](package.json) to call `nyc`
14 |
--------------------------------------------------------------------------------
/examples/use-webpack/src/index.js:
--------------------------------------------------------------------------------
1 | import { reverse } from './calc'
2 |
3 | if (window.Cypress) {
4 | require('console-log-div')
5 | console.log('attaching event listeners')
6 | }
7 |
8 | document.getElementById('user-input').addEventListener('change', e => {
9 | const s = e.target.value
10 | console.log(`input string "${s}"`)
11 | const reversed = reverse(s)
12 | document.getElementById('reversed').innerText = reversed
13 | })
14 | console.log('added event listener')
15 |
--------------------------------------------------------------------------------
/examples/before-each-visit/README.md:
--------------------------------------------------------------------------------
1 | # example: before-each-visit
2 |
3 | Code coverage example where the `cy.visit` happens in `beforeEach` hook
4 |
5 | Code was instrumented with
6 |
7 | ```shell
8 | npx nyc instrument --compact false main.js > main-instrumented.js
9 | ```
10 |
11 | and then removed absolute folder paths, leaving just relative path `main.js` in the produced file.
12 |
13 | The code uses custom coverage report command in [package.json](package.json) to call `nyc`
14 |
--------------------------------------------------------------------------------
/examples/merge-coverage/src/math.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Adds two numbers together
3 | * @param {number} a
4 | * @param {number} b
5 | */
6 | export const add = (a, b) => {
7 | console.log('add %d to %d', a, b)
8 | if (a === 2) {
9 | console.log('a is 2')
10 | }
11 | return a + b
12 | }
13 |
14 | /**
15 | * subtracts numbers
16 | * @param {number} a
17 | * @param {number} b
18 | */
19 | export const sub = (a, b) => {
20 | console.log('sub %d to %d', a, b)
21 | return a - b
22 | }
23 |
--------------------------------------------------------------------------------
/examples/all-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/before-all-visit/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | specPattern: 'cypress/integration/*.js',
6 | coverage: {
7 | exclude: true,
8 | },
9 | // We've imported your old cypress plugins here.
10 | // You may want to clean this up later by importing these.
11 | setupNodeEvents(on, config) {
12 | return require('./cypress/plugins/index.js')(on, config)
13 | },
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/examples/before-each-visit/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | specPattern: 'cypress/integration/*.js',
6 | coverage: {
7 | exclude: true,
8 | },
9 | // We've imported your old cypress plugins here.
10 | // You may want to clean this up later by importing these.
11 | setupNodeEvents(on, config) {
12 | return require('./cypress/plugins/index.js')(on, config)
13 | },
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/examples/docker-paths/app/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/exclude-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
17 |
18 |
--------------------------------------------------------------------------------
/examples/one-spec/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-one-spec",
3 | "description": "Only running a single spec",
4 | "scripts": {
5 | "cy:open": "../../node_modules/.bin/cypress open",
6 | "cy:run": "../../node_modules/.bin/cypress run",
7 | "check:covered": "../../node_modules/.bin/check-coverage main.js",
8 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json main.js",
9 | "check": "npm run check:covered && npm run check:extras"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/use-webpack/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | import { add } from '../../src/calc'
3 |
4 | describe('Webpack example', () => {
5 | it('loads', () => {
6 | cy.visit('/')
7 | cy.contains('Webpack page').should('be.visible')
8 | cy.get('#user-input').type('Hello{enter}')
9 | cy.contains('olleH').should('be.visible')
10 | })
11 |
12 | it('has add function', () => {
13 | // test "add" via this unit test
14 | expect(add(2, 3)).to.equal(5)
15 | })
16 | })
17 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-unit-tests-js",
3 | "description": "Run unit tests written using JavaScript",
4 | "scripts": {
5 | "cy:open": "../../node_modules/.bin/cypress open",
6 | "cy:run": "../../node_modules/.bin/cypress run",
7 | "check:covered": "../../node_modules/.bin/check-coverage misc.js",
8 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json misc.js",
9 | "check": "npm run check:covered && npm run check:extras"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-unit-tests-ts",
3 | "description": "Run unit tests written using TypeScript",
4 | "scripts": {
5 | "cy:open": "../../node_modules/.bin/cypress open",
6 | "cy:run": "../../node_modules/.bin/cypress run",
7 | "check:covered": "../../node_modules/.bin/check-coverage misc.ts",
8 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json misc.ts",
9 | "check": "npm run check:covered && npm run check:extras"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/cypress/e2e/minimatch.cy.js:
--------------------------------------------------------------------------------
1 | describe('minimatch', () => {
2 | it('string matches', () => {
3 | expect(
4 | Cypress.minimatch('/path/to/specA.js', '/path/to/specA.js'),
5 | 'matches full strings',
6 | ).to.be.true
7 |
8 | expect(
9 | Cypress.minimatch('/path/to/specA.js', 'specA.js'),
10 | 'does not match just the end',
11 | ).to.be.false
12 |
13 | expect(
14 | Cypress.minimatch('/path/to/specA.js', '**/specA.js'),
15 | 'matches using **',
16 | ).to.be.true
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/examples/ts-example/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | baseUrl: 'http://localhost:1234',
7 | video: false,
8 | specPattern: 'cypress/integration/spec.js',
9 | // We've imported your old cypress plugins here.
10 | // You may want to clean this up later by importing these.
11 | setupNodeEvents(on, config) {
12 | return require('./cypress/plugins/index.js')(on, config)
13 | },
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/examples/placeholders/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | specPattern: 'cypress/integration/*.js',
8 | supportFile: 'cypress/support/index.js',
9 | // We've imported your old cypress plugins here.
10 | // You may want to clean this up later by importing these.
11 | setupNodeEvents(on, config) {
12 | return require('./cypress/plugins/index.js')(on, config)
13 | },
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/examples/merge-coverage/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | env: {
6 | coverage: {
7 | exclude: '**/cypress/**',
8 | },
9 | },
10 | fixturesFolder: false,
11 | video: false,
12 | // We've imported your old cypress plugins here.
13 | // You may want to clean this up later by importing these.
14 | setupNodeEvents(on, config) {
15 | return require('./cypress/plugins/index.js')(on, config)
16 | },
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/examples/before-all-visit/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe(
3 | 'coverage information',
4 | { testIsolation: false, viewportHeight: 100, viewportWidth: 100 },
5 | () => {
6 | before(() => {
7 | cy.log('visiting index.html')
8 | cy.visit('index.html')
9 | })
10 |
11 | it('calls add', () => {
12 | cy.window().invoke('add', 2, 3).should('equal', 5)
13 | })
14 |
15 | it('calls sub', () => {
16 | cy.window().invoke('sub', 2, 3).should('equal', -1)
17 | })
18 | },
19 | )
20 |
--------------------------------------------------------------------------------
/examples/use-webpack/README.md:
--------------------------------------------------------------------------------
1 | # use-webpack
2 |
3 | > Instruments the built bundle using Webpack
4 |
5 | Webpack uses [webpack.config.js](webpack.config.js) to build the bundle from [src/index.js](src/index.js) into `dist/main.js`, loaded from [dist/index.html](dist/index.html). The [cypress/integration/spec.js](cypress/integration/spec.js) also uses one of the functions from [src/calc.js](src/calc.js) directly. The final coverage includes both E2E and unit test coverage information.
6 |
7 | **Note:** this project requires `npm run build` before running `npm run dev`.
8 |
--------------------------------------------------------------------------------
/examples/exclude-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | baseUrl: 'http://localhost:1234',
6 | specPattern: 'cypress/integration/*.js',
7 | env: {
8 | coverage: {
9 | exclude: true,
10 | },
11 | },
12 | // We've imported your old cypress plugins here.
13 | // You may want to clean this up later by importing these.
14 | setupNodeEvents(on, config) {
15 | return require('./cypress/plugins/index.js')(on, config)
16 | },
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/examples/multiple-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | baseUrl: 'http://localhost:1234',
6 | specPattern: 'cypress/integration/*.js',
7 | env: {
8 | coverage: {
9 | exclude: false,
10 | },
11 | },
12 | // We've imported your old cypress plugins here.
13 | // You may want to clean this up later by importing these.
14 | setupNodeEvents(on, config) {
15 | return require('./cypress/plugins/index.js')(on, config)
16 | },
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "use-plugins-and-support",
3 | "description": "Using included plugins and support files",
4 | "devDependencies": {},
5 | "scripts": {
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "cy:run": "../../node_modules/.bin/cypress run",
8 | "check:covered": "../../node_modules/.bin/check-coverage main.js",
9 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json main.js",
10 | "check": "npm run check:covered && npm run check:extras"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/one-spec/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | specPattern: 'cypress/integration/spec-a.js',
8 | env: {
9 | coverage: {
10 | exclude: true,
11 | },
12 | },
13 | // We've imported your old cypress plugins here.
14 | // You may want to clean this up later by importing these.
15 | setupNodeEvents(on, config) {
16 | return require('./cypress/plugins/index.js')(on, config)
17 | },
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/examples/before-all-visit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-before-all-visit",
3 | "description": "Getting code coverage when cy.visit is used in before hook",
4 | "devDependencies": {},
5 | "scripts": {
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "coverage:report using npx": "npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --reporter=clover --reporter=json",
8 | "coverage:report using bin-up": "bin-up nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --reporter=clover --reporter=json"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/unit-tests-js/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | specPattern: 'cypress/integration/spec.js',
8 | env: {
9 | coverage: {
10 | exclude: true,
11 | },
12 | },
13 | // We've imported your old cypress plugins here.
14 | // You may want to clean this up later by importing these.
15 | setupNodeEvents(on, config) {
16 | return require('./cypress/plugins/index.js')(on, config)
17 | },
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | specPattern: 'cypress/integration/spec.ts',
8 | env: {
9 | coverage: {
10 | exclude: true,
11 | },
12 | },
13 | // We've imported your old cypress plugins here.
14 | // You may want to clean this up later by importing these.
15 | setupNodeEvents(on, config) {
16 | return require('./cypress/plugins/index.js')(on, config)
17 | },
18 | },
19 | })
20 |
--------------------------------------------------------------------------------
/examples/before-each-visit/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-before-each-visit",
3 | "description": "Getting code coverage when cy.visit is used in beforeEach hook",
4 | "devDependencies": {},
5 | "scripts": {
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "coverage:report using npx": "npx nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --reporter=clover --reporter=json",
8 | "coverage:report using bin-up": "bin-up nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov --reporter=clover --reporter=json"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/examples/same-folder/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-same-folder",
3 | "description": "Check if test files are correctly filtered out",
4 | "devDependencies": {},
5 | "scripts": {
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "cy:run": "../../node_modules/.bin/cypress run",
8 | "check:covered": "../../node_modules/.bin/check-coverage main.js unit-utils.js",
9 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json main.js unit-utils.js",
10 | "check": "npm run check:covered && npm run check:extras"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/examples/same-folder/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | specPattern: '**/spec.js',
8 | supportFile: 'support/support.js',
9 | env: {
10 | coverage: {
11 | exclude: true,
12 | },
13 | },
14 | // We've imported your old cypress plugins here.
15 | // You may want to clean this up later by importing these.
16 | setupNodeEvents(on, config) {
17 | return require('./plugins.js')(on, config)
18 | },
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/cypress/fixtures/coverage.json:
--------------------------------------------------------------------------------
1 | {
2 | "/src/index.js": {
3 | "path": "/src/index.js",
4 | "statementMap": {
5 | "0": {
6 | "start": {
7 | "line": 7,
8 | "column": 0
9 | },
10 | "end": {
11 | "line": 14,
12 | "column": 2
13 | }
14 | }
15 | },
16 | "fnMap": {},
17 | "branchMap": {},
18 | "s": {
19 | "0": 1
20 | },
21 | "f": {},
22 | "b": {},
23 | "_coverageSchema": "1a1c01bbd47fc00a2c39e90264f33305004495a9",
24 | "hash": "46f2efd10038593ec768c6cc815a6d6ee2924243"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/examples/same-folder/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { reverse } from './unit-utils'
4 |
5 | describe('coverage information', { testIsolation: false }, () => {
6 | before(() => {
7 | cy.log('visiting index.html')
8 | cy.visit('index.html')
9 | })
10 |
11 | it('calls add', () => {
12 | cy.window().invoke('add', 2, 3).should('equal', 5)
13 | })
14 |
15 | it('calls sub', () => {
16 | cy.window().invoke('sub', 2, 3).should('equal', -1)
17 | })
18 |
19 | it('reverses a string', () => {
20 | expect(reverse('Hello')).to.equal('olleH')
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/examples/no-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | video: false,
7 | baseUrl: 'http://localhost:1234',
8 | specPattern: 'cypress/integration/*.js',
9 | env: {
10 | coverage: {
11 | exclude: true,
12 | },
13 | },
14 | // We've imported your old cypress plugins here.
15 | // You may want to clean this up later by importing these.
16 | setupNodeEvents(on, config) {
17 | return require('./cypress/plugins/index.js')(on, config)
18 | },
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/examples/use-webpack/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | ///
2 | const webpack = require('@cypress/webpack-preprocessor')
3 |
4 | /**
5 | * @type {Cypress.PluginConfig}
6 | */
7 | module.exports = (on, config) => {
8 | const options = {
9 | // use the same Webpack options to bundle spec files as your app does "normally"
10 | // which should instrument the spec files in this project
11 | webpackOptions: require('../../webpack.config'),
12 | watchOptions: {}
13 | }
14 | on('file:preprocessor', webpack(options))
15 |
16 | require('../../../../task')(on, config)
17 | return config
18 | }
19 |
--------------------------------------------------------------------------------
/examples/use-webpack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-use-webpack",
3 | "version": "1.0.0",
4 | "description": "Code coverage from webpack",
5 | "private": true,
6 | "scripts": {
7 | "cy:open": "../../node_modules/.bin/cypress open",
8 | "cy:run": "../../node_modules/.bin/cypress run",
9 | "dev": "../../node_modules/.bin/start-test 5000 cy:open",
10 | "build": "../../node_modules/.bin/webpack",
11 | "start": "../../node_modules/.bin/serve dist",
12 | "test:ci": "../../node_modules/.bin/start-test 5000 cy:run"
13 | },
14 | "keywords": [],
15 | "author": "",
16 | "license": "ISC"
17 | }
18 |
--------------------------------------------------------------------------------
/examples/no-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-filter-files",
3 | "description": "Filtering out files from the code coverage",
4 | "devDependencies": {
5 | "@babel/core": "7.9.0"
6 | },
7 | "scripts": {
8 | "start": "../../node_modules/.bin/parcel serve index.html",
9 | "cy:open": "../../node_modules/.bin/cypress open",
10 | "cy:run": "../../node_modules/.bin/cypress run",
11 | "dev": "../../node_modules/.bin/start-test 1234 cy:open",
12 | "clean": "rm -rf .nyc_output coverage",
13 | "e2e": "../../node_modules/.bin/start-test 1234 cy:run",
14 | "pree2e": "npm run clean"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/examples/support-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | baseUrl: 'http://localhost:1234',
7 | video: false,
8 | specPattern: 'cypress/integration/spec.js',
9 | env: {
10 | coverage: {
11 | exclude: true,
12 | },
13 | },
14 | // We've imported your old cypress plugins here.
15 | // You may want to clean this up later by importing these.
16 | setupNodeEvents(on, config) {
17 | return require('./cypress/plugins/index.js')(on, config)
18 | },
19 | },
20 | })
21 |
--------------------------------------------------------------------------------
/examples/fullstack/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | // load extra files to instrument on the fly
4 | const { reverse } = require('../../string-utils')
5 |
6 | it('uses frontend code and calls backend', () => {
7 | cy.visit('/')
8 | cy.contains('Page body').should('be.visible')
9 |
10 | cy.window()
11 | .invoke('add', 2, 3)
12 | .should('equal', 5)
13 |
14 | cy.window()
15 | .invoke('sub', 2, 3)
16 | .should('equal', -1)
17 |
18 | cy.log('**backend request**')
19 | cy.request('/hello')
20 |
21 | cy.log('**unit test**')
22 | expect(reverse('Hello')).to.equal('olleH')
23 | })
24 |
--------------------------------------------------------------------------------
/examples/ts-example/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('ts-example', () => {
3 | beforeEach(() => {
4 | cy.visit('/')
5 | })
6 |
7 | it('calls add', () => {
8 | cy.window()
9 | .invoke('add', 2, 3)
10 | .should('equal', 5)
11 | })
12 |
13 | it('calls sub', () => {
14 | cy.window()
15 | .invoke('sub', 2, 3)
16 | .should('equal', -1)
17 | })
18 |
19 | it('calls abs twice', () => {
20 | cy.window()
21 | .invoke('abs', 2)
22 | .should('equal', 2)
23 |
24 | cy.window()
25 | .invoke('abs', -5)
26 | .should('equal', 5)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('../../task')(on, config)
3 |
4 | // also use .babelrc file when bundling spec files
5 | // to get the code coverage from unit tests
6 | // https://glebbahmutov.com/blog/combined-end-to-end-and-unit-test-coverage/
7 | on('file:preprocessor', require('../../use-babelrc'))
8 |
9 | // or use browserify and just push babel-plugin-istanbul
10 | // directory to the list of babelify plugins
11 | // on('file:preprocessor', require('../../use-browserify-istanbul'))
12 |
13 | // IMPORTANT to return the config object with changed environment variable
14 | return config
15 | }
16 |
--------------------------------------------------------------------------------
/examples/all-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | viewportHeight: 200,
5 | viewportWidth: 200,
6 | e2e: {
7 | fixturesFolder: false,
8 | baseUrl: 'http://localhost:1234',
9 | specPattern: 'cypress/integration/*.js',
10 | env: {
11 | coverage: {
12 | exclude: true,
13 | },
14 | },
15 | // We've imported your old cypress plugins here.
16 | // You may want to clean this up later by importing these.
17 | setupNodeEvents(on, config) {
18 | return require('./cypress/plugins/index.js')(on, config)
19 | },
20 | },
21 | })
22 |
--------------------------------------------------------------------------------
/examples/exclude-files/cypress/integration/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('works', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 |
6 | cy.window().invoke('reverse', 'super').should('equal', 'repus')
7 | })
8 |
9 | it('uses minimatch', () => {
10 | expect(Cypress.minimatch('/path/to/file.js', 'to/*.js'), 'relative').to.be
11 | .false
12 | expect(Cypress.minimatch('/path/to/file.js', '/path/to/*.js'), 'absolute').to
13 | .be.true
14 | expect(Cypress.minimatch('/path/to/file.js', '**/to/*.js'), '** prefix').to.be
15 | .true
16 | cy.wrap(Cypress)
17 | .invoke({ timeout: 0 }, 'minimatch', '/path/to/file.js', '**/to/*.js')
18 | .should('be.true')
19 | })
20 |
--------------------------------------------------------------------------------
/examples/fullstack/README.md:
--------------------------------------------------------------------------------
1 | # example: fullstack
2 |
3 | > Combined code coverage from the backend code, and e2e and unit tests
4 |
5 | This example runs instrumented server code, that serves instrumented frontend code, and instruments the unit tests on the fly. The final report combines all 3 sources of information.
6 |
7 | To run
8 |
9 | ```sh
10 | $ npm run dev
11 | ```
12 |
13 | You should see messages from the plugin when it saves each coverage object
14 |
15 | 
16 |
17 | In the produced report, you should see
18 |
19 | - `server/server.js` coverage for backend
20 | - `main.js` coverage from end-to-end tests
21 | - `string-utils.js` coverage from unit tests
22 |
--------------------------------------------------------------------------------
/examples/backend/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const app = express()
3 | const port = 3003
4 |
5 | // if there is code coverage information
6 | // then expose an endpoint that returns it
7 | /* istanbul ignore next */
8 | if (global.__coverage__) {
9 | console.log('have code coverage, will add middleware for express')
10 | console.log(`to fetch: GET :${port}/__coverage__`)
11 | require('../../../middleware/express')(app)
12 | }
13 |
14 | app.use(express.static(__dirname))
15 |
16 | app.get('/hello', (req, res) => {
17 | console.log('sending hello world')
18 | res.send('Hello World!')
19 | })
20 |
21 | app.listen(port, () => console.log(`Example app listening on port ${port}!`))
22 |
--------------------------------------------------------------------------------
/examples/fullstack/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const app = express()
3 | const port = 3003
4 |
5 | // if there is code coverage information
6 | // then expose an endpoint that returns it
7 | /* istanbul ignore next */
8 | if (global.__coverage__) {
9 | console.log('have code coverage, will add middleware for express')
10 | console.log(`to fetch: GET :${port}/__coverage__`)
11 | require('../../../middleware/express')(app)
12 | }
13 |
14 | app.use(express.static(__dirname))
15 |
16 | app.get('/hello', (req, res) => {
17 | console.log('sending hello world')
18 | res.send('Hello World!')
19 | })
20 |
21 | app.listen(port, () => console.log(`Example app listening on port ${port}!`))
22 |
--------------------------------------------------------------------------------
/examples/fullstack/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | baseUrl: 'http://localhost:3003',
7 | env: {
8 | codeCoverage: {
9 | url: 'http://localhost:3003/__coverage__',
10 | },
11 | coverage: {
12 | exclude: true,
13 | },
14 | },
15 | video: false,
16 | specPattern: 'cypress/integration/spec.js',
17 | // We've imported your old cypress plugins here.
18 | // You may want to clean this up later by importing these.
19 | setupNodeEvents(on, config) {
20 | return require('./cypress/plugins/index.js')(on, config)
21 | },
22 | },
23 | })
24 |
--------------------------------------------------------------------------------
/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress'
2 |
3 | export default defineConfig({
4 | viewportHeight: 200,
5 | viewportWidth: 200,
6 | e2e: {
7 | env: {
8 | coverage: {
9 | // set to true to hide the messages in the Command Log
10 | quiet: false,
11 | // intercept and instrument scripts matching these URLs
12 | instrument: '**/calculator/**/*.js',
13 | },
14 | },
15 | // We've imported your old cypress plugins here.
16 | // You may want to clean this up later by importing these.
17 | setupNodeEvents(on, config) {
18 | return require('./cypress/plugins/index.js')(on, config)
19 | },
20 | baseUrl: 'http://localhost:1234',
21 | },
22 | })
23 |
--------------------------------------------------------------------------------
/examples/all-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-all-files",
3 | "description": "Report all files",
4 | "private": true,
5 | "scripts": {
6 | "start": "../../node_modules/.bin/parcel serve index.html",
7 | "start:windows": "npx bin-up parcel serve index.html",
8 | "cy:open": "../../node_modules/.bin/cypress open",
9 | "cy:run": "../../node_modules/.bin/cypress run",
10 | "dev": "../../node_modules/.bin/start-test 1234 cy:open",
11 | "e2e": "../../node_modules/.bin/start-test 1234 cy:run",
12 | "report": "../../node_modules/.bin/nyc report"
13 | },
14 | "nyc": {
15 | "all": true,
16 | "include": "*.js"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "7.9.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/examples/backend/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | fixturesFolder: false,
6 | baseUrl: 'http://localhost:3003',
7 | env: {
8 | codeCoverage: {
9 | url: 'http://localhost:3003/__coverage__',
10 | expectBackendCoverageOnly: true,
11 | },
12 | coverage: {
13 | exclude: true,
14 | },
15 | },
16 | video: false,
17 | specPattern: 'cypress/integration/spec.js',
18 | // We've imported your old cypress plugins here.
19 | // You may want to clean this up later by importing these.
20 | setupNodeEvents(on, config) {
21 | return require('./cypress/plugins/index.js')(on, config)
22 | },
23 | },
24 | })
25 |
--------------------------------------------------------------------------------
/examples/support-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-support-files",
3 | "description": "Filtering out support files",
4 | "devDependencies": {
5 | "@babel/core": "7.9.0"
6 | },
7 | "scripts": {
8 | "start": "../../node_modules/.bin/parcel serve index.html",
9 | "cy:open": "../../node_modules/.bin/cypress open",
10 | "cy:run": "../../node_modules/.bin/cypress run",
11 | "dev": "../../node_modules/.bin/start-test 1234 cy:open",
12 | "e2e": "../../node_modules/.bin/start-test 1234 cy:run",
13 | "check:covered": "../../node_modules/.bin/check-coverage main.js",
14 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json main.js",
15 | "check": "npm run check:covered && npm run check:extras"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-backend",
3 | "description": "Code coverage for backend",
4 | "devDependencies": {},
5 | "private": true,
6 | "scripts": {
7 | "start": "../../node_modules/.bin/nyc --silent node server/server",
8 | "cy:open": "../../node_modules/.bin/cypress open",
9 | "cy:run": "../../node_modules/.bin/cypress run",
10 | "dev": "../../node_modules/.bin/start-test 3003 cy:open",
11 | "e2e": "../../node_modules/.bin/start-test 3003 cy:run",
12 | "coverage:report": "../../node_modules/.bin/nyc report",
13 | "check:covered": "../../node_modules/.bin/check-coverage server.js",
14 | "check:extras": "../../node_modules/.bin/only-covered server.js",
15 | "check": "npm run check:covered && npm run check:extras"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/examples/use-webpack/webpack.config.js:
--------------------------------------------------------------------------------
1 | const path = require('path')
2 |
3 | // https://webpack.js.org/guides/development/
4 | module.exports = {
5 | entry: './src/index.js',
6 | mode: 'development',
7 | devtool: 'inline-source-map',
8 | output: {
9 | filename: 'main.js',
10 | path: path.resolve(__dirname, 'dist')
11 | },
12 | module: {
13 | rules: [
14 | {
15 | // when bundling application's own source code
16 | // transpile using Babel which uses .babelrc file
17 | // and instruments code using babel-plugin-istanbul
18 | test: /\.js/,
19 | exclude: /(node_modules|bower_components)/,
20 | use: [
21 | {
22 | loader: 'babel-loader'
23 | }
24 | ]
25 | }
26 | ]
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/cypress/e2e/calculator.cy.js:
--------------------------------------------------------------------------------
1 | import { CalculatorPage } from './calculator-po'
2 |
3 | it(
4 | 'collects code coverage on the fly',
5 | { viewportWidth: 1200, viewportHeight: 1000, baseUrl: null },
6 | () => {
7 | cy.visit('cypress/calculator/index.html')
8 | // after instrumenting the coverage should be collected in the window object
9 | // under window.__coverage__ key
10 | // There should be two keys: one for the main script and one for the spec
11 | cy.window()
12 | .should('have.property', '__coverage__')
13 | .should('have.keys', [
14 | 'cypress/calculator/app.js',
15 | 'cypress/calculator/utils.js',
16 | ])
17 |
18 | // compute an expression and see the increased code coverage
19 | CalculatorPage.compute('1+2.1', '3.1')
20 | },
21 | )
22 |
--------------------------------------------------------------------------------
/cypress/calculator/utils.js:
--------------------------------------------------------------------------------
1 | // pure functions used during computation
2 |
3 | export function appendDot(expression) {
4 | // check if we are trying a number right now
5 | // this will split expressions like
6 | // "1+2.3" into ["1", "2.3"]
7 | const parts = expression.split(/[\+\-\*\/]/)
8 | // the last part is the current number
9 | // the user is appending to
10 | const last = parts[parts.length - 1]
11 | //
12 | // if the last part already has a dot, do nothing
13 | if (last.includes('.')) {
14 | return expression
15 | }
16 | // if the last part is empty, add a zero before the dot
17 | // this will cover starting to type a number with a dot
18 | // or typing a dot after an expression like "1+"
19 |
20 | if (last === '') {
21 | return expression + '0.'
22 | }
23 | // otherwise, add the dot and return the expression
24 | return expression + '.'
25 | }
26 |
--------------------------------------------------------------------------------
/examples/fullstack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-fullstack",
3 | "description": "Combined code coverage from the backend code, and e2e and unit tests",
4 | "devDependencies": {},
5 | "scripts": {
6 | "start": "../../node_modules/.bin/nyc --silent node server/server",
7 | "cy:open": "../../node_modules/.bin/cypress open",
8 | "cy:run": "../../node_modules/.bin/cypress run",
9 | "dev": "../../node_modules/.bin/start-test 3003 cy:open",
10 | "e2e": "../../node_modules/.bin/start-test 3003 cy:run",
11 | "report": "../../node_modules/.bin/nyc report --reporter text",
12 | "check:covered": "../../node_modules/.bin/check-coverage server.js main.js string-utils.js",
13 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json server.js main.js string-utils.js",
14 | "check": "npm run check:covered && npm run check:extras"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/middleware/nextjs.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Middleware for returning server-side code coverage
3 | * for Next.js API route. To use, create new `pages/api/coverage.js` file
4 | * and re-export this default middleware function.
5 | *
6 | ```
7 | // in your pages/api/coverage.js
8 | module.exports = require('@cypress/code-coverage/middleware/nextjs')
9 | // then add to your cypress.json an environment variable pointing at the API
10 | {
11 | "baseUrl": "http://localhost:3000",
12 | "env": {
13 | "codeCoverage": {
14 | "url": "/api/coverage"
15 | }
16 | }
17 | }
18 | ```
19 | *
20 | * @see https://nextjs.org/docs#api-routes
21 | * @see https://github.com/cypress-io/code-coverage
22 | */
23 | module.exports = function returnCodeCoverageNext (req, res) {
24 | // only GET is supported
25 | res.status(200).json({
26 | coverage: global.__coverage__ || null
27 | })
28 | }
29 |
--------------------------------------------------------------------------------
/examples/exclude-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-exclude-files",
3 | "description": "Exclude some files from final coverage report",
4 | "scripts": {
5 | "start": "../../node_modules/.bin/parcel serve index.html",
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "cy:run": "../../node_modules/.bin/cypress run",
8 | "dev": "../../node_modules/.bin/start-test 1234 cy:open",
9 | "e2e": "../../node_modules/.bin/start-test 1234 cy:run",
10 | "check:covered": "../../node_modules/.bin/check-coverage main.js",
11 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json main.js",
12 | "check": "npm run check:covered && npm run check:extras"
13 | },
14 | "nyc": {
15 | "exclude": [
16 | "second.js"
17 | ],
18 | "excludeAfterRemap": true
19 | },
20 | "devDependencies": {
21 | "@babel/core": "7.9.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/merge-coverage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-merge-coverage",
3 | "description": "Merge partial code coverage reports",
4 | "scripts": {
5 | "cy:open": "../../node_modules/.bin/cypress open",
6 | "cy:run1": "npm run clean && rm -rf cc1 && ../../node_modules/.bin/cypress run --spec cypress/e2e/add.cy.js && mv coverage cc1",
7 | "cy:run2": "npm run clean && rm -rf cc2 && ../../node_modules/.bin/cypress run --spec cypress/e2e/sub.cy.js && mv coverage cc2",
8 | "check:covered": "../../node_modules/.bin/check-coverage --from coverage/coverage-final.json math.js",
9 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json math.js",
10 | "check": "npm run check:covered && npm run check:extras",
11 | "clean": "rm -rf .nyc_output && rm -rf coverage",
12 | "merge": "DEBUG=code-coverage ../../bin/cc-merge.js .",
13 | "report": "../../node_modules/.bin/nyc report"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/examples/placeholders/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-placeholders",
3 | "description": "Fills placeholder values",
4 | "private": true,
5 | "scripts": {
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "cy:run": "../../node_modules/.bin/cypress run",
8 | "report": "../../node_modules/.bin/nyc report",
9 | "clean": "rm -rf .nyc_output || true",
10 | "test:a": "../../node_modules/.bin/cypress run --spec cypress/integration/spec-a.js",
11 | "test:b": "../../node_modules/.bin/cypress run --spec cypress/integration/spec-b.js",
12 | "check:covered": "../../node_modules/.bin/check-coverage src/a.js src/b.js",
13 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json src/a.js src/b.js",
14 | "check": "npm run check:covered && npm run check:extras"
15 | },
16 | "nyc": {
17 | "all": true,
18 | "include": "src/*.js"
19 | },
20 | "devDependencies": {
21 | "@babel/core": "7.9.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/examples/multiple-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-multiple-files",
3 | "description": "Multiple specs",
4 | "scripts": {
5 | "start": "../../node_modules/.bin/parcel serve index.html",
6 | "cy:open": "../../node_modules/.bin/cypress open",
7 | "cy:run": "../../node_modules/.bin/cypress run",
8 | "dev": "../../node_modules/.bin/start-test 1234 cy:open",
9 | "e2e": "../../node_modules/.bin/start-test 1234 cy:run",
10 | "report": "../../node_modules/.bin/nyc report --text",
11 | "check:covered": "../../node_modules/.bin/check-coverage first.js second.js third.js",
12 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json first.js second.js third.js",
13 | "check": "npm run check:covered && npm run check:extras"
14 | },
15 | "nyc": {
16 | "all": true,
17 | "include": [
18 | "src"
19 | ],
20 | "excludeAfterRemap": true
21 | },
22 | "devDependencies": {
23 | "@babel/core": "7.9.0"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/.github/workflows/badges.yml:
--------------------------------------------------------------------------------
1 | name: badges
2 | on:
3 | push:
4 | # update README badge only if the README file changes
5 | # or if the package.json file changes, or this file changes
6 | branches:
7 | - main
8 | paths:
9 | - README.md
10 | - package.json
11 | - .github/workflows/badges.yml
12 | schedule:
13 | # update badges every night
14 | # because we have a few badges that are linked
15 | # to the external repositories
16 | - cron: '0 3 * * *'
17 |
18 | jobs:
19 | badges:
20 | name: Badges
21 | runs-on: ubuntu-20.04
22 | steps:
23 | - name: Checkout 🛎
24 | uses: actions/checkout@v4
25 |
26 | - name: Update version badges 🏷
27 | run: npx -p dependency-version-badge update-badge cypress
28 |
29 | - name: Commit any changed files 💾
30 | uses: stefanzweifel/git-auto-commit-action@v4
31 | with:
32 | commit_message: Updated badges
33 | branch: main
34 | file_pattern: README.md
35 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## MIT License
2 |
3 | Copyright (c) 2019 Cypress.io https://www.cypress.io, 2022 Gleb Bahmutov
4 |
5 | Permission is hereby granted, free of charge, to any person
6 | obtaining a copy of this software and associated documentation
7 | files (the "Software"), to deal in the Software without
8 | restriction, including without limitation the rights to use,
9 | copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the
11 | Software is furnished to do so, subject to the following
12 | conditions:
13 |
14 | The above copyright notice and this permission notice shall be
15 | included in all copies or substantial portions of the Software.
16 |
17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 | OTHER DEALINGS IN THE SOFTWARE.
25 |
--------------------------------------------------------------------------------
/examples/component/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-component-spec",
3 | "description": "Only running a single component spec",
4 | "scripts": {
5 | "postinstall": "mkdir -p node_modules/code-coverage && cp cc.json node_modules/code-coverage/package.json",
6 | "cy:open": "../../node_modules/.bin/cypress open --component",
7 | "cy:run": "../../node_modules/.bin/cypress run --component",
8 | "check:covered": "../../node_modules/.bin/check-coverage Hello.jsx calc.js",
9 | "check:extras": "../../node_modules/.bin/only-covered Hello.jsx calc.js",
10 | "check": "npm run check:covered && npm run check:extras",
11 | "coverage:report": "../../node_modules/.bin/nyc report",
12 | "clean": "rm -rf .nyc_output coverage"
13 | },
14 | "devDependencies": {
15 | "react": "^18.2.0",
16 | "react-dom": "^18.2.0",
17 | "react-scripts": "^5.0.1"
18 | },
19 | "browserslist": {
20 | "production": [
21 | ">0.2%",
22 | "not dead",
23 | "not op_mini all",
24 | "ie 11"
25 | ],
26 | "development": [
27 | "last 1 chrome version",
28 | "last 1 firefox version",
29 | "last 1 safari version",
30 | "ie 11"
31 | ]
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/cypress/README.md:
--------------------------------------------------------------------------------
1 | # Cypress.io end-to-end tests
2 |
3 | [Cypress.io](https://www.cypress.io) is an open source, MIT licensed end-to-end test runner
4 |
5 | ## Folder structure
6 |
7 | These folders hold end-to-end tests and supporting files for the Cypress Test Runner.
8 |
9 | - [fixtures](fixtures) holds optional JSON data for mocking, [read more](https://on.cypress.io/fixture)
10 | - [integration](integration) holds the actual test files, [read more](https://on.cypress.io/writing-and-organizing-tests)
11 | - [plugins](plugins) allow you to customize how tests are loaded, [read more](https://on.cypress.io/plugins)
12 | - [support](support) file runs before all tests and is a great place to write or load additional custom commands, [read more](https://on.cypress.io/writing-and-organizing-tests#Support-file)
13 |
14 | ## `cypress.json` file
15 |
16 | You can configure project options in the [../cypress.json](../cypress.json) file, see [Cypress configuration doc](https://on.cypress.io/configuration).
17 |
18 | ## More information
19 |
20 | - [https://github.com/cypress.io/cypress](https://github.com/cypress.io/cypress)
21 | - [https://docs.cypress.io/](https://docs.cypress.io/)
22 | - [Writing your first Cypress test](http://on.cypress.io/intro)
23 |
--------------------------------------------------------------------------------
/examples/ts-example/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-before-each-visit",
3 | "description": "Getting code coverage when cy.visit is used in beforeEach hook",
4 | "devDependencies": {
5 | "@babel/core": "7.9.0"
6 | },
7 | "scripts": {
8 | "start": "../../node_modules/.bin/parcel serve index.html",
9 | "build": "../../node_modules/.bin/parcel build index.html",
10 | "serve": "../../node_modules/.bin/serve dist",
11 | "cy:open": "../../node_modules/.bin/cypress open",
12 | "cy:run": "../../node_modules/.bin/cypress run",
13 | "coverage": "../../node_modules/.bin/nyc report --report-dir ./coverage --temp-dir .nyc_output --reporter=lcov",
14 | "coverage:check": "../../node_modules/.bin/nyc report --check-coverage true --lines 100",
15 | "dev": "../../node_modules/.bin/start-test 1234 cy:open",
16 | "dev:dist": "CYPRESS_baseUrl=http://localhost:5000 ../../node_modules/.bin/start-test serve 5000 cy:open",
17 | "e2e": "../../node_modules/.bin/start-test 1234 cy:run",
18 | "check:covered": "../../node_modules/.bin/check-coverage main.ts calc.ts",
19 | "check:extras": "../../node_modules/.bin/only-covered --from coverage/coverage-final.json main.ts calc.ts",
20 | "check": "npm run check:covered && npm run check:extras"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/examples/unit-tests-ts/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | // using browserify
2 | const browserify = require('@cypress/browserify-preprocessor')
3 |
4 | module.exports = (on, config) => {
5 | require('../../../../task')(on, config)
6 |
7 | const options = browserify.defaultOptions
8 | // options.browserifyOptions.transform[1][1].babelrc = true
9 | options.browserifyOptions.extensions.push('.ts')
10 | // options.browserifyOptions.transform.push([
11 | // require.resolve('browserify-istanbul'),
12 | // {}
13 | // ])
14 | options.typescript = require.resolve('typescript')
15 | // on('file:preprocessor', require('../../../../use-babelrc'))
16 | console.log('browserify options')
17 | console.log(JSON.stringify(options, null, 2))
18 |
19 | on('file:preprocessor', browserify(options))
20 | return config
21 | }
22 |
23 | // using webpack
24 | ///
25 | // const webpack = require('@cypress/webpack-preprocessor')
26 |
27 | // /**
28 | // * @type {Cypress.PluginConfig}
29 | // */
30 | // module.exports = (on, config) => {
31 | // const options = {
32 | // // use the same Webpack options to bundle spec files as your app does "normally"
33 | // // which should instrument the spec files in this project
34 | // webpackOptions: require('../../webpack.config'),
35 | // watchOptions: {}
36 | // }
37 | // on('file:preprocessor', webpack(options))
38 |
39 | // require('../../../../task')(on, config)
40 | // return config
41 | // }
42 |
--------------------------------------------------------------------------------
/examples/docker-paths/README.md:
--------------------------------------------------------------------------------
1 | # example-docker-paths
2 |
3 | In this example, the source files are "instrumented" as if they were instrumented inside a Docker container. Still, Cypress code coverage plugin should find the matching current folder where same files exist and update `.nyc_output/out.json` file before generating reports.
4 |
5 | Source files from `app` folder were instrumented into `dist` folder with command
6 |
7 | ```shell
8 | $ npx nyc instrument app dist
9 | ```
10 |
11 | Then the `index.html` file was copied into `dist` folder.
12 |
13 | Then the source paths in [dist/main.js](dist/main.js) and [dist/second.js](dist/second.js) were changed to non-existent prefix folder `/var/www/test/site`.
14 |
15 | When Cypress runs, the `.nyc_output/out.json` is updated, so the path is valid local path like:
16 |
17 | ```
18 | {
19 | "/var/www/test/site/app/main.js": {
20 | "path": "/Users/gleb/git/code-coverage/examples/docker-paths/app/main.js",
21 | "statementMap": {
22 | ...
23 | ```
24 |
25 | And the report has valid HTML with sources
26 |
27 | 
28 |
29 | 
30 |
31 | **Note:** remember to remove existing `.nyc_output` folder if running Cypress in non-interactive mode `rm -rf .nyc_output/`.
32 |
33 | When running with [debug logs](https://github.com/cypress-io/code-coverage#debugging) you should see messages:
34 |
35 | ```
36 | found common folder /var/www/test/site that matches
37 | current working directory /Users/gleb/git/code-coverage/examples/docker-paths
38 | ```
39 |
--------------------------------------------------------------------------------
/examples/component/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | component: {
5 | framework: 'react-scripts',
6 | bundler: 'webpack',
7 | fixturesFolder: false,
8 | video: false,
9 | },
10 |
11 | component: {
12 | viewportHeight: 100,
13 | viewportWidth: 100,
14 | fixturesFolder: false,
15 | video: false,
16 | // We've imported your old cypress plugins here.
17 | // You may want to clean this up later by importing these.
18 | setupNodeEvents(on, config) {
19 | return require('../../plugins')(on, config)
20 | },
21 | devServer: {
22 | framework: 'create-react-app',
23 | bundler: 'webpack',
24 | webpackConfig: {
25 | mode: 'development',
26 | devtool: false,
27 | module: {
28 | rules: [
29 | // application and Cypress files are bundled like React components
30 | // and instrumented using the babel-plugin-istanbul
31 | {
32 | test: /\.jsx?$/,
33 | // do not instrument node_modules
34 | // or Cypress component specs
35 | exclude: /node_modules|\.cy\.jsx/,
36 | use: {
37 | loader: 'babel-loader',
38 | options: {
39 | presets: ['@babel/preset-env', '@babel/preset-react'],
40 | plugins: ['istanbul'],
41 | },
42 | },
43 | },
44 | ],
45 | },
46 | },
47 | },
48 | },
49 | })
50 |
--------------------------------------------------------------------------------
/cypress/e2e/calculator-po.js:
--------------------------------------------------------------------------------
1 | function enterExpression(expression) {
2 | cy.log(`Entering expression "**${expression}**"`)
3 | expression.split('').forEach((char) => {
4 | cy.contains('#buttons button', char, { log: false }).click({
5 | log: false,
6 | })
7 | })
8 | }
9 |
10 | export const CalculatorPage = {
11 | visit() {
12 | cy.visit('public/index.html')
13 | },
14 |
15 | /**
16 | * @param {string} expression to enter into the calculator, like "1+2+3"
17 | */
18 | enterExpression,
19 |
20 | /**
21 | * @param {string} expression to be evaluated, like "1+2"
22 | * @param {string} expectedResult the expected result, like "3"
23 | */
24 | compute(expression, expectedResult) {
25 | enterExpression(expression)
26 | cy.contains('#buttons button', '=', { log: false }).click({
27 | log: false,
28 | })
29 | cy.get('#display').should('have.text', expectedResult)
30 | return this
31 | },
32 |
33 | clear() {
34 | cy.log('**clearing the calculator**')
35 | cy.contains('#buttons button', 'C', { log: false }).click({
36 | log: false,
37 | })
38 | return this
39 | },
40 |
41 | /**
42 | * @param {string[]} items list of history items to check
43 | */
44 | checkHistory(...items) {
45 | cy.log(`**checking history with ${items.length} items**`)
46 | cy.get('#history li', { log: false }).should('have.length', items.length)
47 | items.forEach((item, k) => {
48 | cy.get('#history li', { log: false })
49 | .eq(k, { log: false })
50 | .should('have.text', item)
51 | })
52 | },
53 | }
54 |
--------------------------------------------------------------------------------
/cypress/calculator/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 | TDD Calculator
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | +
17 | 7
18 | 8
19 | 9
20 |
21 | -
22 |
23 | 4
24 | 5
25 | 6
26 |
27 | *
28 |
29 | 1
30 | 2
31 | 3
32 |
33 | /
34 |
35 | 0
36 | .
37 | =
38 |
39 | C
40 |
41 |
42 |
43 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: ci
2 | on: push
3 | permissions:
4 | contents: write
5 | issues: write
6 | pull-requests: write
7 | jobs:
8 | test:
9 | runs-on: ubuntu-22.04
10 | steps:
11 | - name: Checkout 🛎
12 | uses: actions/checkout@v4
13 |
14 | - name: Install dependencies 📦
15 | # https://github.com/cypress-io/github-action
16 | uses: cypress-io/github-action@v6
17 | with:
18 | runTests: false
19 |
20 | - name: Check types 🧾
21 | run: npm run types
22 |
23 | - name: Catch it.only 🫴
24 | run: npm run stop-only
25 |
26 | - name: Run tests 🧪
27 | uses: cypress-io/github-action@v6
28 | with:
29 | install: false
30 | start: npm start
31 |
32 | test-merge-coverage:
33 | runs-on: ubuntu-20.04
34 | env:
35 | DEBUG: code-coverage
36 | steps:
37 | - name: Checkout 🛎
38 | uses: actions/checkout@v4
39 |
40 | - name: Install dependencies 🧪
41 | # https://github.com/cypress-io/github-action
42 | uses: cypress-io/github-action@v6
43 | with:
44 | runTests: false
45 |
46 | - name: Merge coverage tests
47 | working-directory: examples/merge-coverage
48 | run: |
49 | npm run cy:run1
50 | npm run cy:run2
51 | npm run merge
52 |
53 | release:
54 | needs: [test, test-merge-coverage]
55 | runs-on: ubuntu-22.04
56 | if: github.ref == 'refs/heads/main'
57 | steps:
58 | - name: Checkout 🛎
59 | uses: actions/checkout@v4
60 |
61 | - name: Install only the semantic release 📦
62 | run: npm install semantic-release
63 |
64 | - name: Semantic Release 🚀
65 | uses: cycjimmy/semantic-release-action@v4
66 | with:
67 | branch: main
68 | env:
69 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
70 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
71 |
--------------------------------------------------------------------------------
/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | // enables intelligent code completion for Cypress commands
2 | // https://on.cypress.io/intelligent-code-completion
3 | ///
4 |
5 | import { add } from '../unit'
6 | const { fixSourcePaths } = require('../../support-utils')
7 |
8 | context('Page test', () => {
9 | beforeEach(() => {
10 | cy.visit('/', {
11 | onBeforeLoad(win) {
12 | cy.spy(win.console, 'log').as('log')
13 | }
14 | })
15 | })
16 |
17 | it('logs names', function() {
18 | cy.get('@log')
19 | .should('have.been.calledOnce')
20 | .should('have.been.calledWith', 'just names', ['joe', 'mary'])
21 | })
22 |
23 | it('loads About page', () => {
24 | cy.contains('About').click()
25 | cy.url().should('match', /\/about/)
26 | cy.contains('h2', 'About')
27 | cy.contains('Est. 2019')
28 | })
29 | })
30 |
31 | context('Unit tests', () => {
32 | it('adds numbers', () => {
33 | expect(add(2, 3)).to.equal(5)
34 | })
35 |
36 | it('concatenates strings', () => {
37 | expect(add('foo', 'Bar')).to.equal('fooBar')
38 | })
39 |
40 | it('fixes webpack loader source-map pathes', () => {
41 | const coverage = {
42 | '/absolute/src/component.vue': {
43 | path: '/absolute/src/component.vue',
44 | inputSourceMap: {
45 | sources: [
46 | '/folder/node_modules/cache-loader/dist/cjs.js??ref--0-0!/folder/node_modules/vue-loader/lib/index.js??vue-loader-options!component.vue?vue&type=script&lang=ts&',
47 | 'otherFile.js'
48 | ],
49 | sourceRoot: 'src'
50 | }
51 | },
52 | '/folder/module-without-sourcemap.js': {
53 | path: '/folder/module-without-sourcemap.js'
54 | }
55 | }
56 |
57 | fixSourcePaths(coverage)
58 |
59 | expect(coverage).to.deep.eq({
60 | '/absolute/src/component.vue': {
61 | path: '/absolute/src/component.vue',
62 | inputSourceMap: {
63 | sources: ['/absolute/src/component.vue', 'otherFile.js'],
64 | sourceRoot: ''
65 | }
66 | },
67 | '/folder/module-without-sourcemap.js': {
68 | path: '/folder/module-without-sourcemap.js'
69 | }
70 | })
71 | })
72 | })
73 |
--------------------------------------------------------------------------------
/plugin.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | require('console.table')
3 | const { getNycReportFilename } = require('./task-utils')
4 | const { existsSync } = require('fs')
5 | const NYC = require('nyc')
6 | const debug = require('debug')('code-coverage')
7 | const { reportCodeCoverageGHACallback } = require('./src/utils')
8 | const { isPluginDisabled } = require('./common-utils')
9 |
10 | const nycFilename = getNycReportFilename(process.cwd())
11 |
12 | function registerCodeCoveragePlugin(on, config) {
13 | require('./task')(on, config)
14 |
15 | // the user can report the code coverage after each spec
16 | // reportAfterEachSpec: false = disabled reporting
17 | // reportAfterEachSpec: true = enabled reporting, default reporter
18 | // reportAfterEachSpec: 'text' = enabled "text" code coverage reporter
19 | // typical values: 'text-summary', 'text'
20 | let reportAfterEachSpec = 'text-summary'
21 | let shouldReport = !isPluginDisabled(config.env)
22 | if (
23 | config.env &&
24 | typeof config.env.coverage === 'object' &&
25 | 'reportAfterEachSpec' in config.env.coverage
26 | ) {
27 | if (config.env.coverage.reportAfterEachSpec === false) {
28 | shouldReport = false
29 | } else if (config.env.coverage.reportAfterEachSpec === true) {
30 | // use the default code coverage reporter
31 | } else {
32 | reportAfterEachSpec = config.env.coverage.reportAfterEachSpec
33 | debug('')
34 | }
35 | }
36 |
37 | debug('should report? %s', shouldReport)
38 | if (shouldReport) {
39 | const nyc = new NYC({
40 | cwd: process.cwd(),
41 | reporter: reportAfterEachSpec,
42 | })
43 | on('after:spec', (t) => {
44 | console.log('code coverage after spec %s', t.relative)
45 | if (existsSync(nycFilename)) {
46 | return nyc.report()
47 | } else {
48 | console.warn('Could not find coverage file %s', nycFilename)
49 | }
50 | })
51 |
52 | if (process.env.GITHUB_ACTIONS) {
53 | debug('will report code coverage on GitHub Actions')
54 | on('after:run', reportCodeCoverageGHACallback)
55 | }
56 | }
57 |
58 | // IMPORTANT to return the config object
59 | // with the any changed environment variables
60 | return config
61 | }
62 |
63 | module.exports = registerCodeCoveragePlugin
64 |
--------------------------------------------------------------------------------
/bin/cc-merge.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const debug = require('debug')('code-coverage')
4 |
5 | // finds all "coverage-final.json" files in the give folder
6 | // and merges them into a single "cc-merged/out.json" file
7 | // Use:
8 | // npx cc-merge
9 | const topCoverageFolder = process.argv[2]
10 | if (!topCoverageFolder) {
11 | console.error('use: npx cc-merge ')
12 | process.exit(1)
13 | }
14 |
15 | const coverageFilename = 'coverage-final.json'
16 | debug('looking for %s files in %s', coverageFilename, topCoverageFolder)
17 |
18 | const fs = require('fs')
19 | const rimraf = require('rimraf')
20 | const path = require('path')
21 | const globby = require('globby')
22 | const allFiles = globby.sync(`**/${coverageFilename}`, {
23 | absolute: true,
24 | cwd: topCoverageFolder,
25 | })
26 | debug('found %d coverage files', allFiles.length)
27 | debug(allFiles.join(','))
28 |
29 | if (!allFiles.length) {
30 | console.error(
31 | 'Could not find any %s files in the folder %s',
32 | coverageFilename,
33 | topCoverageFolder,
34 | )
35 | process.exit(1)
36 | }
37 |
38 | const nycOutputFolder = '.nyc_output'
39 | if (fs.existsSync(nycOutputFolder)) {
40 | debug('deleting the existing folder %s', nycOutputFolder)
41 | rimraf.nativeSync(nycOutputFolder)
42 | }
43 |
44 | if (!fs.existsSync(nycOutputFolder)) {
45 | debug('creating folder %s', nycOutputFolder)
46 | fs.mkdirSync(nycOutputFolder)
47 | }
48 | allFiles.forEach((filename, k) => {
49 | const outputFilename = path.join(nycOutputFolder, `c-${k + 1}.json`)
50 | fs.copyFileSync(filename, outputFilename)
51 | debug('%d: copied %s to %s', k + 1, filename, outputFilename)
52 | })
53 |
54 | const { getNycOptions } = require('../task-utils')
55 | const { reportCodeCoverageGHA } = require('../src/utils')
56 |
57 | const processWorkingDirectory = process.cwd()
58 | const nycReportOptions = getNycOptions(processWorkingDirectory)
59 | debug('calling NYC reporter with options %o', nycReportOptions)
60 | debug('current working directory is %s', processWorkingDirectory)
61 | const NYC = require('nyc')
62 | const nyc = new NYC(nycReportOptions)
63 |
64 | nyc.report().then(() => {
65 | if (process.env.GITHUB_ACTIONS) {
66 | debug('will report combined code coverage on GitHub Actions')
67 | reportCodeCoverageGHA('Combined code coverage')
68 | }
69 | })
70 |
--------------------------------------------------------------------------------
/common-utils.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | function stringToArray(prop, obj) {
3 | if (typeof obj[prop] === 'string') {
4 | obj[prop] = [obj[prop]]
5 | }
6 |
7 | return obj
8 | }
9 |
10 | function combineNycOptions(...options) {
11 | // last option wins
12 | const nycOptions = Object.assign({}, ...options)
13 |
14 | // normalize string and [string] props
15 | stringToArray('reporter', nycOptions)
16 | stringToArray('extension', nycOptions)
17 | stringToArray('exclude', nycOptions)
18 |
19 | return nycOptions
20 | }
21 |
22 | const defaultNycOptions = {
23 | 'report-dir': './coverage',
24 | reporter: ['lcov', 'clover', 'json', 'json-summary'],
25 | extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx'],
26 | excludeAfterRemap: false,
27 | }
28 |
29 | /**
30 | * Returns an object with placeholder properties for files we
31 | * do not have coverage yet. The result can go into the coverage object
32 | *
33 | * @param {string} fullPath Filename
34 | */
35 | const fileCoveragePlaceholder = (fullPath) => {
36 | return {
37 | path: fullPath,
38 | statementMap: {},
39 | fnMap: {},
40 | branchMap: {},
41 | s: {},
42 | f: {},
43 | b: {},
44 | }
45 | }
46 |
47 | const isPlaceholder = (entry) => {
48 | // when the file has been instrumented, its entry has "hash" property
49 | return !('hash' in entry)
50 | }
51 |
52 | /**
53 | * Given a coverage object with potential placeholder entries
54 | * inserted instead of covered files, removes them. Modifies the object in place
55 | */
56 | const removePlaceholders = (coverage) => {
57 | Object.keys(coverage).forEach((key) => {
58 | if (isPlaceholder(coverage[key])) {
59 | delete coverage[key]
60 | }
61 | })
62 | }
63 |
64 | /**
65 | * Returns true if the user disabled the plugin using the env object.
66 | */
67 | function isPluginDisabled(cyEnv) {
68 | if (cyEnv.coverage === false) {
69 | return true
70 | }
71 | if (typeof cyEnv.coverage === 'object') {
72 | // the user explicitly disabled the plugin
73 | // be kind and accept both "disable" and "disabled" options
74 | return cyEnv.coverage.disable === true || cyEnv.coverage.disabled === true
75 | }
76 |
77 | // by default the plugin is enabled
78 | return false
79 | }
80 |
81 | module.exports = {
82 | combineNycOptions,
83 | defaultNycOptions,
84 | fileCoveragePlaceholder,
85 | removePlaceholders,
86 | isPluginDisabled,
87 | }
88 |
--------------------------------------------------------------------------------
/cypress/calculator/styles.css:
--------------------------------------------------------------------------------
1 | /* calculator styles */
2 |
3 | body {
4 | margin: 0;
5 | padding: 0;
6 | display: flex;
7 | justify-content: center;
8 | align-items: center;
9 | height: 100vh;
10 | background-color: hsl(0, 0%, 95%);
11 | }
12 |
13 | #calculator {
14 | overflow: hidden;
15 | font-family: Arial, sans-serif;
16 | background-color: hsl(0, 0%, 15%);
17 | border-radius: 15px;
18 | display: flex;
19 | flex-direction: row;
20 | }
21 |
22 | #display {
23 | width: 100%;
24 | padding: 20px;
25 | height: 80px;
26 | font-size: 80px;
27 | text-align: left;
28 | border: none;
29 | background-color: hsl(0, 0%, 20%);
30 | color: white;
31 | /* color: hsl(0, 0%, 30%); */
32 | }
33 |
34 | #buttons {
35 | /* use CSS grid to place all buttons into 4 columns */
36 | display: grid;
37 | grid-template-columns: repeat(4, 1fr);
38 | /* use 10 pixel gap between the items */
39 | /* and put some padding around the buttons */
40 | gap: 10px;
41 | padding: 25px;
42 | }
43 |
44 | button {
45 | /* make each button a circle with 100px diameter */
46 | width: 100px;
47 | height: 100px;
48 | border-radius: 50px;
49 | border: none;
50 | background-color: hsl(0, 0%, 30%);
51 | color: white;
52 | font-size: 3rem;
53 | font-weight: bold;
54 | cursor: pointer;
55 | }
56 |
57 | button:hover {
58 | /* the button background becomes slightly lighter on hover */
59 | background-color: hsl(0, 0%, 40%);
60 | }
61 |
62 | /* active state for all buttons */
63 | button:active {
64 | background-color: hsl(0, 0%, 60%);
65 | }
66 | .operator-btn:active {
67 | background-color: hsl(35, 100%, 85%);
68 | }
69 |
70 | .operator-btn {
71 | background-color: hsl(35, 100%, 55%);
72 | color: hsl(0, 0%, 20%);
73 | }
74 |
75 | .operator-btn:hover {
76 | /* the orange button background becomes slightly lighter on hover */
77 | background-color: hsl(35, 100%, 65%);
78 | }
79 |
80 | #right-column {
81 | width: 500px;
82 | background-color: hsl(0, 0%, 35%);
83 | }
84 |
85 | #history {
86 | width: 100%;
87 | height: 95vh;
88 | min-height: 100px;
89 | overflow-y: scroll;
90 | display: flex;
91 | flex-direction: column;
92 | font-size: 3rem;
93 | text-align: right;
94 | }
95 |
96 | #history-list {
97 | padding-right: 30px;
98 | }
99 |
100 | #history li {
101 | list-style-type: none;
102 | padding: 10px;
103 | color: white;
104 | margin: 5px;
105 | }
106 |
107 | @media screen and (max-width: 500px) {
108 | /* hide the right column element on smaller screens */
109 | #right-column {
110 | display: none;
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/cypress/e2e/combine.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | const { combineNycOptions, defaultNycOptions } = require('../../common-utils')
3 | describe('Combine NYC options', () => {
4 | it('overrides defaults', () => {
5 | const pkgNycOptions = {
6 | extends: '@istanbuljs/nyc-config-typescript',
7 | all: true
8 | }
9 | const combined = combineNycOptions(defaultNycOptions, pkgNycOptions)
10 | cy.wrap(combined).should('deep.equal', {
11 | extends: '@istanbuljs/nyc-config-typescript',
12 | all: true,
13 | 'report-dir': './coverage',
14 | reporter: ['lcov', 'clover', 'json', 'json-summary'],
15 | extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx'],
16 | excludeAfterRemap: false
17 | })
18 | })
19 |
20 | it('allows to specify reporter, but changes to array', () => {
21 | const pkgNycOptions = {
22 | reporter: 'text'
23 | }
24 | const combined = combineNycOptions(defaultNycOptions, pkgNycOptions)
25 | cy.wrap(combined).should('deep.equal', {
26 | 'report-dir': './coverage',
27 | reporter: ['text'],
28 | extension: ['.js', '.cjs', '.mjs', '.ts', '.tsx', '.jsx'],
29 | excludeAfterRemap: false
30 | })
31 | })
32 |
33 | it('combines multiple options', () => {
34 | const pkgNycOptions = {
35 | all: true,
36 | extension: '.js'
37 | }
38 | const nycrc = {
39 | include: ['foo.js']
40 | }
41 | const nycrcJson = {
42 | exclude: ['bar.js'],
43 | reporter: ['json']
44 | }
45 | const nycConfig = {
46 | 'report-dir': './report'
47 | }
48 | const combined = combineNycOptions(
49 | defaultNycOptions,
50 | nycrc,
51 | nycrcJson,
52 | nycConfig,
53 | pkgNycOptions
54 | )
55 | cy.wrap(combined).should('deep.equal', {
56 | all: true,
57 | 'report-dir': './report',
58 | reporter: ['json'],
59 | extension: ['.js'],
60 | excludeAfterRemap: false,
61 | include: ['foo.js'],
62 | exclude: ['bar.js']
63 | })
64 | })
65 |
66 | it('converts exclude to array', () => {
67 | // https://github.com/cypress-io/code-coverage/issues/248
68 | const pkgNycOptions = {
69 | all: true,
70 | extension: '.js'
71 | }
72 | const nycrc = {
73 | include: ['foo.js']
74 | }
75 | const nycrcJson = {
76 | exclude: 'bar.js',
77 | reporter: ['json']
78 | }
79 | const combined = combineNycOptions(
80 | defaultNycOptions,
81 | nycrc,
82 | nycrcJson,
83 | pkgNycOptions
84 | )
85 | cy.wrap(combined).should('deep.equal', {
86 | all: true,
87 | 'report-dir': './coverage',
88 | reporter: ['json'],
89 | extension: ['.js'],
90 | excludeAfterRemap: false,
91 | include: ['foo.js'],
92 | exclude: ['bar.js']
93 | })
94 | })
95 | })
96 |
--------------------------------------------------------------------------------
/src/utils.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 |
3 | const debug = require('debug')('code-coverage')
4 | const ghCore = require('@actions/core')
5 | const path = require('path')
6 | const { existsSync, readFileSync } = require('fs')
7 |
8 | function pickCoverageEmoji(percentage) {
9 | if (percentage >= 95) {
10 | return '✅'
11 | }
12 | if (percentage >= 90) {
13 | return '🏆'
14 | }
15 | if (percentage >= 80) {
16 | return '🥇'
17 | }
18 | if (percentage >= 70) {
19 | return '🥈'
20 | }
21 | if (percentage >= 60) {
22 | return '🥉'
23 | }
24 | if (percentage >= 50) {
25 | return '📈'
26 | }
27 | if (percentage >= 40) {
28 | return '⚠️'
29 | }
30 | return '🪫'
31 | }
32 |
33 | function reportCodeCoverageGHA(heading = 'Code coverage') {
34 | if (typeof heading !== 'string') {
35 | debug(heading)
36 | throw new Error('Expected a string heading when reporting CC on GHA')
37 | }
38 | const summaryFilename = path.join('coverage', 'coverage-summary.json')
39 | if (!existsSync(summaryFilename)) {
40 | debug('cannot find summary file %s', summaryFilename)
41 | } else {
42 | const summary = JSON.parse(readFileSync(summaryFilename, 'utf8'))
43 | if (summary.total) {
44 | debug('code coverage summary totals %o', summary.total)
45 | const s = summary.total.statements
46 | const b = summary.total.branches
47 | const f = summary.total.functions
48 | const l = summary.total.lines
49 | const row = [
50 | String(s.pct),
51 | `${s.covered}/${s.total}`,
52 | String(b.pct),
53 | `${b.covered}/${b.total}`,
54 | String(f.pct),
55 | `${f.covered}/${f.total}`,
56 | String(l.pct),
57 | `${l.covered}/${l.total}`,
58 | ]
59 | debug(row)
60 |
61 | ghCore.summary
62 | .addHeading(heading)
63 | .addTable([
64 | [
65 | { data: 'Statements %', header: true },
66 | { data: pickCoverageEmoji(s.pct), header: true },
67 | { data: 'Branches %', header: true },
68 | { data: pickCoverageEmoji(b.pct), header: true },
69 | { data: 'Functions %', header: true },
70 | { data: pickCoverageEmoji(f.pct), header: true },
71 | { data: 'Lines %', header: true },
72 | { data: pickCoverageEmoji(l.pct), header: true },
73 | ],
74 | row,
75 | ])
76 | .addLink(
77 | '@bahmutov/cypress-code-coverage',
78 | 'https://github.com/bahmutov/cypress-code-coverage',
79 | )
80 | .write()
81 | } else {
82 | debug('could not find totals in %s', summaryFilename)
83 | }
84 | }
85 | }
86 |
87 | function reportCodeCoverageGHACallback() {
88 | // we could use the test run summary passed in "after:run" event
89 | return reportCodeCoverageGHA()
90 | }
91 |
92 | module.exports = {
93 | pickCoverageEmoji,
94 | reportCodeCoverageGHA,
95 | reportCodeCoverageGHACallback,
96 | }
97 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@bahmutov/cypress-code-coverage",
3 | "version": "0.0.0-development",
4 | "description": "My version of Cypress code coverage plugin",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "cypress run",
8 | "start": "parcel serve cypress/index.html",
9 | "cy:open": "cypress open",
10 | "dev": "start-test 1234 cy:open",
11 | "semantic-release": "semantic-release",
12 | "test:ci": "start-test 1234",
13 | "report:coverage": "nyc report --reporter=html",
14 | "dev:no:coverage": "start-test 1234 'cypress open --env coverage=false'",
15 | "format": "prettier --write '*.js'",
16 | "format:check": "prettier --check '*.js'",
17 | "check:markdown": "find *.md -exec npx markdown-link-check {} \\;",
18 | "effective:config": "circleci config process .circleci/config.yml | sed /^#/d",
19 | "types": "tsc --noEmit --allowJs *.js cypress/e2e/*.js",
20 | "clean": "rm -rf .nyc_output coverage",
21 | "stop-only": "stop-only --folder cypress/e2e --folder examples --skip node_modules"
22 | },
23 | "peerDependencies": {
24 | "cypress": ">=10.0.0"
25 | },
26 | "repository": {
27 | "type": "git",
28 | "url": "https://github.com/bahmutov/cypress-code-coverage.git"
29 | },
30 | "keywords": [
31 | "cypress",
32 | "istanbul",
33 | "cypress-plugin",
34 | "code",
35 | "coverage"
36 | ],
37 | "author": "Gleb Bahmutov ",
38 | "license": "MIT",
39 | "bugs": {
40 | "url": "https://github.com/bahmutov/cypress-code-coverage/issues"
41 | },
42 | "homepage": "https://github.com/bahmutov/cypress-code-coverage#readme",
43 | "files": [
44 | "*.js",
45 | "src",
46 | "middleware",
47 | "bin"
48 | ],
49 | "bin": {
50 | "cc-merge": "bin/cc-merge.js"
51 | },
52 | "publishConfig": {
53 | "access": "public"
54 | },
55 | "private": false,
56 | "dependencies": {
57 | "@actions/core": "^1.10.0",
58 | "@cypress/browserify-preprocessor": "3.0.2",
59 | "chalk": "4.1.2",
60 | "console.table": "^0.10.0",
61 | "dayjs": "1.10.7",
62 | "debug": "4.3.7",
63 | "execa": "4.1.0",
64 | "globby": "11.1.0",
65 | "istanbul-lib-coverage": "3.0.0",
66 | "js-yaml": "3.14.1",
67 | "nyc": "17.1.0",
68 | "rimraf": "6.0.1",
69 | "sort-array": "^4.1.5"
70 | },
71 | "devDependencies": {
72 | "@babel/core": "7.16.0",
73 | "@babel/preset-typescript": "7.16.0",
74 | "@cypress/webpack-preprocessor": "5.10.0",
75 | "babel-loader": "8.2.3",
76 | "babel-plugin-istanbul": "6.1.1",
77 | "browserify-istanbul": "3.0.1",
78 | "check-code-coverage": "1.10.0",
79 | "console-log-div": "0.6.3",
80 | "cypress": "14.2.1",
81 | "express": "4.17.1",
82 | "lodash": "4.17.21",
83 | "markdown-link-check": "3.9.0",
84 | "parcel-bundler": "1.12.5",
85 | "prettier": "2.5.0",
86 | "semantic-release": "^19.0.2",
87 | "serve": "11.3.2",
88 | "start-server-and-test": "1.14.0",
89 | "stop-only": "^3.3.1",
90 | "ts-loader": "8.3.0",
91 | "typescript": "4.5.2",
92 | "webpack": "4.46.0",
93 | "webpack-cli": "3.3.12"
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/cypress/e2e/merge.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | const istanbul = require('istanbul-lib-coverage')
3 | const coverage = require('../fixtures/coverage.json')
4 | const {
5 | fileCoveragePlaceholder,
6 | removePlaceholders
7 | } = require('../../common-utils')
8 |
9 | /**
10 | * Extracts just the data from the coverage map object
11 | * @param {*} cm
12 | */
13 | const coverageMapToCoverage = (cm) => {
14 | return JSON.parse(JSON.stringify(cm))
15 | }
16 |
17 | describe('merging coverage', () => {
18 | const filename = '/src/index.js'
19 |
20 | before(() => {
21 | expect(coverage, 'initial coverage has this file').to.have.property(
22 | filename
23 | )
24 | })
25 |
26 | it('combines an empty coverage object', () => {
27 | const previous = istanbul.createCoverageMap({})
28 | const coverageMap = istanbul.createCoverageMap(previous)
29 | coverageMap.merge(Cypress._.cloneDeep(coverage))
30 |
31 | const merged = coverageMapToCoverage(coverageMap)
32 |
33 | expect(merged, 'merged coverage').to.deep.equal(coverage)
34 | })
35 |
36 | it('combines the same full coverage twice', () => {
37 | const previous = istanbul.createCoverageMap(Cypress._.cloneDeep(coverage))
38 | const coverageMap = istanbul.createCoverageMap(previous)
39 | coverageMap.merge(Cypress._.cloneDeep(coverage))
40 |
41 | const merged = coverageMapToCoverage(coverageMap)
42 | // it is almost the same - only the statement count has been doubled
43 | const expected = Cypress._.cloneDeep(coverage)
44 | expected[filename].s[0] = 2
45 | expect(merged, 'merged coverage').to.deep.equal(expected)
46 | })
47 |
48 | it('does not merge correctly placeholders', () => {
49 | const coverageWithPlaceHolder = Cypress._.cloneDeep(coverage)
50 | const placeholder = fileCoveragePlaceholder(filename)
51 | coverageWithPlaceHolder[filename] = placeholder
52 |
53 | expect(coverageWithPlaceHolder, 'placeholder').to.deep.equal({
54 | [filename]: placeholder
55 | })
56 |
57 | // now lets merge full info
58 | const previous = istanbul.createCoverageMap(coverageWithPlaceHolder)
59 | const coverageMap = istanbul.createCoverageMap(previous)
60 | coverageMap.merge(coverage)
61 |
62 | const merged = coverageMapToCoverage(coverageMap)
63 | const expected = Cypress._.cloneDeep(coverage)
64 | // the merge against the placeholder without valid statement map
65 | // removes the statement map and sets the counter to null
66 | expected[filename].s = { 0: null }
67 | expected[filename].statementMap = {}
68 | // and no hashes :(
69 | delete expected[filename].hash
70 | delete expected[filename]._coverageSchema
71 | expect(merged).to.deep.equal(expected)
72 | })
73 |
74 | it('removes placeholders', () => {
75 | const inputCoverage = Cypress._.cloneDeep(coverage)
76 | removePlaceholders(inputCoverage)
77 | expect(inputCoverage, 'nothing to remove').to.deep.equal(coverage)
78 |
79 | // add placeholder
80 | const placeholder = fileCoveragePlaceholder(filename)
81 | inputCoverage[filename] = placeholder
82 |
83 | removePlaceholders(inputCoverage)
84 | expect(inputCoverage, 'the placeholder has been removed').to.deep.equal({})
85 | })
86 | })
87 |
--------------------------------------------------------------------------------
/cypress/e2e/filtering.cy.js:
--------------------------------------------------------------------------------
1 | const { filterSpecsFromCoverage } = require('../../support-utils')
2 |
3 | describe('filtering specs', () => {
4 | it('filters out the config filename', () => {
5 | const config = cy.stub()
6 | config.withArgs('specPattern').returns([])
7 | config.withArgs('configFile').returns('/root/path/cypress.config.js')
8 | config.withArgs('projectRoot').returns('/')
9 |
10 | const totalCoverage = {
11 | '/path/to/specA.js': {},
12 | '/path/to/specB.js': {},
13 | // the config file should be filtered out
14 | 'cypress.config.js': {},
15 | }
16 | const result = filterSpecsFromCoverage(totalCoverage, config)
17 | expect(result).to.deep.equal({
18 | '/path/to/specA.js': {},
19 | '/path/to/specB.js': {},
20 | })
21 | })
22 |
23 | it('filters list of specs by single string', () => {
24 | const config = cy.stub()
25 | config.withArgs('specPattern').returns(['**/specA.js'])
26 | config.withArgs('configFile').returns('/root/path/cypress.config.js')
27 | config.withArgs('projectRoot').returns('/')
28 |
29 | const totalCoverage = {
30 | '/path/to/specA.js': {},
31 | '/path/to/specB.js': {},
32 | }
33 | const result = filterSpecsFromCoverage(totalCoverage, config)
34 | expect(result).to.deep.equal({
35 | '/path/to/specB.js': {},
36 | })
37 | })
38 |
39 | it('filters list of specs by pattern', () => {
40 | const config = cy.stub()
41 | config.withArgs('specPattern').returns(['**/*B.js'])
42 | config.withArgs('configFile').returns('/root/path/cypress.config.js')
43 | config.withArgs('projectRoot').returns('/')
44 |
45 | const totalCoverage = {
46 | '/path/to/specA.js': {},
47 | '/path/to/specB.js': {},
48 | }
49 | const result = filterSpecsFromCoverage(totalCoverage, config)
50 | expect(result).to.deep.equal({
51 | '/path/to/specA.js': {},
52 | })
53 | })
54 |
55 | it('filters list of specs by pattern and single spec', () => {
56 | const config = cy.stub()
57 | config.withArgs('specPattern').returns(['**/*B.js', '**/specA.js'])
58 | config.withArgs('configFile').returns('/root/path/cypress.config.js')
59 | config.withArgs('projectRoot').returns('/')
60 |
61 | const totalCoverage = {
62 | '/path/to/specA.js': {},
63 | '/path/to/specB.js': {},
64 | }
65 | const result = filterSpecsFromCoverage(totalCoverage, config)
66 | expect(result, 'all specs have been filtered out').to.deep.equal({})
67 | })
68 |
69 | it('filters list of specs in integration folder', () => {
70 | const config = cy.stub()
71 | config.withArgs('specPattern').returns('**/*.cy.*')
72 | config.withArgs('configFile').returns('/root/path/cypress.config.js')
73 | config.withArgs('projectRoot').returns('/')
74 |
75 | const totalCoverage = {
76 | '/path/to/specA.js': {},
77 | '/path/to/specB.js': {},
78 | // these files should be removed
79 | '/path/to/e2e/spec1.cy.js': {},
80 | '/path/to/e2e/feature/spec2.cy.js': {},
81 | }
82 | const result = filterSpecsFromCoverage(totalCoverage, config)
83 | expect(result).to.deep.equal({
84 | '/path/to/specA.js': {},
85 | '/path/to/specB.js': {},
86 | })
87 | })
88 | })
89 |
--------------------------------------------------------------------------------
/examples/one-spec/main-instrumented.js:
--------------------------------------------------------------------------------
1 | function cov_6k5v991cn() {
2 | var path = 'main.js'
3 | var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
4 | var global = new Function('return this')()
5 | var gcv = '__coverage__'
6 | var coverageData = {
7 | path: 'main.js',
8 | statementMap: {
9 | '0': {
10 | start: {
11 | line: 1,
12 | column: 0
13 | },
14 | end: {
15 | line: 1,
16 | column: 28
17 | }
18 | },
19 | '1': {
20 | start: {
21 | line: 1,
22 | column: 23
23 | },
24 | end: {
25 | line: 1,
26 | column: 28
27 | }
28 | },
29 | '2': {
30 | start: {
31 | line: 3,
32 | column: 0
33 | },
34 | end: {
35 | line: 3,
36 | column: 28
37 | }
38 | },
39 | '3': {
40 | start: {
41 | line: 3,
42 | column: 23
43 | },
44 | end: {
45 | line: 3,
46 | column: 28
47 | }
48 | }
49 | },
50 | fnMap: {
51 | '0': {
52 | name: '(anonymous_0)',
53 | decl: {
54 | start: {
55 | line: 1,
56 | column: 13
57 | },
58 | end: {
59 | line: 1,
60 | column: 14
61 | }
62 | },
63 | loc: {
64 | start: {
65 | line: 1,
66 | column: 23
67 | },
68 | end: {
69 | line: 1,
70 | column: 28
71 | }
72 | },
73 | line: 1
74 | },
75 | '1': {
76 | name: '(anonymous_1)',
77 | decl: {
78 | start: {
79 | line: 3,
80 | column: 13
81 | },
82 | end: {
83 | line: 3,
84 | column: 14
85 | }
86 | },
87 | loc: {
88 | start: {
89 | line: 3,
90 | column: 23
91 | },
92 | end: {
93 | line: 3,
94 | column: 28
95 | }
96 | },
97 | line: 3
98 | }
99 | },
100 | branchMap: {},
101 | s: {
102 | '0': 0,
103 | '1': 0,
104 | '2': 0,
105 | '3': 0
106 | },
107 | f: {
108 | '0': 0,
109 | '1': 0
110 | },
111 | b: {},
112 | _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
113 | hash: 'd384017ecd51a8d90283ba0dec593332209519de'
114 | }
115 | var coverage = global[gcv] || (global[gcv] = {})
116 |
117 | if (!coverage[path] || coverage[path].hash !== hash) {
118 | coverage[path] = coverageData
119 | }
120 |
121 | var actualCoverage = coverage[path]
122 |
123 | cov_6k5v991cn = function() {
124 | return actualCoverage
125 | }
126 |
127 | return actualCoverage
128 | }
129 |
130 | cov_6k5v991cn()
131 | cov_6k5v991cn().s[0]++
132 |
133 | window.add = (a, b) => {
134 | cov_6k5v991cn().f[0]++
135 | cov_6k5v991cn().s[1]++
136 | return a + b
137 | }
138 |
139 | cov_6k5v991cn().s[2]++
140 |
141 | window.sub = (a, b) => {
142 | cov_6k5v991cn().f[1]++
143 | cov_6k5v991cn().s[3]++
144 | return a - b
145 | }
146 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
147 |
--------------------------------------------------------------------------------
/examples/same-folder/main-instrumented.js:
--------------------------------------------------------------------------------
1 | function cov_6k5v991cn() {
2 | var path = 'main.js'
3 | var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
4 | var global = new Function('return this')()
5 | var gcv = '__coverage__'
6 | var coverageData = {
7 | path: 'main.js',
8 | statementMap: {
9 | '0': {
10 | start: {
11 | line: 1,
12 | column: 0
13 | },
14 | end: {
15 | line: 1,
16 | column: 28
17 | }
18 | },
19 | '1': {
20 | start: {
21 | line: 1,
22 | column: 23
23 | },
24 | end: {
25 | line: 1,
26 | column: 28
27 | }
28 | },
29 | '2': {
30 | start: {
31 | line: 3,
32 | column: 0
33 | },
34 | end: {
35 | line: 3,
36 | column: 28
37 | }
38 | },
39 | '3': {
40 | start: {
41 | line: 3,
42 | column: 23
43 | },
44 | end: {
45 | line: 3,
46 | column: 28
47 | }
48 | }
49 | },
50 | fnMap: {
51 | '0': {
52 | name: '(anonymous_0)',
53 | decl: {
54 | start: {
55 | line: 1,
56 | column: 13
57 | },
58 | end: {
59 | line: 1,
60 | column: 14
61 | }
62 | },
63 | loc: {
64 | start: {
65 | line: 1,
66 | column: 23
67 | },
68 | end: {
69 | line: 1,
70 | column: 28
71 | }
72 | },
73 | line: 1
74 | },
75 | '1': {
76 | name: '(anonymous_1)',
77 | decl: {
78 | start: {
79 | line: 3,
80 | column: 13
81 | },
82 | end: {
83 | line: 3,
84 | column: 14
85 | }
86 | },
87 | loc: {
88 | start: {
89 | line: 3,
90 | column: 23
91 | },
92 | end: {
93 | line: 3,
94 | column: 28
95 | }
96 | },
97 | line: 3
98 | }
99 | },
100 | branchMap: {},
101 | s: {
102 | '0': 0,
103 | '1': 0,
104 | '2': 0,
105 | '3': 0
106 | },
107 | f: {
108 | '0': 0,
109 | '1': 0
110 | },
111 | b: {},
112 | _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
113 | hash: 'd384017ecd51a8d90283ba0dec593332209519de'
114 | }
115 | var coverage = global[gcv] || (global[gcv] = {})
116 |
117 | if (!coverage[path] || coverage[path].hash !== hash) {
118 | coverage[path] = coverageData
119 | }
120 |
121 | var actualCoverage = coverage[path]
122 |
123 | cov_6k5v991cn = function() {
124 | return actualCoverage
125 | }
126 |
127 | return actualCoverage
128 | }
129 |
130 | cov_6k5v991cn()
131 | cov_6k5v991cn().s[0]++
132 |
133 | window.add = (a, b) => {
134 | cov_6k5v991cn().f[0]++
135 | cov_6k5v991cn().s[1]++
136 | return a + b
137 | }
138 |
139 | cov_6k5v991cn().s[2]++
140 |
141 | window.sub = (a, b) => {
142 | cov_6k5v991cn().f[1]++
143 | cov_6k5v991cn().s[3]++
144 | return a - b
145 | }
146 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
147 |
--------------------------------------------------------------------------------
/examples/before-all-visit/main-instrumented.js:
--------------------------------------------------------------------------------
1 | function cov_6k5v991cn() {
2 | var path = 'main.js'
3 | var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
4 | var global = new Function('return this')()
5 | var gcv = '__coverage__'
6 | var coverageData = {
7 | path: 'main.js',
8 | statementMap: {
9 | '0': {
10 | start: {
11 | line: 1,
12 | column: 0
13 | },
14 | end: {
15 | line: 1,
16 | column: 28
17 | }
18 | },
19 | '1': {
20 | start: {
21 | line: 1,
22 | column: 23
23 | },
24 | end: {
25 | line: 1,
26 | column: 28
27 | }
28 | },
29 | '2': {
30 | start: {
31 | line: 3,
32 | column: 0
33 | },
34 | end: {
35 | line: 3,
36 | column: 28
37 | }
38 | },
39 | '3': {
40 | start: {
41 | line: 3,
42 | column: 23
43 | },
44 | end: {
45 | line: 3,
46 | column: 28
47 | }
48 | }
49 | },
50 | fnMap: {
51 | '0': {
52 | name: '(anonymous_0)',
53 | decl: {
54 | start: {
55 | line: 1,
56 | column: 13
57 | },
58 | end: {
59 | line: 1,
60 | column: 14
61 | }
62 | },
63 | loc: {
64 | start: {
65 | line: 1,
66 | column: 23
67 | },
68 | end: {
69 | line: 1,
70 | column: 28
71 | }
72 | },
73 | line: 1
74 | },
75 | '1': {
76 | name: '(anonymous_1)',
77 | decl: {
78 | start: {
79 | line: 3,
80 | column: 13
81 | },
82 | end: {
83 | line: 3,
84 | column: 14
85 | }
86 | },
87 | loc: {
88 | start: {
89 | line: 3,
90 | column: 23
91 | },
92 | end: {
93 | line: 3,
94 | column: 28
95 | }
96 | },
97 | line: 3
98 | }
99 | },
100 | branchMap: {},
101 | s: {
102 | '0': 0,
103 | '1': 0,
104 | '2': 0,
105 | '3': 0
106 | },
107 | f: {
108 | '0': 0,
109 | '1': 0
110 | },
111 | b: {},
112 | _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
113 | hash: 'd384017ecd51a8d90283ba0dec593332209519de'
114 | }
115 | var coverage = global[gcv] || (global[gcv] = {})
116 |
117 | if (!coverage[path] || coverage[path].hash !== hash) {
118 | coverage[path] = coverageData
119 | }
120 |
121 | var actualCoverage = coverage[path]
122 |
123 | cov_6k5v991cn = function() {
124 | return actualCoverage
125 | }
126 |
127 | return actualCoverage
128 | }
129 |
130 | cov_6k5v991cn()
131 | cov_6k5v991cn().s[0]++
132 |
133 | window.add = (a, b) => {
134 | cov_6k5v991cn().f[0]++
135 | cov_6k5v991cn().s[1]++
136 | return a + b
137 | }
138 |
139 | cov_6k5v991cn().s[2]++
140 |
141 | window.sub = (a, b) => {
142 | cov_6k5v991cn().f[1]++
143 | cov_6k5v991cn().s[3]++
144 | return a - b
145 | }
146 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
147 |
--------------------------------------------------------------------------------
/examples/before-each-visit/main-instrumented.js:
--------------------------------------------------------------------------------
1 | function cov_6k5v991cn() {
2 | var path = 'main.js'
3 | var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
4 | var global = new Function('return this')()
5 | var gcv = '__coverage__'
6 | var coverageData = {
7 | path: 'main.js',
8 | statementMap: {
9 | '0': {
10 | start: {
11 | line: 1,
12 | column: 0
13 | },
14 | end: {
15 | line: 1,
16 | column: 28
17 | }
18 | },
19 | '1': {
20 | start: {
21 | line: 1,
22 | column: 23
23 | },
24 | end: {
25 | line: 1,
26 | column: 28
27 | }
28 | },
29 | '2': {
30 | start: {
31 | line: 3,
32 | column: 0
33 | },
34 | end: {
35 | line: 3,
36 | column: 28
37 | }
38 | },
39 | '3': {
40 | start: {
41 | line: 3,
42 | column: 23
43 | },
44 | end: {
45 | line: 3,
46 | column: 28
47 | }
48 | }
49 | },
50 | fnMap: {
51 | '0': {
52 | name: '(anonymous_0)',
53 | decl: {
54 | start: {
55 | line: 1,
56 | column: 13
57 | },
58 | end: {
59 | line: 1,
60 | column: 14
61 | }
62 | },
63 | loc: {
64 | start: {
65 | line: 1,
66 | column: 23
67 | },
68 | end: {
69 | line: 1,
70 | column: 28
71 | }
72 | },
73 | line: 1
74 | },
75 | '1': {
76 | name: '(anonymous_1)',
77 | decl: {
78 | start: {
79 | line: 3,
80 | column: 13
81 | },
82 | end: {
83 | line: 3,
84 | column: 14
85 | }
86 | },
87 | loc: {
88 | start: {
89 | line: 3,
90 | column: 23
91 | },
92 | end: {
93 | line: 3,
94 | column: 28
95 | }
96 | },
97 | line: 3
98 | }
99 | },
100 | branchMap: {},
101 | s: {
102 | '0': 0,
103 | '1': 0,
104 | '2': 0,
105 | '3': 0
106 | },
107 | f: {
108 | '0': 0,
109 | '1': 0
110 | },
111 | b: {},
112 | _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
113 | hash: 'd384017ecd51a8d90283ba0dec593332209519de'
114 | }
115 | var coverage = global[gcv] || (global[gcv] = {})
116 |
117 | if (!coverage[path] || coverage[path].hash !== hash) {
118 | coverage[path] = coverageData
119 | }
120 |
121 | var actualCoverage = coverage[path]
122 |
123 | cov_6k5v991cn = function() {
124 | return actualCoverage
125 | }
126 |
127 | return actualCoverage
128 | }
129 |
130 | cov_6k5v991cn()
131 | cov_6k5v991cn().s[0]++
132 |
133 | window.add = (a, b) => {
134 | cov_6k5v991cn().f[0]++
135 | cov_6k5v991cn().s[1]++
136 | return a + b
137 | }
138 |
139 | cov_6k5v991cn().s[2]++
140 |
141 | window.sub = (a, b) => {
142 | cov_6k5v991cn().f[1]++
143 | cov_6k5v991cn().s[3]++
144 | return a - b
145 | }
146 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
147 |
--------------------------------------------------------------------------------
/examples/fullstack/server/main-instrumented.js:
--------------------------------------------------------------------------------
1 | function cov_6k5v991cn() {
2 | var path = 'main.js'
3 | var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
4 | var global = new Function('return this')()
5 | var gcv = '__coverage__'
6 | var coverageData = {
7 | path: 'main.js',
8 | statementMap: {
9 | '0': {
10 | start: {
11 | line: 1,
12 | column: 0
13 | },
14 | end: {
15 | line: 1,
16 | column: 28
17 | }
18 | },
19 | '1': {
20 | start: {
21 | line: 1,
22 | column: 23
23 | },
24 | end: {
25 | line: 1,
26 | column: 28
27 | }
28 | },
29 | '2': {
30 | start: {
31 | line: 3,
32 | column: 0
33 | },
34 | end: {
35 | line: 3,
36 | column: 28
37 | }
38 | },
39 | '3': {
40 | start: {
41 | line: 3,
42 | column: 23
43 | },
44 | end: {
45 | line: 3,
46 | column: 28
47 | }
48 | }
49 | },
50 | fnMap: {
51 | '0': {
52 | name: '(anonymous_0)',
53 | decl: {
54 | start: {
55 | line: 1,
56 | column: 13
57 | },
58 | end: {
59 | line: 1,
60 | column: 14
61 | }
62 | },
63 | loc: {
64 | start: {
65 | line: 1,
66 | column: 23
67 | },
68 | end: {
69 | line: 1,
70 | column: 28
71 | }
72 | },
73 | line: 1
74 | },
75 | '1': {
76 | name: '(anonymous_1)',
77 | decl: {
78 | start: {
79 | line: 3,
80 | column: 13
81 | },
82 | end: {
83 | line: 3,
84 | column: 14
85 | }
86 | },
87 | loc: {
88 | start: {
89 | line: 3,
90 | column: 23
91 | },
92 | end: {
93 | line: 3,
94 | column: 28
95 | }
96 | },
97 | line: 3
98 | }
99 | },
100 | branchMap: {},
101 | s: {
102 | '0': 0,
103 | '1': 0,
104 | '2': 0,
105 | '3': 0
106 | },
107 | f: {
108 | '0': 0,
109 | '1': 0
110 | },
111 | b: {},
112 | _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
113 | hash: 'd384017ecd51a8d90283ba0dec593332209519de'
114 | }
115 | var coverage = global[gcv] || (global[gcv] = {})
116 |
117 | if (!coverage[path] || coverage[path].hash !== hash) {
118 | coverage[path] = coverageData
119 | }
120 |
121 | var actualCoverage = coverage[path]
122 |
123 | cov_6k5v991cn = function() {
124 | return actualCoverage
125 | }
126 |
127 | return actualCoverage
128 | }
129 |
130 | cov_6k5v991cn()
131 | cov_6k5v991cn().s[0]++
132 |
133 | window.add = (a, b) => {
134 | cov_6k5v991cn().f[0]++
135 | cov_6k5v991cn().s[1]++
136 | return a + b
137 | }
138 |
139 | cov_6k5v991cn().s[2]++
140 |
141 | window.sub = (a, b) => {
142 | cov_6k5v991cn().f[1]++
143 | cov_6k5v991cn().s[3]++
144 | return a - b
145 | }
146 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
147 |
--------------------------------------------------------------------------------
/examples/use-plugins-and-support/main-instrumented.js:
--------------------------------------------------------------------------------
1 | function cov_6k5v991cn() {
2 | var path = 'main.js'
3 | var hash = 'd384017ecd51a8d90283ba0dec593332209519de'
4 | var global = new Function('return this')()
5 | var gcv = '__coverage__'
6 | var coverageData = {
7 | path: 'main.js',
8 | statementMap: {
9 | '0': {
10 | start: {
11 | line: 1,
12 | column: 0
13 | },
14 | end: {
15 | line: 1,
16 | column: 28
17 | }
18 | },
19 | '1': {
20 | start: {
21 | line: 1,
22 | column: 23
23 | },
24 | end: {
25 | line: 1,
26 | column: 28
27 | }
28 | },
29 | '2': {
30 | start: {
31 | line: 3,
32 | column: 0
33 | },
34 | end: {
35 | line: 3,
36 | column: 28
37 | }
38 | },
39 | '3': {
40 | start: {
41 | line: 3,
42 | column: 23
43 | },
44 | end: {
45 | line: 3,
46 | column: 28
47 | }
48 | }
49 | },
50 | fnMap: {
51 | '0': {
52 | name: '(anonymous_0)',
53 | decl: {
54 | start: {
55 | line: 1,
56 | column: 13
57 | },
58 | end: {
59 | line: 1,
60 | column: 14
61 | }
62 | },
63 | loc: {
64 | start: {
65 | line: 1,
66 | column: 23
67 | },
68 | end: {
69 | line: 1,
70 | column: 28
71 | }
72 | },
73 | line: 1
74 | },
75 | '1': {
76 | name: '(anonymous_1)',
77 | decl: {
78 | start: {
79 | line: 3,
80 | column: 13
81 | },
82 | end: {
83 | line: 3,
84 | column: 14
85 | }
86 | },
87 | loc: {
88 | start: {
89 | line: 3,
90 | column: 23
91 | },
92 | end: {
93 | line: 3,
94 | column: 28
95 | }
96 | },
97 | line: 3
98 | }
99 | },
100 | branchMap: {},
101 | s: {
102 | '0': 0,
103 | '1': 0,
104 | '2': 0,
105 | '3': 0
106 | },
107 | f: {
108 | '0': 0,
109 | '1': 0
110 | },
111 | b: {},
112 | _coverageSchema: '1a1c01bbd47fc00a2c39e90264f33305004495a9',
113 | hash: 'd384017ecd51a8d90283ba0dec593332209519de'
114 | }
115 | var coverage = global[gcv] || (global[gcv] = {})
116 |
117 | if (!coverage[path] || coverage[path].hash !== hash) {
118 | coverage[path] = coverageData
119 | }
120 |
121 | var actualCoverage = coverage[path]
122 |
123 | cov_6k5v991cn = function() {
124 | return actualCoverage
125 | }
126 |
127 | return actualCoverage
128 | }
129 |
130 | cov_6k5v991cn()
131 | cov_6k5v991cn().s[0]++
132 |
133 | window.add = (a, b) => {
134 | cov_6k5v991cn().f[0]++
135 | cov_6k5v991cn().s[1]++
136 | return a + b
137 | }
138 |
139 | cov_6k5v991cn().s[2]++
140 |
141 | window.sub = (a, b) => {
142 | cov_6k5v991cn().f[1]++
143 | cov_6k5v991cn().s[3]++
144 | return a - b
145 | }
146 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1haW4uanMiXSwibmFtZXMiOlsid2luZG93IiwiYWRkIiwiYSIsImIiLCJzdWIiXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBQSxNQUFNLENBQUNDLEdBQVAsR0FBYSxDQUFDQyxDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1Qjs7OztBQUVBSCxNQUFNLENBQUNJLEdBQVAsR0FBYSxDQUFDRixDQUFELEVBQUlDLENBQUosS0FBVTtBQUFBO0FBQUE7QUFBQSxTQUFBRCxDQUFDLEdBQUdDLENBQUo7QUFBSyxDQUE1QiIsInNvdXJjZXNDb250ZW50IjpbIndpbmRvdy5hZGQgPSAoYSwgYikgPT4gYSArIGJcblxud2luZG93LnN1YiA9IChhLCBiKSA9PiBhIC0gYlxuIl19
147 |
--------------------------------------------------------------------------------
/support-utils.js:
--------------------------------------------------------------------------------
1 | ///
2 | // @ts-check
3 | // helper functions that are safe to use in the browser
4 | // from support.js file - no file system access
5 | const debug = require('debug')('code-coverage')
6 |
7 | /**
8 | * remove coverage for the spec files themselves,
9 | * only keep "external" application source file coverage.
10 | * Config has keys with absolute path names for each source file
11 | */
12 | const filterSpecsFromCoverage = (totalCoverage, config = Cypress.config) => {
13 | /** @type {string|string[]} Cypress run-time config has test files string pattern */
14 | const specPattern = config('specPattern')
15 | const configFilename = config('configFile')
16 |
17 | // test files could be:
18 | // wild card string "**/*.*" (default)
19 | // wild card string "**/*spec.js"
20 | // list of wild card strings or names ["**/*spec.js", "spec-one.js"]
21 | const rootFolder = config('projectRoot')
22 | if (typeof rootFolder === 'undefined') {
23 | throw new Error('Cypress projectRoot folder cannot be undefined')
24 | }
25 | const testFilePatterns = (
26 | Array.isArray(specPattern) ? specPattern : [specPattern]
27 | ).map((pattern) => {
28 | // we want absolute paths
29 | return rootFolder + '/' + pattern
30 | })
31 | debug({ specPattern, testFilePatterns, configFilename })
32 |
33 | const isTestFile = (filename) => {
34 | debug('testing filename', filename)
35 | const matchedPattern = testFilePatterns.some((specPattern) => {
36 | debug('minimatch %s against %s', filename, specPattern)
37 | return Cypress.minimatch(filename, specPattern)
38 | })
39 | const matchedEndOfPath = testFilePatterns.some((specPattern) =>
40 | filename.endsWith(specPattern),
41 | )
42 | const matchedConfig = configFilename.endsWith(filename)
43 | debug({ matchedPattern, matchedEndOfPath, matchedConfig })
44 |
45 | return matchedPattern || matchedEndOfPath || matchedConfig
46 | }
47 |
48 | const coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
49 | isTestFile(filename),
50 | )
51 | // debug(Object.keys(coverage))
52 |
53 | return coverage
54 | }
55 |
56 | /**
57 | * Replace source-map's path by the corresponding absolute file path
58 | * (coverage report wouldn't work with source-map path being relative
59 | * or containing Webpack loaders and query parameters)
60 | */
61 | function fixSourcePaths(coverage) {
62 | Object.values(coverage).forEach((file) => {
63 | const { path: absolutePath, inputSourceMap } = file
64 | // @ts-ignore
65 | const fileName = /([^\/\\]+)$/.exec(absolutePath)[1]
66 | if (!inputSourceMap || !fileName) return
67 |
68 | if (inputSourceMap.sourceRoot) inputSourceMap.sourceRoot = ''
69 | inputSourceMap.sources = inputSourceMap.sources.map((source) =>
70 | source.includes(fileName) ? absolutePath : source,
71 | )
72 | })
73 | }
74 |
75 | /**
76 | * by default we do not filter anything from the code coverage object
77 | * if the user gives a list of patters to filter, we filter the coverage object.
78 | * @param {boolean|string|string[]} exclude What to exclude (default files or specific list)
79 | * @param {object} coverage Each key is an absolute filepath
80 | */
81 | function excludeByUser(exclude, coverage) {
82 | if (!exclude) {
83 | return coverage
84 | }
85 |
86 | debug('excludeByUser config exclude', exclude)
87 | debug(Object.keys(coverage))
88 |
89 | if (exclude === true) {
90 | // try excluding spec and support files
91 | const withoutSpecs = filterSpecsFromCoverage(coverage)
92 | debug('exclude specs', Object.keys(withoutSpecs))
93 | const filteredCoverage = filterSupportFilesFromCoverage(withoutSpecs)
94 | debug('exclude true filtered', Object.keys(filteredCoverage))
95 | return filteredCoverage
96 | }
97 |
98 | const filterOut = Cypress._.isString(exclude) ? [exclude] : exclude
99 | // debug({ filterOut })
100 |
101 | const filteredCoverage = Cypress._.omitBy(
102 | coverage,
103 | (fileCoverage, filename) => {
104 | return filterOut.some((pattern) => {
105 | if (pattern.includes('*')) {
106 | return Cypress.minimatch(filename, pattern)
107 | }
108 | return filename.endsWith(pattern)
109 | })
110 | },
111 | )
112 | debug('exclude masks filtered', Object.keys(filteredCoverage))
113 | return filteredCoverage
114 | }
115 |
116 | /**
117 | * Removes support file from the coverage object.
118 | * If there are more files loaded from support folder, also removes them
119 | */
120 | const filterSupportFilesFromCoverage = (totalCoverage) => {
121 | const supportFile = Cypress.config('supportFile')
122 |
123 | /** @type {string} Cypress run-time config has the support folder string */
124 | const supportFolder = Cypress.config('supportFolder')
125 |
126 | const isSupportFile = (filename) => filename === supportFile
127 | const isInSupportFolder = (filename) => filename.startsWith(supportFolder)
128 |
129 | const coverage = Cypress._.omitBy(
130 | totalCoverage,
131 | (fileCoverage, filename) =>
132 | isSupportFile(filename) || isInSupportFolder(filename),
133 | )
134 |
135 | return coverage
136 | }
137 |
138 | module.exports = {
139 | fixSourcePaths,
140 | filterSpecsFromCoverage,
141 | filterSupportFilesFromCoverage,
142 | excludeByUser,
143 | }
144 |
--------------------------------------------------------------------------------
/cypress/calculator/app.js:
--------------------------------------------------------------------------------
1 | // calculator logic
2 |
3 | import { appendDot } from './utils.js'
4 |
5 | // the current list of history entries
6 | const history = []
7 |
8 | // on page visit
9 | // load the last expression from the localStorage (if any)
10 | // use the local storage key "calculator_data"
11 | try {
12 | const data = JSON.parse(localStorage.getItem('calculator_data'))
13 | if (data.version === 'v1') {
14 | const lastExpression = data.expression
15 | if (lastExpression) {
16 | document.getElementById('display').innerText = lastExpression
17 | // push the last expression to the history
18 | const historyListElement =
19 | document.getElementById('history-list')
20 | historyListElement.innerHTML = `${lastExpression}=${lastExpression} `
21 | history.push(`${lastExpression}=${lastExpression}`)
22 | // save the migrated data into the localStorage
23 | const migratedData = {
24 | version: 'v2',
25 | expression: lastExpression,
26 | history,
27 | }
28 | localStorage.setItem(
29 | 'calculator_data',
30 | JSON.stringify(migratedData),
31 | )
32 | }
33 | } else if (data.version === 'v2') {
34 | // set the DOM elements based on the data stored in the item
35 | // - expression
36 | // - history items
37 | const lastExpression = data.expression
38 | if (lastExpression) {
39 | document.getElementById('display').innerText = lastExpression
40 | }
41 | if (Array.isArray(data.history)) {
42 | const historyListElement =
43 | document.getElementById('history-list')
44 | historyListElement.innerHTML = data.history
45 | .map((item) => `${item} `)
46 | .join('\n')
47 | history.length = 0
48 | history.push(...data.history)
49 | }
50 | }
51 | } catch {
52 | // ignore serialization errors
53 | }
54 |
55 | // we probably want to keep around the reference to the
56 | // LI element with ID "history-list" to append new history items
57 | const historyListElement = document.getElementById('history-list')
58 |
59 | /**
60 | * Function that receives a digit to append to the currently displayed text
61 | * @param {number|'+'|'-'|'*'|'/'} digit A single digit to append to the display text
62 | */
63 | function enterDigit(digit) {
64 | const display = document.getElementById('display')
65 |
66 | if (digit === '.') {
67 | // special logic for adding the "." character
68 | display.innerText = appendDot(display.innerText)
69 | } else {
70 | display.innerText += digit
71 | }
72 |
73 | // store the current expression in the localStorage
74 | const data = {
75 | version: 'v2',
76 | expression: display.innerText,
77 | history,
78 | }
79 | localStorage.setItem('calculator_data', JSON.stringify(data))
80 | }
81 |
82 | /**
83 | * Compute the current display text as a JavaScript arithmetic expression
84 | * using the `eval` function and replace the display text with the result
85 | * @see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval
86 | */
87 | function calculate() {
88 | const display = document.getElementById('display')
89 | const expression = display.innerText
90 |
91 | // check the expression to be a simple numerical expression
92 | // without any extra characters
93 | // we only allow the following characters
94 | // digits, "+", "-", "*", "/", "."
95 | if (!/^[\d\-\+\*\/\.]+?$/.test(expression)) {
96 | display.innerText = 'INVALID'
97 | return
98 | }
99 |
100 | try {
101 | const result = eval(expression)
102 | display.innerText = result
103 | } catch (err) {
104 | display.innerText = 'ERROR'
105 | // after 1 second, put the original
106 | // expression back in the display
107 | setTimeout(() => {
108 | display.innerText = expression
109 | }, 1000)
110 | }
111 |
112 | // append the new expression and result to the history list
113 | const historyItem = `${expression}=${display.innerText}`
114 | historyListElement.innerHTML += `${historyItem} `
115 | // store the history in the localStorage
116 | history.push(historyItem)
117 |
118 | // store the current expression in the localStorage
119 | const data = {
120 | version: 'v2',
121 | expression: display.innerText,
122 | history,
123 | }
124 | localStorage.setItem('calculator_data', JSON.stringify(data))
125 | }
126 |
127 | /**
128 | * Clears the current display text
129 | */
130 | function clearDisplay() {
131 | const display = document.getElementById('display')
132 | display.innerText = ''
133 | }
134 |
135 | // attach event handlers
136 | // - button calculate should call the calculate function
137 | // - button clear should call the clearDisplay function
138 | // - all digit buttons should call the enterDigit function
139 | document
140 | .querySelector('#buttons button[title=calculate]')
141 | .addEventListener('click', calculate)
142 | document
143 | .querySelector('#buttons button[title="clear display"]')
144 | .addEventListener('click', clearDisplay)
145 | document
146 | .querySelectorAll('#buttons button[title="enter digit"]')
147 | .forEach((button) => {
148 | button.addEventListener('click', (e) => {
149 | // get the character from the event target
150 | const digit = e.target.innerText
151 | enterDigit(digit)
152 | })
153 | })
154 |
--------------------------------------------------------------------------------
/task.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const istanbul = require('istanbul-lib-coverage')
3 | const sortArray = require('sort-array')
4 | const { join, relative } = require('path')
5 | const { existsSync, mkdirSync, readFileSync, writeFileSync } = require('fs')
6 | const execa = require('execa')
7 | const {
8 | showNycInfo,
9 | resolveRelativePaths,
10 | checkAllPathsNotFound,
11 | tryFindingLocalFiles,
12 | getNycOptions,
13 | includeAllFiles,
14 | getCoverage,
15 | updateSpecCovers,
16 | } = require('./task-utils')
17 | const { fixSourcePaths } = require('./support-utils')
18 | const { removePlaceholders } = require('./common-utils')
19 |
20 | const debug = require('debug')('code-coverage')
21 |
22 | // these are standard folder and file names used by NYC tools
23 | const processWorkingDirectory = process.cwd()
24 |
25 | // there might be custom "nyc" options in the user package.json
26 | // see https://github.com/istanbuljs/nyc#configuring-nyc
27 | // potentially there might be "nyc" options in other configuration files
28 | // it allows, but for now ignore those options
29 | const pkgFilename = join(processWorkingDirectory, 'package.json')
30 | const pkg = existsSync(pkgFilename)
31 | ? JSON.parse(readFileSync(pkgFilename, 'utf8'))
32 | : {}
33 | const scripts = pkg.scripts || {}
34 | const DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME = 'coverage:report'
35 | const customNycReportScript = scripts[DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME]
36 |
37 | const nycReportOptions = getNycOptions(processWorkingDirectory)
38 |
39 | const nycFilename = join(nycReportOptions['temp-dir'], 'out.json')
40 |
41 | function saveCoverage(coverage) {
42 | if (!existsSync(nycReportOptions.tempDir)) {
43 | mkdirSync(nycReportOptions.tempDir, { recursive: true })
44 | debug('created folder %s for output coverage', nycReportOptions.tempDir)
45 | }
46 |
47 | writeFileSync(nycFilename, JSON.stringify(coverage, null, 2))
48 | }
49 |
50 | function maybePrintFinalCoverageFiles(folder) {
51 | const jsonReportFilename = join(folder, 'coverage-final.json')
52 | if (!existsSync(jsonReportFilename)) {
53 | debug('Did not find final coverage file %s', jsonReportFilename)
54 | return
55 | }
56 |
57 | debug('Final coverage in %s', jsonReportFilename)
58 | const finalCoverage = JSON.parse(readFileSync(jsonReportFilename, 'utf8'))
59 | const finalCoverageKeys = Object.keys(finalCoverage)
60 | debug(
61 | 'There are %d key(s) in %s',
62 | finalCoverageKeys.length,
63 | jsonReportFilename,
64 | )
65 |
66 | finalCoverageKeys.forEach((key) => {
67 | const s = finalCoverage[key].s || {}
68 | const statements = Object.keys(s)
69 | const totalStatements = statements.length
70 | let coveredStatements = 0
71 | statements.forEach((statementKey) => {
72 | if (s[statementKey]) {
73 | coveredStatements += 1
74 | }
75 | })
76 |
77 | const hasStatements = totalStatements > 0
78 | const allCovered = coveredStatements === totalStatements
79 | const coverageStatus = hasStatements ? (allCovered ? '✅' : '⚠️') : '❓'
80 |
81 | debug(
82 | '%s %s statements covered %d/%d',
83 | coverageStatus,
84 | key,
85 | coveredStatements,
86 | totalStatements,
87 | )
88 | })
89 | }
90 |
91 | const tasks = {
92 | /**
93 | * Clears accumulated code coverage information.
94 | *
95 | * Interactive mode with "cypress open"
96 | * - running a single spec or "Run all specs" needs to reset coverage
97 | * Headless mode with "cypress run"
98 | * - runs EACH spec separately, so we cannot reset the coverage
99 | * or we will lose the coverage from previous specs.
100 | */
101 | resetCoverage(options = {}) {
102 | const { isInteractive, specCovers } = options
103 | debug('reset coverage %o', options)
104 |
105 | if (isInteractive || specCovers) {
106 | debug('reset code coverage in interactive mode')
107 | const coverageMap = istanbul.createCoverageMap({})
108 | saveCoverage(coverageMap)
109 | } else {
110 | debug('not resetting code coverage')
111 | }
112 |
113 | /*
114 | Else:
115 | in headless mode, assume the coverage file was deleted
116 | before the `cypress run` command was called
117 | example: rm -rf .nyc_output || true
118 | */
119 |
120 | return null
121 | },
122 |
123 | /**
124 | * Combines coverage information from single test
125 | * with previously collected coverage.
126 | *
127 | * @param {string} sentCoverage Stringified coverage object sent by the test runner
128 | * @returns {null} Nothing is returned from this task
129 | */
130 | combineCoverage(sentCoverage) {
131 | const coverage = JSON.parse(sentCoverage)
132 | debug('parsed sent coverage')
133 |
134 | fixSourcePaths(coverage)
135 |
136 | const previousCoverage = existsSync(nycFilename)
137 | ? JSON.parse(readFileSync(nycFilename, 'utf8'))
138 | : {}
139 |
140 | // previous code coverage object might have placeholder entries
141 | // for files that we have not seen yet,
142 | // but the user expects to include in the coverage report
143 | // the merge function messes up, so we should remove any placeholder entries
144 | // and re-insert them again when creating the report
145 | removePlaceholders(previousCoverage)
146 |
147 | const coverageMap = istanbul.createCoverageMap(previousCoverage)
148 | coverageMap.merge(coverage)
149 | saveCoverage(coverageMap)
150 | debug('wrote coverage file %s', nycFilename)
151 |
152 | return null
153 | },
154 |
155 | reportSpecCovers(options) {
156 | debug('report spec covers %o', options)
157 | const { specCovers, spec } = options
158 | if (!specCovers) {
159 | return null
160 | }
161 |
162 | const specNumbers = []
163 | const coverage = getCoverage()
164 | const coverageKeys = Object.keys(coverage)
165 | const cwd = process.cwd()
166 | coverageKeys.forEach((key) => {
167 | const fileCoverage = coverage[key]
168 | const sourceRelative = relative(cwd, fileCoverage.path)
169 | const s = fileCoverage.s || {}
170 | const statements = Object.keys(s)
171 | const totalStatements = statements.length
172 | let coveragePercentage = 0
173 | if (totalStatements > 0) {
174 | let covered = 0
175 | statements.forEach((k) => {
176 | const count = s[k]
177 | if (count > 0) {
178 | covered += 1
179 | }
180 | })
181 | coveragePercentage = Math.round((covered / totalStatements) * 100)
182 | }
183 | specNumbers.push({
184 | name: sourceRelative,
185 | covered: coveragePercentage,
186 | })
187 | // console.log('%s - %d', sourceRelative, )
188 | })
189 |
190 | const sorted = sortArray(specNumbers, {
191 | by: ['covered', 'name'],
192 | order: ['desc'],
193 | })
194 |
195 | console.table(`spec ${spec.relative} covers`, sorted)
196 | // console.log('spec %s covers:', spec.relative)
197 |
198 | updateSpecCovers(spec.relative, sorted)
199 |
200 | return null
201 | },
202 |
203 | /**
204 | * Saves coverage information as a JSON file and calls
205 | * NPM script to generate HTML report
206 | */
207 | coverageReport(options = {}) {
208 | debug('coverage report %o', options)
209 | const { specCovers } = options
210 | if (specCovers) {
211 | debug('when using spec covers, skipping final report')
212 | return null
213 | }
214 |
215 | if (!existsSync(nycFilename)) {
216 | console.warn('Cannot find coverage file %s', nycFilename)
217 | console.warn('Skipping coverage report')
218 | return null
219 | }
220 |
221 | showNycInfo(nycFilename)
222 |
223 | const allSourceFilesMissing = checkAllPathsNotFound(nycFilename)
224 | if (allSourceFilesMissing) {
225 | tryFindingLocalFiles(nycFilename)
226 | }
227 |
228 | resolveRelativePaths(nycFilename)
229 |
230 | if (customNycReportScript) {
231 | debug(
232 | 'saving coverage report using script "%s" from package.json, command: %s',
233 | DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME,
234 | customNycReportScript,
235 | )
236 | debug('current working directory is %s', process.cwd())
237 | return execa('npm', ['run', DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME], {
238 | stdio: 'inherit',
239 | })
240 | }
241 |
242 | if (nycReportOptions.all) {
243 | debug('nyc needs to report on all included files')
244 | includeAllFiles(nycFilename, nycReportOptions)
245 | }
246 |
247 | debug('calling NYC reporter with options %o', nycReportOptions)
248 | debug('current working directory is %s', process.cwd())
249 | const NYC = require('nyc')
250 | const nyc = new NYC(nycReportOptions)
251 |
252 | const returnReportFolder = () => {
253 | const reportFolder = nycReportOptions['report-dir']
254 | debug(
255 | 'after reporting, returning the report folder name %s',
256 | reportFolder,
257 | )
258 |
259 | maybePrintFinalCoverageFiles(reportFolder)
260 |
261 | return reportFolder
262 | }
263 | return nyc.report().then(returnReportFolder)
264 | },
265 | }
266 |
267 | /**
268 | * Registers code coverage collection and reporting tasks.
269 | * Sets an environment variable to tell the browser code that it can
270 | * send the coverage.
271 | * @example
272 | ```
273 | // your plugins file
274 | module.exports = (on, config) => {
275 | require('cypress/code-coverage/task')(on, config)
276 | // IMPORTANT to return the config object
277 | // with the any changed environment variables
278 | return config
279 | }
280 | ```
281 | */
282 | function registerCodeCoverageTasks(on, config) {
283 | debug('registering code coverage tasks')
284 | on('task', tasks)
285 |
286 | // set a variable to let the hooks running in the browser
287 | // know that they can send coverage commands
288 | config.env.codeCoverageTasksRegistered = true
289 |
290 | return config
291 | }
292 |
293 | module.exports = registerCodeCoverageTasks
294 |
--------------------------------------------------------------------------------
/support.js:
--------------------------------------------------------------------------------
1 | ///
2 | // @ts-check
3 |
4 | const dayjs = require('dayjs')
5 | var duration = require('dayjs/plugin/duration')
6 | const {
7 | excludeByUser,
8 | filterSupportFilesFromCoverage,
9 | } = require('./support-utils')
10 | const { isPluginDisabled } = require('./common-utils')
11 |
12 | dayjs.extend(duration)
13 |
14 | function getCoverageConfig() {
15 | const env = Cypress.env()
16 | return env.coverage || {}
17 | }
18 |
19 | /**
20 | * Sends collected code coverage object to the backend code
21 | * via "cy.task".
22 | */
23 | const sendCoverage = (coverage, pathname = '/') => {
24 | const config = getCoverageConfig()
25 |
26 | if (!config.quiet) {
27 | logMessage(`Saving code coverage for **${pathname}**`)
28 | }
29 |
30 | let filteredCoverage = coverage
31 |
32 | // console.log({ config })
33 |
34 | // by default we do not filter anything from the code coverage object
35 | // if the user gives a list of patters to filter, we filter the coverage object
36 | if (config.exclude) {
37 | filteredCoverage = excludeByUser(config.exclude, coverage)
38 | } else if (Cypress.spec.specType === 'component') {
39 | filteredCoverage = filterSupportFilesFromCoverage(coverage)
40 | }
41 |
42 | // stringify coverage object for speed
43 | cy.task('combineCoverage', JSON.stringify(filteredCoverage), {
44 | log: false,
45 | })
46 | }
47 |
48 | /**
49 | * Consistently logs the given string to the Command Log
50 | * so the user knows the log message is coming from this plugin.
51 | * @param {string} message String message to log.
52 | */
53 | const logMessage = (message) => {
54 | const logInstance = Cypress.log({
55 | name: 'Coverage',
56 | message,
57 | })
58 | }
59 |
60 | const registerHooks = () => {
61 | let windowCoverageObjects
62 |
63 | const hasE2ECoverage = () => Boolean(windowCoverageObjects.length)
64 |
65 | // @ts-ignore
66 | const hasUnitTestCoverage = () => Boolean(window.__coverage__)
67 |
68 | before(() => {
69 | const config = getCoverageConfig()
70 | let logInstance
71 |
72 | if (!config.quiet) {
73 | // we need to reset the coverage when running
74 | // in the interactive mode, otherwise the counters will
75 | // keep increasing every time we rerun the tests
76 | logInstance = Cypress.log({
77 | name: 'Coverage',
78 | message: ['Reset [@bahmutov/cypress-code-coverage]'],
79 | })
80 | }
81 |
82 | cy.task(
83 | 'resetCoverage',
84 | {
85 | // @ts-ignore
86 | isInteractive: Cypress.config('isInteractive'),
87 | specCovers: Cypress.env('specCovers'),
88 | },
89 | { log: false },
90 | ).then(() => {
91 | if (logInstance) {
92 | logInstance.end()
93 | }
94 | })
95 | })
96 |
97 | beforeEach(() => {
98 | const instrumentScripts = Cypress.env('coverage')?.instrument
99 |
100 | if (instrumentScripts) {
101 | // the user wants Cypress to instrument the application code
102 | // by intercepting the script requests and instrumenting them on the fly
103 |
104 | // https://github.com/istanbuljs/istanbuljs
105 | // @ts-ignore
106 | const { createInstrumenter } = require('istanbul-lib-instrument')
107 |
108 | const instrumenter = createInstrumenter({
109 | esModules: true,
110 | compact: false,
111 | preserveComments: true,
112 | })
113 |
114 | const baseUrl = Cypress.config('baseUrl')
115 | // @ts-ignore
116 | const proxyServer = Cypress.config('proxyServer') + '/'
117 |
118 | cy.intercept(
119 | {
120 | method: 'GET',
121 | resourceType: 'script',
122 | url: instrumentScripts,
123 | },
124 | (req) => {
125 | // remove the cache headers to force the server
126 | // to return the script source code
127 | // Idea: we could cache the instrumented code
128 | // and let the browser receive 304 status code
129 | delete req.headers['if-none-match']
130 | delete req.headers['if-modified-since']
131 |
132 | // @ts-ignore
133 | req.continue((res) => {
134 | const relativeUrl = req.url
135 | // @ts-ignore
136 | .replace(baseUrl, '')
137 | .replace(proxyServer, '')
138 | // console.log('instrumenting', relativeUrl)
139 |
140 | // @ts-ignore
141 | const instrumented = instrumenter.instrumentSync(
142 | res.body,
143 | relativeUrl,
144 | )
145 | res.body = instrumented
146 | return res
147 | })
148 | },
149 | )
150 | }
151 |
152 | // each object will have the coverage and url pathname
153 | // to let the user know the coverage has been collected
154 | windowCoverageObjects = []
155 |
156 | const saveCoverageObject = (win) => {
157 | // if application code has been instrumented, the app iframe "window" has an object
158 | try {
159 | const applicationSourceCoverage = win.__coverage__
160 | if (!applicationSourceCoverage) {
161 | return
162 | }
163 |
164 | if (
165 | Cypress._.find(windowCoverageObjects, {
166 | coverage: applicationSourceCoverage,
167 | })
168 | ) {
169 | // this application code coverage object is already known
170 | // which can happen when combining `window:load` and `before` callbacks
171 | return
172 | }
173 |
174 | if (Cypress.spec.specType === 'component') {
175 | windowCoverageObjects.push({
176 | coverage: applicationSourceCoverage,
177 | pathname: Cypress.spec.relative,
178 | })
179 | } else {
180 | windowCoverageObjects.push({
181 | coverage: applicationSourceCoverage,
182 | pathname: win.location.pathname,
183 | })
184 | }
185 | } catch {
186 | // do nothing, probably cross-origin access
187 | }
188 | }
189 |
190 | // save reference to coverage for each app window loaded in the test
191 | cy.on('window:load', saveCoverageObject)
192 |
193 | // save reference if visiting a page inside a before() hook
194 | cy.window({ log: false }).then(saveCoverageObject)
195 | })
196 |
197 | afterEach(() => {
198 | // save coverage after the test
199 | // because now the window coverage objects have been updated
200 | windowCoverageObjects.forEach((cover) => {
201 | sendCoverage(cover.coverage, cover.pathname)
202 | })
203 |
204 | const taskOptions = { spec: Cypress.spec }
205 | if (Cypress.env('specCovers')) {
206 | taskOptions.specCovers = Cypress.env('specCovers')
207 | }
208 |
209 | const config = getCoverageConfig()
210 | if (!config.quiet) {
211 | cy.log(`**Reporting coverage for** ${Cypress.spec.relative}`)
212 | }
213 | cy.task('reportSpecCovers', taskOptions, { log: false })
214 |
215 | if (!hasE2ECoverage()) {
216 | if (hasUnitTestCoverage()) {
217 | if (!config.quiet) {
218 | logMessage(`👉 Only found unit test code coverage.`)
219 | }
220 | } else {
221 | const expectBackendCoverageOnly = Cypress._.get(
222 | Cypress.env('codeCoverage'),
223 | 'expectBackendCoverageOnly',
224 | false,
225 | )
226 | if (!expectBackendCoverageOnly) {
227 | logMessage(`
228 | ⚠️ Could not find any coverage information in your application
229 | by looking at the window coverage object.
230 | Did you forget to instrument your application?
231 | See [code-coverage#instrument-your-application](https://github.com/cypress-io/code-coverage#instrument-your-application)
232 | `)
233 | }
234 | }
235 | }
236 | })
237 |
238 | after(function collectBackendCoverage() {
239 | let runningEndToEndTests, isIntegrationSpec
240 |
241 | try {
242 | // I wish I could fail the tests if there is no code coverage information
243 | // but throwing an error here does not fail the test run due to
244 | // https://github.com/cypress-io/cypress/issues/2296
245 |
246 | // there might be server-side code coverage information
247 | // we should grab it once after all tests finish
248 | // @ts-ignore
249 | const baseUrl = Cypress.config('baseUrl') || cy.state('window').origin
250 | // @ts-ignore
251 | runningEndToEndTests = baseUrl !== Cypress.config('proxyUrl')
252 | const specType = Cypress._.get(Cypress.spec, 'specType', 'integration')
253 | isIntegrationSpec = specType === 'integration'
254 | } catch {
255 | // probably cross-origin request
256 | // cannot access the window
257 | }
258 |
259 | if (runningEndToEndTests && isIntegrationSpec) {
260 | const baseUrl = Cypress.config('baseUrl')
261 | if (baseUrl) {
262 | // can only fetch server-side code coverage if we have a baseUrl
263 |
264 | // we can only request server-side code coverage
265 | // if we are running end-to-end tests,
266 | // otherwise where do we send the request?
267 | const url = Cypress._.get(
268 | Cypress.env('codeCoverage'),
269 | 'url',
270 | '/__coverage__',
271 | )
272 | cy.request({
273 | url,
274 | log: false,
275 | failOnStatusCode: false,
276 | })
277 | .then((r) => {
278 | return Cypress._.get(r, 'body.coverage', null)
279 | })
280 | .then((coverage) => {
281 | if (!coverage) {
282 | // we did not get code coverage - this is the
283 | // original failed request
284 | const expectBackendCoverageOnly = Cypress._.get(
285 | Cypress.env('codeCoverage'),
286 | 'expectBackendCoverageOnly',
287 | false,
288 | )
289 | if (expectBackendCoverageOnly) {
290 | throw new Error(
291 | `Expected to collect backend code coverage from ${url}`,
292 | )
293 | } else {
294 | // we did not really expect to collect the backend code coverage
295 | return
296 | }
297 | }
298 | sendCoverage(coverage, 'backend')
299 | })
300 | }
301 | }
302 | })
303 |
304 | after(function mergeUnitTestCoverage() {
305 | // collect and merge frontend coverage
306 |
307 | // if spec bundle has been instrumented (using Cypress preprocessor)
308 | // then we will have unit test coverage
309 | // NOTE: spec iframe is NOT reset between the tests, so we can grab
310 | // the coverage information only once after all tests have finished
311 | // @ts-ignore
312 | if (window.__coverage__) {
313 | const unitTestCoverage = filterSupportFilesFromCoverage(
314 | // @ts-ignore
315 | window.__coverage__,
316 | )
317 | sendCoverage(unitTestCoverage, 'component tests')
318 | }
319 | })
320 |
321 | after(function generateReport() {
322 | const config = getCoverageConfig()
323 | let logInstance
324 |
325 | if (!config.quiet) {
326 | // when all tests finish, lets generate the coverage report
327 | logInstance = Cypress.log({
328 | name: 'Coverage',
329 | message: ['Generating report [@bahmutov/cypress-code-coverage]'],
330 | })
331 | }
332 |
333 | const options = {
334 | specCovers: Cypress.env('specCovers'),
335 | }
336 | cy.task('coverageReport', options, {
337 | timeout: dayjs.duration(3, 'minutes').asMilliseconds(),
338 | log: false,
339 | }).then((coverageReportFolder) => {
340 | if (logInstance) {
341 | logInstance.set('consoleProps', () => ({
342 | 'coverage report folder': coverageReportFolder,
343 | }))
344 | logInstance.end()
345 | }
346 | return coverageReportFolder
347 | })
348 | })
349 | }
350 |
351 | // to disable code coverage commands and save time
352 | // pass environment variable coverage=false
353 | // cypress run --env coverage=false
354 | // or
355 | // CYPRESS_coverage=false cypress run
356 | // see https://on.cypress.io/environment-variables
357 |
358 | // to avoid "coverage" env variable being case-sensitive, convert to lowercase
359 | const cyEnvs = Cypress._.mapKeys(Cypress.env(), (value, key) =>
360 | key.toLowerCase(),
361 | )
362 |
363 | const pluginDisabled = isPluginDisabled(cyEnvs)
364 |
365 | if (pluginDisabled) {
366 | console.log('Skipping code coverage hooks')
367 | } else if (Cypress.env('codeCoverageTasksRegistered') !== true) {
368 | // register a hook just to log a message
369 | before(() => {
370 | logMessage(`
371 | ⚠️ Code coverage tasks were not registered by the plugins file.
372 | See [support issue](https://github.com/cypress-io/code-coverage/issues/179)
373 | for possible workarounds.
374 | `)
375 | })
376 | } else {
377 | registerHooks()
378 | }
379 |
--------------------------------------------------------------------------------
/task-utils.js:
--------------------------------------------------------------------------------
1 | // helper functions to use from "task.js" plugins code
2 | // that need access to the file system
3 |
4 | // @ts-check
5 | ///
6 | const { readFileSync, writeFileSync, existsSync } = require('fs')
7 | const { isAbsolute, resolve, join } = require('path')
8 | const debug = require('debug')('code-coverage')
9 | const chalk = require('chalk')
10 | const globby = require('globby')
11 | const yaml = require('js-yaml')
12 | const {
13 | combineNycOptions,
14 | defaultNycOptions,
15 | fileCoveragePlaceholder,
16 | } = require('./common-utils')
17 |
18 | function readNycOptions(workingDirectory) {
19 | const pkgFilename = join(workingDirectory, 'package.json')
20 | const pkg = existsSync(pkgFilename)
21 | ? JSON.parse(readFileSync(pkgFilename, 'utf8'))
22 | : {}
23 | const pkgNycOptions = pkg.nyc || {}
24 |
25 | const nycrcFilename = join(workingDirectory, '.nycrc')
26 | const nycrc = existsSync(nycrcFilename)
27 | ? JSON.parse(readFileSync(nycrcFilename, 'utf8'))
28 | : {}
29 |
30 | const nycrcJsonFilename = join(workingDirectory, '.nycrc.json')
31 | const nycrcJson = existsSync(nycrcJsonFilename)
32 | ? JSON.parse(readFileSync(nycrcJsonFilename, 'utf8'))
33 | : {}
34 |
35 | const nycrcYamlFilename = join(workingDirectory, '.nycrc.yaml')
36 | let nycrcYaml = {}
37 | if (existsSync(nycrcYamlFilename)) {
38 | try {
39 | nycrcYaml = yaml.safeLoad(readFileSync(nycrcYamlFilename, 'utf8'))
40 | } catch (error) {
41 | throw new Error(`Failed to load .nycrc.yaml: ${error.message}`)
42 | }
43 | }
44 |
45 | const nycrcYmlFilename = join(workingDirectory, '.nycrc.yml')
46 | let nycrcYml = {}
47 | if (existsSync(nycrcYmlFilename)) {
48 | try {
49 | nycrcYml = yaml.safeLoad(readFileSync(nycrcYmlFilename, 'utf8'))
50 | } catch (error) {
51 | throw new Error(`Failed to load .nycrc.yml: ${error.message}`)
52 | }
53 | }
54 |
55 | const nycConfigFilename = join(workingDirectory, 'nyc.config.js')
56 | let nycConfig = {}
57 | if (existsSync(nycConfigFilename)) {
58 | try {
59 | nycConfig = require(nycConfigFilename)
60 | } catch (error) {
61 | throw new Error(`Failed to load nyc.config.js: ${error.message}`)
62 | }
63 | }
64 |
65 | const nycOptions = combineNycOptions(
66 | defaultNycOptions,
67 | nycrc,
68 | nycrcJson,
69 | nycrcYaml,
70 | nycrcYml,
71 | nycConfig,
72 | pkgNycOptions,
73 | )
74 | debug('combined NYC options %o', nycOptions)
75 |
76 | return nycOptions
77 | }
78 |
79 | function getNycOptions(workingDirectory) {
80 | if (!workingDirectory) {
81 | workingDirectory = process.cwd()
82 | }
83 |
84 | // https://github.com/istanbuljs/nyc#common-configuration-options
85 | const nycReportOptions = readNycOptions(workingDirectory)
86 |
87 | if (nycReportOptions.exclude && !Array.isArray(nycReportOptions.exclude)) {
88 | console.error('NYC options: %o', nycReportOptions)
89 | throw new Error('Expected "exclude" to by an array')
90 | }
91 |
92 | if (nycReportOptions['temp-dir']) {
93 | nycReportOptions['temp-dir'] = resolve(nycReportOptions['temp-dir'])
94 | } else {
95 | nycReportOptions['temp-dir'] = join(workingDirectory, '.nyc_output')
96 | }
97 |
98 | nycReportOptions.tempDir = nycReportOptions['temp-dir']
99 |
100 | if (nycReportOptions['report-dir']) {
101 | nycReportOptions['report-dir'] = resolve(nycReportOptions['report-dir'])
102 | }
103 | // seems nyc API really is using camel cased version
104 | nycReportOptions.reportDir = nycReportOptions['report-dir']
105 |
106 | return nycReportOptions
107 | }
108 |
109 | function getNycReportFilename(workingDirectory) {
110 | const nycReportOptions = getNycOptions(workingDirectory)
111 |
112 | const nycFilename = join(nycReportOptions['temp-dir'], 'out.json')
113 | return nycFilename
114 | }
115 |
116 | function checkAllPathsNotFound(nycFilename) {
117 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
118 |
119 | const coverageKeys = Object.keys(nycCoverage)
120 | if (!coverageKeys.length) {
121 | debug('⚠️ file %s has no coverage information', nycFilename)
122 | return
123 | }
124 |
125 | const allFilesAreMissing = coverageKeys.every((key, k) => {
126 | const coverage = nycCoverage[key]
127 | return !existsSync(coverage.path)
128 | })
129 |
130 | debug(
131 | 'in file %s all files are not found? %o',
132 | nycFilename,
133 | allFilesAreMissing,
134 | )
135 | return allFilesAreMissing
136 | }
137 |
138 | /**
139 | * A small debug utility to inspect paths saved in NYC output JSON file
140 | */
141 | function showNycInfo(nycFilename) {
142 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
143 |
144 | const coverageKeys = Object.keys(nycCoverage)
145 | if (!coverageKeys.length) {
146 | console.error(
147 | '⚠️ file %s has no coverage information',
148 | chalk.yellow(nycFilename),
149 | )
150 | console.error(
151 | 'Did you forget to instrument your web application? Read %s',
152 | chalk.blue(
153 | 'https://github.com/cypress-io/code-coverage#instrument-your-application',
154 | ),
155 | )
156 | return
157 | }
158 | debug('NYC file %s has %d key(s)', nycFilename, coverageKeys.length)
159 |
160 | const maxPrintKeys = 3
161 | const showKeys = coverageKeys.slice(0, maxPrintKeys)
162 |
163 | showKeys.forEach((key, k) => {
164 | const coverage = nycCoverage[key]
165 |
166 | // printing a few found keys and file paths from the coverage file
167 | // will make debugging any problems much much easier
168 | if (k < maxPrintKeys) {
169 | debug('%d key %s file path %s', k + 1, key, coverage.path)
170 | }
171 | })
172 | }
173 |
174 | /**
175 | * Looks at all coverage objects in the given JSON coverage file
176 | * and if the file is relative, and exists, changes its path to
177 | * be absolute.
178 | */
179 | function resolveRelativePaths(nycFilename) {
180 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
181 |
182 | const coverageKeys = Object.keys(nycCoverage)
183 | if (!coverageKeys.length) {
184 | debug('⚠️ file %s has no coverage information', nycFilename)
185 | return
186 | }
187 | debug('NYC file %s has %d key(s)', nycFilename, coverageKeys.length)
188 |
189 | let changed
190 |
191 | coverageKeys.forEach((key, k) => {
192 | const coverage = nycCoverage[key]
193 |
194 | if (!coverage.path) {
195 | debug('key %s does not have path', key)
196 | return
197 | }
198 |
199 | if (!isAbsolute(coverage.path)) {
200 | if (existsSync(coverage.path)) {
201 | debug('resolving path %s', coverage.path)
202 | coverage.path = resolve(coverage.path)
203 | changed = true
204 | }
205 | return
206 | }
207 |
208 | // path is absolute, let's check if it exists
209 | if (!existsSync(coverage.path)) {
210 | debug('⚠️ cannot find file %s with hash %s', coverage.path, coverage.hash)
211 | }
212 | })
213 |
214 | if (changed) {
215 | debug('resolveRelativePaths saving updated file %s', nycFilename)
216 | debug('there are %d keys in the file', coverageKeys.length)
217 | writeFileSync(
218 | nycFilename,
219 | JSON.stringify(nycCoverage, null, 2) + '\n',
220 | 'utf8',
221 | )
222 | }
223 | }
224 |
225 | /**
226 | * @param {string[]} filepaths
227 | * @returns {string | undefined} common prefix that corresponds to current folder
228 | */
229 | function findCommonRoot(filepaths) {
230 | if (!filepaths.length) {
231 | debug('cannot find common root without any files')
232 | return
233 | }
234 |
235 | // assuming / as file separator
236 | const splitParts = filepaths.map((name) => name.split('/'))
237 | const lengths = splitParts.map((arr) => arr.length)
238 | const shortestLength = Math.min.apply(null, lengths)
239 | debug('shorted file path has %d parts', shortestLength)
240 |
241 | const cwd = process.cwd()
242 | let commonPrefix = []
243 | let foundCurrentFolder
244 |
245 | for (let k = 0; k < shortestLength; k += 1) {
246 | const part = splitParts[0][k]
247 | const prefix = commonPrefix.concat(part).join('/')
248 | debug('testing prefix %o', prefix)
249 | const allFilesStart = filepaths.every((name) => name.startsWith(prefix))
250 | if (!allFilesStart) {
251 | debug('stopped at non-common prefix %s', prefix)
252 | break
253 | }
254 |
255 | commonPrefix.push(part)
256 |
257 | const removedPrefixNames = filepaths.map((filepath) =>
258 | filepath.slice(prefix.length),
259 | )
260 | debug('removedPrefix %o', removedPrefixNames)
261 |
262 | let foundCount = 0
263 | removedPrefixNames.forEach((filepath) => {
264 | if (existsSync(join(cwd, filepath))) {
265 | foundCount += 1
266 | }
267 | })
268 | const foundMostPaths = foundCount >= removedPrefixNames.length / 2
269 | debug('found %d files out of %d', foundCount, removedPrefixNames.length)
270 | debug('found most files found at "%s"? %o', prefix, foundMostPaths)
271 | if (foundMostPaths) {
272 | foundCurrentFolder = prefix
273 | debug(
274 | 'found prefix that matches current folder: "%s"',
275 | foundCurrentFolder,
276 | )
277 | break
278 | }
279 | }
280 |
281 | return foundCurrentFolder
282 | }
283 |
284 | function tryFindingLocalFiles(nycFilename) {
285 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
286 | const coverageKeys = Object.keys(nycCoverage)
287 | const filenames = coverageKeys.map((key) => nycCoverage[key].path)
288 | const commonFolder = findCommonRoot(filenames)
289 | if (commonFolder === undefined) {
290 | debug('could not find common folder %s', commonFolder)
291 | return
292 | }
293 | const cwd = process.cwd()
294 | debug(
295 | 'found common folder %s that matches current working directory %s',
296 | commonFolder,
297 | cwd,
298 | )
299 | let changed
300 |
301 | if (commonFolder === '') {
302 | // we just need to prefix the paths with the current folder
303 | coverageKeys.forEach((key) => {
304 | const filePath = nycCoverage[key].path
305 | const to = join(cwd, filePath)
306 | // ? Do we need to replace the "key" in the coverage object or can we just replace the "path"?
307 | nycCoverage[key].path = to
308 | debug('formed %s -> %s', filePath, to)
309 | changed = true
310 | })
311 | } else {
312 | // we need to replace part of the paths with the current folder
313 | const length = commonFolder.length
314 | coverageKeys.forEach((key) => {
315 | const from = nycCoverage[key].path
316 | if (from.startsWith(commonFolder)) {
317 | const to = join(cwd, from.slice(length))
318 | // ? Do we need to replace the "key" in the coverage object or can we just replace the "path"?
319 | nycCoverage[key].path = to
320 | debug('replaced %s -> %s', from, to)
321 | changed = true
322 | }
323 | })
324 | }
325 |
326 | if (changed) {
327 | debug('tryFindingLocalFiles saving updated file %s', nycFilename)
328 | debug('there are %d keys in the file', coverageKeys.length)
329 | writeFileSync(
330 | nycFilename,
331 | JSON.stringify(nycCoverage, null, 2) + '\n',
332 | 'utf8',
333 | )
334 | }
335 | }
336 |
337 | /**
338 | * Tries to find source files to be included in the final coverage report
339 | * using NYC options: extension list, include and exclude.
340 | */
341 | function findSourceFiles(nycOptions) {
342 | debug('include all files options: %o', {
343 | all: nycOptions.all,
344 | include: nycOptions.include,
345 | exclude: nycOptions.exclude,
346 | extension: nycOptions.extension,
347 | })
348 |
349 | if (!Array.isArray(nycOptions.extension)) {
350 | console.error(
351 | 'Expected NYC "extension" option to be a list of file extensions',
352 | )
353 | console.error(nycOptions)
354 | return []
355 | }
356 |
357 | let patterns = []
358 | if (Array.isArray(nycOptions.include)) {
359 | patterns = patterns.concat(nycOptions.include)
360 | } else if (typeof nycOptions.include === 'string') {
361 | patterns.push(nycOptions.include)
362 | } else {
363 | debug('using default list of extensions')
364 | nycOptions.extension.forEach((extension) => {
365 | patterns.push('**/*' + extension)
366 | })
367 | }
368 |
369 | if (Array.isArray(nycOptions.exclude)) {
370 | const negated = nycOptions.exclude.map((s) => '!' + s)
371 | patterns = patterns.concat(negated)
372 | } else if (typeof nycOptions.exclude === 'string') {
373 | patterns.push('!' + nycOptions.exclude)
374 | }
375 | // always exclude node_modules
376 | // https://github.com/istanbuljs/nyc#including-files-within-node_modules
377 | patterns.push('!**/node_modules/**')
378 | // and exclude the Cypress config file
379 | patterns.push('!cypress.config.*')
380 |
381 | debug('searching files to include using patterns %o', patterns)
382 |
383 | const allFiles = globby.sync(patterns, { absolute: true })
384 | return allFiles
385 | }
386 | /**
387 | * If the website or unit tests did not load ALL files we need to
388 | * include, then we should include the missing files ourselves
389 | * before generating the report.
390 | *
391 | * @see https://github.com/cypress-io/code-coverage/issues/207
392 | */
393 | function includeAllFiles(nycFilename, nycOptions) {
394 | if (!nycOptions.all) {
395 | debug('NYC "all" option is not set, skipping including all files')
396 | return
397 | }
398 |
399 | const allFiles = findSourceFiles(nycOptions)
400 | if (debug.enabled) {
401 | debug('found %d file(s)', allFiles.length)
402 | console.error(allFiles.join('\n'))
403 | }
404 | if (!allFiles.length) {
405 | debug('no files found, hoping for the best')
406 | return
407 | }
408 |
409 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
410 | const coverageKeys = Object.keys(nycCoverage)
411 | const coveredPaths = coverageKeys.map((key) =>
412 | nycCoverage[key].path.replace(/\\/g, '/'),
413 | )
414 |
415 | debug('coverage has %d record(s)', coveredPaths.length)
416 | // report on first couple of entries
417 | if (debug.enabled) {
418 | console.error('coverage has the following first paths')
419 | console.error(coveredPaths.slice(0, 4).join('\n'))
420 | }
421 |
422 | let changed
423 | allFiles.forEach((fullPath) => {
424 | if (coveredPaths.includes(fullPath)) {
425 | // all good, this file exists in coverage object
426 | return
427 | }
428 | debug('adding empty coverage for file %s', fullPath)
429 | changed = true
430 | // insert placeholder object for now
431 | const placeholder = fileCoveragePlaceholder(fullPath)
432 | nycCoverage[fullPath] = placeholder
433 | })
434 |
435 | if (changed) {
436 | debug('includeAllFiles saving updated file %s', nycFilename)
437 | debug('there are %d keys in the file', Object.keys(nycCoverage).length)
438 |
439 | writeFileSync(
440 | nycFilename,
441 | JSON.stringify(nycCoverage, null, 2) + '\n',
442 | 'utf8',
443 | )
444 | }
445 | }
446 |
447 | function getCoverage() {
448 | const jsonFilename = join('.', '.nyc_output', 'out.json')
449 | const json = JSON.parse(readFileSync(jsonFilename, 'utf8'))
450 | return json
451 | }
452 |
453 | function updateSpecCovers(specName, specCovers) {
454 | const jsonFilename = join('.', '.nyc_output', 'spec-covers.json')
455 | const json = existsSync(jsonFilename)
456 | ? JSON.parse(readFileSync(jsonFilename, 'utf8'))
457 | : {}
458 | json[specName] = specCovers
459 | const text = JSON.stringify(json, null, 2) + '\n'
460 | writeFileSync(jsonFilename, text, 'utf8')
461 | return json
462 | }
463 |
464 | module.exports = {
465 | showNycInfo,
466 | resolveRelativePaths,
467 | checkAllPathsNotFound,
468 | tryFindingLocalFiles,
469 | readNycOptions,
470 | getNycOptions,
471 | getNycReportFilename,
472 | includeAllFiles,
473 | getCoverage,
474 | updateSpecCovers,
475 | }
476 |
--------------------------------------------------------------------------------