├── .babelrc
├── .circleci
└── config.yml
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── config.yml
│ └── feature_request.md
└── workflows
│ ├── add-to-triage-board.yml
│ ├── snyk_sca_scan.yaml
│ ├── snyk_static_analysis_scan.yaml
│ └── triage_closed_issue_comment.yml
├── .gitignore
├── .nycrc.json
├── .prettierrc.json
├── .releaserc
├── LICENSE.md
├── README.md
├── common-utils.js
├── cypress-backend.json
├── cypress.config.js
├── cypress
├── README.md
├── e2e
│ ├── combine.cy.js
│ ├── filtering.cy.js
│ ├── fix-source-paths.cy.js
│ └── merge.cy.js
├── fixtures
│ ├── coverage.json
│ └── example.json
├── index.html
├── plugins
│ └── index.js
└── support
│ └── e2e.js
├── images
├── coverage.jpg
├── expect-backend.png
├── gui.png
├── instrumented-code.png
├── warning.png
└── window-coverage-object.png
├── middleware
├── express.js
├── hapi.js
└── nextjs.js
├── package-lock.json
├── package.json
├── plugins.js
├── renovate.json
├── support-utils.js
├── support.js
├── task-utils.js
├── task.d.ts
├── task.js
├── test-apps
├── all-files
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ ├── not-covered.js
│ ├── package.json
│ └── second.js
├── backend
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── package.json
│ └── server
│ │ ├── index.html
│ │ └── server.js
├── batch-send-coverage
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ ├── package-lock.json
│ ├── package.json
│ ├── second.js
│ └── third.js
├── before-all-visit
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ └── package.json
├── before-each-visit
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── fixtures
│ │ │ └── example.json
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ └── package.json
├── cra-e2e-and-ct
│ ├── .gitignore
│ ├── README.md
│ ├── cypress.config.ts
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.ts
│ │ ├── fixtures
│ │ │ └── example.json
│ │ └── support
│ │ │ ├── commands.ts
│ │ │ ├── component-index.html
│ │ │ ├── component.ts
│ │ │ └── e2e.ts
│ ├── package-lock.json
│ ├── package.json
│ ├── public
│ │ ├── favicon.ico
│ │ ├── index.html
│ │ ├── logo192.png
│ │ ├── logo512.png
│ │ ├── manifest.json
│ │ └── robots.txt
│ ├── src
│ │ ├── App.css
│ │ ├── App.test.tsx
│ │ ├── App.tsx
│ │ ├── components
│ │ │ ├── Button.cy.tsx
│ │ │ ├── Button.tsx
│ │ │ ├── Stepper.cy.tsx
│ │ │ └── Stepper.tsx
│ │ ├── index.css
│ │ ├── index.tsx
│ │ ├── logo.svg
│ │ ├── react-app-env.d.ts
│ │ ├── reportWebVitals.ts
│ │ └── setupTests.ts
│ └── tsconfig.json
├── exclude-files
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ ├── package.json
│ └── second.js
├── frontend
│ ├── .babelrc
│ ├── README.md
│ ├── about.html
│ ├── about.js
│ ├── app.js
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── e2e.js
│ ├── index.html
│ ├── package.json
│ └── unit.js
├── fullstack
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── images
│ │ └── fullstack.png
│ ├── index.html
│ ├── main.js
│ ├── package-lock.json
│ ├── package.json
│ ├── server
│ │ └── server.js
│ └── string-utils.js
├── multiple-backends
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── package.json
│ └── server
│ │ ├── index.html
│ │ ├── server-3003.js
│ │ └── server-3004.js
├── one-spec
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ ├── spec-a.cy.js
│ │ │ └── spec-b.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ ├── package-lock.json
│ └── package.json
├── redirect
│ ├── .babelrc
│ ├── .nycrc.json
│ ├── README.md
│ ├── app.js
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── index.html
│ ├── package.json
│ └── utils.js
├── same-folder
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ └── fixtures
│ │ │ └── example.json
│ ├── index.html
│ ├── main.js
│ ├── package-lock.json
│ ├── package.json
│ ├── plugins.js
│ ├── spec.js
│ ├── support.js
│ └── unit-utils.js
├── support-files
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ ├── commands.js
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.js
│ └── package.json
├── ts-example
│ ├── .babelrc
│ ├── README.md
│ ├── calc.ts
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ └── spec.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── index.html
│ ├── main.ts
│ ├── package-lock.json
│ └── package.json
├── unit-tests-js
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ │ ├── e2e
│ │ │ ├── spec-a.cy.js
│ │ │ └── spec-b.cy.js
│ │ ├── plugins
│ │ │ └── index.js
│ │ └── support
│ │ │ └── e2e.js
│ ├── package.json
│ └── src
│ │ └── utils
│ │ ├── math.js
│ │ └── misc.js
└── use-webpack
│ ├── .babelrc
│ ├── README.md
│ ├── cypress.config.js
│ ├── cypress
│ ├── e2e
│ │ └── spec.cy.js
│ ├── plugins
│ │ └── index.js
│ └── support
│ │ └── e2e.js
│ ├── index.html
│ ├── package.json
│ ├── src
│ ├── calc.js
│ └── index.js
│ └── webpack.config.js
└── use-babelrc.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/.circleci/config.yml:
--------------------------------------------------------------------------------
1 | # https://circleci.com/docs/2.0/configuration-reference/
2 | version: 2.1
3 | orbs:
4 | # https://github.com/cypress-io/circleci-orb
5 | cypress: cypress-io/cypress@3.0.0 # used to run e2e tests
6 | win: circleci/windows@5.1.0 # run a test job on Windows
7 |
8 | jobs:
9 | lint:
10 | description: Checks the code formatting
11 | docker:
12 | - image: cimg/node:22.14.0
13 | environment:
14 | # we don't need Cypress to check code style
15 | CYPRESS_INSTALL_BINARY: '0'
16 | steps:
17 | - attach_workspace:
18 | at: ~/
19 | - run:
20 | name: Code style check 🧹
21 | command: npm run format:check
22 | - run: npm run check:markdown
23 |
24 | install_and_persist:
25 | executor: cypress/default
26 | steps:
27 | - cypress/install
28 | - run: npm run check:markdown
29 | - persist_to_workspace:
30 | paths:
31 | - .cache/Cypress
32 | - project
33 | root: ~/
34 |
35 | windows_test:
36 | executor:
37 | # executor comes from the "windows" orb
38 | name: win/default
39 | shell: bash.exe
40 | steps:
41 | - checkout
42 | - run:
43 | name: Install node 20
44 | command: nvm install 20.12.1
45 | - run:
46 | name: Use node 20
47 | command: nvm use 20.12.1
48 | - run:
49 | name: Install deps for code coverage
50 | command: npm ci
51 | - cypress/run-tests:
52 | # no-workspace: true
53 | start-command: npm run start:windows --prefix test-apps/all-files
54 | # wait-on: 'http://localhost:1234'
55 | cypress-command: npx cypress run --project test-apps/all-files
56 | # store screenshots and videos
57 | # store_artifacts: true
58 | - run:
59 | # make sure the examples captures 100% of code
60 | name: Verify Code Coverage
61 | command: npm run coverage:verify
62 | working_directory: test-apps/all-files
63 | - run:
64 | name: Check code coverage files 📈
65 | # we will check the final coverage report
66 | # to make sure it only has files we are interested in
67 | # because there are files covered at 0 in the report
68 | command: npm i -D check-code-coverage && npm run coverage:check-files
69 | working_directory: test-apps/all-files
70 |
71 | publish:
72 | description: Publishes the new version of the plugin to NPM
73 | docker:
74 | - image: cimg/node:22.14.0
75 | environment:
76 | # we don't need Cypress to do the release
77 | CYPRESS_INSTALL_BINARY: '0'
78 | # trick semantic-release into thinking this is NOT a pull request
79 | # (under the hood the module env-ci is used to check if this is a PR)
80 | CIRCLE_PR_NUMBER: ''
81 | CIRCLE_PULL_REQUEST: ''
82 | CI_PULL_REQUEST: ''
83 | steps:
84 | - attach_workspace:
85 | at: ~/
86 | - run: npm run semantic-release
87 |
88 | cyrun:
89 | docker:
90 | - image: cypress/base:16.18.1
91 | parameters:
92 | jobname:
93 | type: string
94 | steps:
95 | - attach_workspace:
96 | at: ~/
97 | - run:
98 | command: npm run test
99 | working_directory: test-apps/<< parameters.jobname >>
100 | - store_artifacts:
101 | path: test-apps/<< parameters.jobname >>/coverage
102 | - run:
103 | name: Verify Code Coverage
104 | command: npm run coverage:verify
105 | working_directory: test-apps/<< parameters.jobname >>
106 | - run:
107 | name: Check code coverage files 📈
108 | # we will check the final coverage report
109 | # to make sure it only has files we are interested in
110 | # because there are files covered at 0 in the report
111 | command: npm run coverage:check-files
112 | working_directory: test-apps/<< parameters.jobname >>
113 |
114 | test-code-coverage-plugin:
115 | docker:
116 | - image: cypress/base:16.18.1
117 | steps:
118 | - attach_workspace:
119 | at: ~/
120 | - run:
121 | command: npm run test
122 | - store_artifacts:
123 | path: coverage
124 | - run:
125 | name: Verify Code Coverage
126 | command: npm run coverage:verify
127 |
128 | workflows:
129 | build:
130 | jobs:
131 | - install_and_persist
132 | - lint:
133 | requires:
134 | - install_and_persist
135 |
136 | - test-code-coverage-plugin:
137 | requires:
138 | - install_and_persist
139 |
140 | - cyrun:
141 | name: test-<< matrix.jobname>>
142 | requires:
143 | - install_and_persist
144 | matrix:
145 | parameters:
146 | jobname:
147 | - all-files
148 | - backend
149 | - batch-send-coverage
150 | - before-all-visit
151 | - before-each-visit
152 | - cra-e2e-and-ct
153 | - exclude-files
154 | - frontend
155 | - fullstack
156 | - multiple-backends
157 | - one-spec
158 | - same-folder
159 | - support-files
160 | - ts-example
161 | - unit-tests-js
162 | - use-webpack
163 | - redirect
164 | - windows_test
165 | - publish:
166 | filters:
167 | branches:
168 | only:
169 | - master
170 | - beta
171 | - next
172 | - dev
173 | requires:
174 | - lint
175 | - test-code-coverage-plugin
176 | - test-all-files
177 | - test-backend
178 | - test-batch-send-coverage
179 | - test-before-all-visit
180 | - test-before-each-visit
181 | - test-cra-e2e-and-ct
182 | - test-exclude-files
183 | - test-frontend
184 | - test-fullstack
185 | - test-multiple-backends
186 | - test-one-spec
187 | - test-same-folder
188 | - test-support-files
189 | - test-ts-example
190 | - test-unit-tests-js
191 | - test-use-webpack
192 | - test-redirect
193 | - windows_test
194 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 | **Logs and screenshots**
10 | Please provide debug logs by running Cypress from the terminal with `DEBUG=code-coverage` environment variable set. See the [Debugging](https://github.com/cypress-io/code-coverage#debugging) section of the README file.
11 |
12 | **Versions**
13 |
14 | - What is this plugin's version? If this is NOT the latest [released version](https://github.com/cypress-io/code-coverage/releases), can you try the latest version, please?
15 | - If the plugin worked before in version X but stopped after upgrading to version Y, please try the [released versions](https://github.com/cypress-io/code-coverage/releases) between X and Y to see where the breaking change was.
16 | - What is the Cypress version?
17 | - What is your operating system?
18 | - What is the shell?
19 | - What is the Node version?
20 | - What is the NPM version?
21 | - How do you instrument your application? Cypress [does not instrument web application code](https://github.com/cypress-io/code-coverage#instrument-your-application), so you must do it yourself.
22 | - When running tests, if you open the web application in a regular browser and open DevTools, do you see `window.__coverage__` object? Can you paste a screenshot?
23 | - Is there a `.nyc_output` folder? Is there a `.nyc_output/out.json` file? Is it empty? Can you paste at least part of it so we can see the keys and file paths?
24 | - Do you have any custom NYC settings in `package.json` (`nyc` object) or in other [NYC config files](https://github.com/istanbuljs/nyc#configuration-files)?
25 | - Do you run Cypress tests in a Docker container?
26 |
27 | **Describe the bug**
28 | A clear and concise description of what the bug is.
29 |
30 | **Link to the repo**
31 | Bugs with a reproducible example, like an open-source repo showing the bug, are the most likely to be resolved.
32 |
33 | **Example**
34 | See [#217](https://github.com/cypress-io/code-coverage/issues/217) that is an excellent bug report example
35 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: true
2 | contact_links:
3 | - name: Cypress Community Forum
4 | url: https://on.cypress.io/chat
5 | about: Please ask and answer questions here.
6 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/add-to-triage-board.yml:
--------------------------------------------------------------------------------
1 | name: 'Add issue/PR to Triage Board'
2 | on:
3 | issues:
4 | types:
5 | - opened
6 | pull_request_target:
7 | types:
8 | - opened
9 | jobs:
10 | add-to-triage-project-board:
11 | uses: cypress-io/cypress/.github/workflows/triage_add_to_project.yml@develop
12 | secrets: inherit
13 |
--------------------------------------------------------------------------------
/.github/workflows/snyk_sca_scan.yaml:
--------------------------------------------------------------------------------
1 | name: Snyk Software Composition Analysis Scan
2 | # This git workflow leverages Snyk actions to perform a Software Composition
3 | # Analysis scan on our Opensource libraries upon Pull Requests to Master &
4 | # Develop branches. We use this as a control to prevent vulnerable packages
5 | # from being introduced into the codebase.
6 | on:
7 | pull_request:
8 | branches:
9 | - master
10 | - develop
11 | jobs:
12 | Snyk_SCA_Scan:
13 | runs-on: ubuntu-latest
14 | strategy:
15 | matrix:
16 | node-version: [20.x]
17 | steps:
18 | - uses: actions/checkout@v4
19 | - name: Setting up Node
20 | uses: actions/setup-node@v3
21 | with:
22 | node-version: ${{ matrix.node-version }}
23 | - name: Installing snyk-delta and dependencies
24 | run: npm i -g snyk-delta
25 | - uses: snyk/actions/setup@master
26 | - name: Perform SCA Scan
27 | continue-on-error: false
28 | run: |
29 | snyk test --all-projects --detection-depth=4 --exclude=docker,Dockerfile,test-apps --severity-threshold=critical
30 | env:
31 | SNYK_TOKEN: ${{ secrets.SNYK_API_TOKEN }}
32 |
--------------------------------------------------------------------------------
/.github/workflows/snyk_static_analysis_scan.yaml:
--------------------------------------------------------------------------------
1 | name: Snyk Static Analysis Scan
2 | # This SAST (Static Application Security Testing) scan is used to scan
3 | # our first-party code for security vulnerabilities
4 | on:
5 | pull_request:
6 | branches:
7 | - master
8 | - develop
9 | jobs:
10 | Snyk_SAST_Scan:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v4
14 | - uses: snyk/actions/setup@master
15 | - name: Perform Static Analysis Test
16 | env:
17 | SNYK_TOKEN: ${{ secrets.SNYK_API_TOKEN }}
18 | continue-on-error: true
19 | run: snyk code test --all-projects --strict-out-of-sync=false --detection-depth=6 --exclude=docker,Dockerfile --severity-threshold=high
20 |
--------------------------------------------------------------------------------
/.github/workflows/triage_closed_issue_comment.yml:
--------------------------------------------------------------------------------
1 | name: 'Handle Comment Workflow'
2 | on:
3 | issue_comment:
4 | types:
5 | - created
6 | jobs:
7 | closed-issue-comment:
8 | uses: cypress-io/cypress/.github/workflows/triage_handle_new_comments.yml@develop
9 | secrets: inherit
10 |
--------------------------------------------------------------------------------
/.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 | yarn.lock
11 | .parcel-cache
12 |
--------------------------------------------------------------------------------
/.nycrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | "support.js",
4 | "task-utils.js"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "none",
3 | "tabWidth": 2,
4 | "semi": false,
5 | "singleQuote": true,
6 | "bracketSpacing": true
7 | }
8 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": [
3 | "master",
4 | "next",
5 | {
6 | "name": "besta",
7 | "prerelease": true
8 | },
9 | {
10 | "name": "dev",
11 | "prerelease": true
12 | }
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | ## MIT License
2 |
3 | Copyright (c) 2019 Cypress.io https://www.cypress.io
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 |
--------------------------------------------------------------------------------
/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 | module.exports = {
65 | combineNycOptions,
66 | defaultNycOptions,
67 | fileCoveragePlaceholder,
68 | removePlaceholders
69 | }
70 |
--------------------------------------------------------------------------------
/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.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | viewportHeight: 200,
5 | viewportWidth: 200,
6 | e2e: {
7 | // We've imported your old cypress plugins here.
8 | // You may want to clean this up later by importing these.
9 | setupNodeEvents(on, config) {
10 | return require('./cypress/plugins/index.js')(on, config)
11 | },
12 | baseUrl: 'http://localhost:1234'
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/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 | - [e2e](e2e) 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.config.js` file
15 |
16 | You can configure project options in the [../cypress.config.js](../cypress.config.js) 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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/cypress/e2e/filtering.cy.js:
--------------------------------------------------------------------------------
1 | const { filterFilesFromCoverage } = require('../../support-utils')
2 |
3 | describe('minimatch', () => {
4 | it('string matches', () => {
5 | expect(
6 | Cypress.minimatch('/user/app/src/codeA.js', '/user/app/src/codeA.js'),
7 | 'matches full strings'
8 | ).to.be.true
9 |
10 | expect(
11 | Cypress.minimatch('/user/app/src/codeA.js', 'codeA.js'),
12 | 'does not match just the end'
13 | ).to.be.false
14 |
15 | expect(
16 | Cypress.minimatch('/user/app/src/codeA.js', '**/codeA.js'),
17 | 'matches using **'
18 | ).to.be.true
19 | })
20 | })
21 |
22 | describe('filtering specs', () => {
23 | describe('using integrationFolder and testFiles in Cypress < v10', () => {
24 | let config
25 | let env
26 | let spec
27 |
28 | beforeEach(() => {
29 | config = cy.stub()
30 | config.withArgs('integrationFolder').returns('/user/app/cypress/integration')
31 | config
32 | .withArgs('supportFile')
33 | .returns('/user/app/cypress/support/index.js')
34 | config
35 | .withArgs('supportFolder')
36 | .returns('/user/app/cypress/support')
37 |
38 | env = cy.stub().returns({})
39 |
40 | spec = {
41 | absolute: '/user/app/cypress/integration/test.cy.js',
42 | relative: 'cypress/integration/test.cy.js'
43 | }
44 | })
45 |
46 | it('filters list of specs by single string', () => {
47 | config.withArgs('testFiles').returns('specA.js')
48 | const totalCoverage = {
49 | '/user/app/cypress/integration/specA.js': {},
50 | '/user/app/cypress/integration/specB.js': {}
51 | }
52 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
53 | expect(result).to.deep.equal({
54 | '/user/app/cypress/integration/specB.js': {}
55 | })
56 | })
57 |
58 | it('filters list of specs by single string in array', () => {
59 | config.withArgs('testFiles').returns(['codeA.js'])
60 | const totalCoverage = {
61 | '/user/app/src/codeA.js': {},
62 | '/user/app/src/codeB.js': {}
63 | }
64 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
65 | expect(result).to.deep.equal({
66 | '/user/app/src/codeB.js': {}
67 | })
68 | })
69 |
70 | it('filters list of specs by pattern', () => {
71 | config.withArgs('testFiles').returns(['**/*B.js'])
72 |
73 | const totalCoverage = {
74 | '/user/app/src/codeA.js': {},
75 | '/user/app/src/codeB.js': {}
76 | }
77 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
78 | expect(result).to.deep.equal({
79 | '/user/app/src/codeA.js': {}
80 | })
81 | })
82 |
83 | it('filters list of specs by pattern and single spec', () => {
84 | config.withArgs('testFiles').returns(['**/*B.js', 'codeA.js'])
85 |
86 | const totalCoverage = {
87 | '/user/app/src/codeA.js': {},
88 | '/user/app/src/codeB.js': {}
89 | }
90 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
91 | expect(result, 'all specs have been filtered out').to.deep.equal({})
92 | })
93 |
94 | it('filters specs from integration folder', () => {
95 | config.withArgs('testFiles').returns('**/*.*') // default pattern
96 |
97 | const totalCoverage = {
98 | '/user/app/src/codeA.js': {},
99 | '/user/app/src/codeB.js': {},
100 | // these files should be removed
101 | '/user/app/cypress/integration/spec1.js': {},
102 | '/user/app/cypress/integration/spec2.js': {}
103 | }
104 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
105 | expect(result).to.deep.equal({
106 | '/user/app/src/codeA.js': {},
107 | '/user/app/src/codeB.js': {}
108 | })
109 | })
110 |
111 | it('filters list of specs when testFiles specifies folder', () => {
112 | config.withArgs('testFiles').returns(['cypress/integration/**.*'])
113 |
114 | const totalCoverage = {
115 | '/user/app/cypress/integration/specA.js': {},
116 | '/user/app/cypress/integration/specB.js': {},
117 | // This file should be included in coverage
118 | 'src/my-code.js': {}
119 | }
120 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
121 | expect(result).to.deep.equal({
122 | 'src/my-code.js': {}
123 | })
124 | })
125 |
126 | it('filters files out of cypress support directory', () => {
127 | config.withArgs('testFiles').returns(['**/*.*']) // default pattern
128 | const totalCoverage = {
129 | '/user/app/cypress/support/index.js': {},
130 | '/user/app/cypress/support/command.js': {},
131 | '/user/app/cypress/integration/spec.js': {},
132 | // This file should be included in coverage
133 | 'src/my-code.js': {}
134 | }
135 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
136 | expect(result).to.deep.equal({
137 | 'src/my-code.js': {}
138 | })
139 | })
140 | })
141 |
142 | describe('using codeCoverage.exclude and specPattern in Cypress >= v10', () => {
143 | let config
144 | let env
145 | let spec
146 |
147 | beforeEach(() => {
148 | config = cy.stub()
149 |
150 | env = cy.stub().returns({
151 | //filter out all files in the cypress folder
152 | codeCoverage: {
153 | exclude: 'cypress/**/*.*'
154 | }
155 | })
156 |
157 | spec = {
158 | absolute: '/user/app/cypress/integration/test.cy.js',
159 | relative: 'cypress/integration/test.cy.js'
160 | }
161 | })
162 |
163 | it('filters list of specs by single string', () => {
164 | config.withArgs('specPattern').returns('specA.cy.js')
165 | const totalCoverage = {
166 | '/user/app/src/specA.cy.js': {},
167 | '/user/app/src/specB.cy.js': {}
168 | }
169 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
170 | expect(result).to.deep.equal({
171 | '/user/app/src/specB.cy.js': {}
172 | })
173 | })
174 |
175 | it('filters list of specs by single string in array', () => {
176 | config.withArgs('specPattern').returns(['specA.cy.js'])
177 | const totalCoverage = {
178 | '/user/app/src/specA.cy.js': {},
179 | '/user/app/src/specB.cy.js': {}
180 | }
181 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
182 | expect(result).to.deep.equal({
183 | '/user/app/src/specB.cy.js': {}
184 | })
185 | })
186 |
187 | it('filters out file in codeCoverage.exclude', () => {
188 | config.withArgs('specPattern').returns(['**/*.cy.js'])
189 | const totalCoverage = {
190 | '/user/app/cypress/support/index.js': {},
191 | '/user/app/cypress/commands/index.js': {},
192 | //these files should be included
193 | '/user/app/src/codeA.js': {},
194 | '/user/app/src/codeB.js': {}
195 | }
196 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
197 | expect(result).to.deep.equal({
198 | '/user/app/src/codeA.js': {},
199 | '/user/app/src/codeB.js': {}
200 | })
201 | })
202 |
203 | it('filters list of specs by pattern', () => {
204 | config.withArgs('specPattern').returns(['**/*B.js'])
205 |
206 | const totalCoverage = {
207 | '/user/app/src/codeA.js': {},
208 | '/user/app/src/codeB.js': {}
209 | }
210 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
211 | expect(result).to.deep.equal({
212 | '/user/app/src/codeA.js': {}
213 | })
214 | })
215 |
216 | it('filters list of specs by pattern and single spec', () => {
217 | config.withArgs('specPattern').returns(['**/*B.js', 'codeA.js'])
218 |
219 | const totalCoverage = {
220 | '/user/app/src/codeA.js': {},
221 | '/user/app/src/codeB.js': {}
222 | }
223 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
224 | expect(result, 'all specs have been filtered out').to.deep.equal({})
225 | })
226 |
227 | it('filters list of specs in integration folder', () => {
228 | config.withArgs('specPattern').returns('**/*.cy.{js,jsx,ts,tsx}') // default pattern
229 |
230 | const totalCoverage = {
231 | '/user/app/src/codeA.js': {},
232 | '/user/app/src/codeB.js': {},
233 | // these files should be removed
234 | '/user/app/cypress/integration/spec1.js': {},
235 | '/user/app/cypress/integration/spec2.js': {}
236 | }
237 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
238 | expect(result).to.deep.equal({
239 | '/user/app/src/codeA.js': {},
240 | '/user/app/src/codeB.js': {}
241 | })
242 | })
243 |
244 | it('filters list of specs when specPattern specifies folder', () => {
245 | config.withArgs('specPattern').returns(['src/**/*.cy.js'])
246 |
247 | const totalCoverage = {
248 | '/user/app/src/specA.cy.js': {},
249 | '/user/app/src/specB.cy.js': {},
250 | // This file should be included in coverage
251 | 'src/my-code.js': {}
252 | }
253 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
254 | expect(result).to.deep.equal({
255 | 'src/my-code.js': {}
256 | })
257 | })
258 |
259 | it('filters list of specs when exclude pattern is an array', () => {
260 | env = cy.stub().returns({
261 | //filter out a.js and b.js in cypress folder
262 | codeCoverage: {
263 | exclude: ['cypress/**/a.js', 'cypress/**/b.js']
264 | }
265 | })
266 |
267 | config.withArgs('specPattern').returns(['src/**/*.cy.js'])
268 |
269 | const totalCoverage = {
270 | '/user/app/cypress/a.js': {},
271 | '/user/app/cypress/b.js': {},
272 | // These files should be included in coverage
273 | '/user/app/cypress/c.js': {},
274 | 'src/my-code.js': {}
275 | }
276 | const result = filterFilesFromCoverage(totalCoverage, config, env, spec)
277 | expect(result).to.deep.equal({
278 | '/user/app/cypress/c.js': {},
279 | 'src/my-code.js': {}
280 | })
281 | })
282 | })
283 | })
284 |
--------------------------------------------------------------------------------
/cypress/e2e/fix-source-paths.cy.js:
--------------------------------------------------------------------------------
1 | import { fixSourcePaths } from '../../support-utils'
2 |
3 | describe('fixSourcePaths', () => {
4 | it('fixes webpack loader source-map pathes', () => {
5 | const coverage = {
6 | '/absolute/src/component.vue': {
7 | path: '/absolute/src/component.vue',
8 | inputSourceMap: {
9 | sources: [
10 | '/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&',
11 | 'otherFile.js'
12 | ],
13 | sourceRoot: 'src'
14 | }
15 | },
16 | '/folder/module-without-sourcemap.js': {
17 | path: '/folder/module-without-sourcemap.js'
18 | }
19 | }
20 |
21 | fixSourcePaths(coverage)
22 |
23 | expect(coverage).to.deep.eq({
24 | '/absolute/src/component.vue': {
25 | path: '/absolute/src/component.vue',
26 | inputSourceMap: {
27 | sources: ['/absolute/src/component.vue', 'otherFile.js'],
28 | sourceRoot: ''
29 | }
30 | },
31 | '/folder/module-without-sourcemap.js': {
32 | path: '/folder/module-without-sourcemap.js'
33 | }
34 | })
35 | })
36 | })
37 |
--------------------------------------------------------------------------------
/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/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/cypress/index.html:
--------------------------------------------------------------------------------
1 |
2 | Test page
3 |
4 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '../../support'
2 |
--------------------------------------------------------------------------------
/images/coverage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/images/coverage.jpg
--------------------------------------------------------------------------------
/images/expect-backend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/images/expect-backend.png
--------------------------------------------------------------------------------
/images/gui.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/images/gui.png
--------------------------------------------------------------------------------
/images/instrumented-code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/images/instrumented-code.png
--------------------------------------------------------------------------------
/images/warning.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/images/warning.png
--------------------------------------------------------------------------------
/images/window-coverage-object.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/images/window-coverage-object.png
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@cypress/code-coverage",
3 | "version": "0.0.0-development",
4 | "description": "Saves the code coverage collected during Cypress tests",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "parcel serve cypress/index.html",
8 | "coverage:verify": "npx nyc report --check-coverage true --lines 80",
9 | "cy:open": "cypress open",
10 | "dev": "start-test 1234 cy:open",
11 | "semantic-release": "semantic-release",
12 | "test": "start-test 1234 'npx cypress run'",
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 | },
20 | "peerDependencies": {
21 | "@babel/core": "^7.0.1",
22 | "@babel/preset-env": "^7.0.0",
23 | "babel-loader": "^8.3 || ^9 || ^10",
24 | "cypress": "*",
25 | "webpack": "^4 || ^5"
26 | },
27 | "repository": {
28 | "type": "git",
29 | "url": "https://github.com/cypress-io/code-coverage.git"
30 | },
31 | "keywords": [
32 | "cypress",
33 | "istanbul",
34 | "cypress-plugin",
35 | "code",
36 | "coverage"
37 | ],
38 | "author": "Cypress (http://www.cypress.io/)",
39 | "license": "MIT",
40 | "bugs": {
41 | "url": "https://github.com/cypress-io/code-coverage/issues"
42 | },
43 | "homepage": "https://github.com/cypress-io/code-coverage#readme",
44 | "files": [
45 | "*.d.ts",
46 | "*.js",
47 | "middleware"
48 | ],
49 | "publishConfig": {
50 | "access": "public"
51 | },
52 | "private": false,
53 | "dependencies": {
54 | "@cypress/webpack-preprocessor": "^6.0.0",
55 | "chalk": "4.1.2",
56 | "dayjs": "1.11.13",
57 | "debug": "4.4.0",
58 | "execa": "4.1.0",
59 | "globby": "11.1.0",
60 | "istanbul-lib-coverage": "^3.0.0",
61 | "js-yaml": "4.1.0",
62 | "nyc": "15.1.0"
63 | },
64 | "devDependencies": {
65 | "@babel/core": "^7.16.0",
66 | "@babel/preset-env": "^7.16.11",
67 | "@cypress/code-coverage": "file:.",
68 | "babel-loader": "^9.1.3",
69 | "babel-plugin-istanbul": "6.1.1",
70 | "check-code-coverage": "1.10.5",
71 | "console-log-div": "0.6.3",
72 | "cypress": "^13.1.0",
73 | "express": "^4.18.2",
74 | "lodash": "4.17.21",
75 | "markdown-link-check": "3.13.6",
76 | "parcel-bundler": "1.12.5",
77 | "prettier": "3.5.3",
78 | "rimraf": "6.0.1",
79 | "semantic-release": "17.4.7",
80 | "serve": "14.2.4",
81 | "start-server-and-test": "2.0.12",
82 | "webpack": "^5.68.0",
83 | "webpack-cli": "^5.1.4"
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/plugins.js:
--------------------------------------------------------------------------------
1 | // common Cypress plugin file you can point at to have the
2 | // code coverage tasks registered correctly. From your "cypress.json" file
3 | // {
4 | // "pluginsFile": "@cypress/code-coverage/plugins",
5 | // "supportFile": "@cypress/code-coverage/support"
6 | // }
7 | //
8 | module.exports = (on, config) => {
9 | require('./task')(on, config)
10 | // IMPORTANT to return the config object
11 | // with the any changed environment variables
12 | return config
13 | }
14 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/support-utils.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | // helper functions that are safe to use in the browser
3 | // from support.js file - no file system access
4 |
5 | /** excludes files that shouldn't be in code coverage report */
6 | const filterFilesFromCoverage = (
7 | totalCoverage,
8 | config = Cypress.config,
9 | env = Cypress.env,
10 | spec = Cypress.spec
11 | ) => {
12 | const withoutSpecs = filterSpecsFromCoverage(totalCoverage, config, env, spec)
13 | const appCoverageOnly = filterSupportFilesFromCoverage(withoutSpecs, config)
14 | return appCoverageOnly
15 | }
16 |
17 | /**
18 | * remove coverage for the spec files themselves,
19 | * only keep "external" application source file coverage
20 | */
21 | const filterSpecsFromCoverage = (
22 | totalCoverage,
23 | config = Cypress.config,
24 | env = Cypress.env,
25 | spec = Cypress.spec
26 | ) => {
27 | const testFilePatterns = getCypressExcludePatterns(config, env, spec)
28 |
29 | const isTestFile = (_, filePath) => {
30 | const workingDir = spec.absolute.replace(spec.relative, '')
31 | const filename = filePath.replace(workingDir, '')
32 | const matchedPattern = testFilePatterns.some((specPattern) =>
33 | Cypress.minimatch(filename, specPattern, { debug: false })
34 | )
35 | const matchedEndOfPath = testFilePatterns.some((specPattern) =>
36 | filename.endsWith(specPattern)
37 | )
38 | return matchedPattern || matchedEndOfPath
39 | }
40 |
41 | const coverage = Cypress._.omitBy(totalCoverage, isTestFile)
42 | return coverage
43 | }
44 |
45 | /**
46 | * Reads Cypress config and exclude patterns and combines them into one array
47 | * @param {*} config
48 | * @param {*} env
49 | * @returns string[]
50 | */
51 | function getCypressExcludePatterns(config, env, spec) {
52 | let testFilePatterns = []
53 |
54 | const testFilePattern = config('specPattern') || config('testFiles')
55 | const excludePattern = env().codeCoverage && env().codeCoverage.exclude
56 |
57 | if (Array.isArray(testFilePattern)) {
58 | testFilePatterns = testFilePattern
59 | } else {
60 | testFilePatterns = [testFilePattern]
61 | }
62 |
63 | // combine test files pattern and exclude patterns into single exclude pattern
64 | if (Array.isArray(excludePattern)) {
65 | testFilePatterns = [...testFilePatterns, ...excludePattern]
66 | } else if (excludePattern) {
67 | testFilePatterns = [...testFilePatterns, excludePattern]
68 | }
69 |
70 | // Cypress {
77 | if (pattern === '**/*.*') {
78 | testFilePatterns[idx] = `${integrationFolder}/${pattern}`.replace(
79 | '//',
80 | '/'
81 | )
82 | }
83 | })
84 | }
85 |
86 | return testFilePatterns
87 | }
88 |
89 | /**
90 | * Removes support file from the coverage object.
91 | * If there are more files loaded from support folder, also removes them
92 | */
93 | const filterSupportFilesFromCoverage = (
94 | totalCoverage,
95 | config = Cypress.config
96 | ) => {
97 | const integrationFolder = config('integrationFolder')
98 |
99 | /**
100 | * Cypress v10 doesn't have an integrationFolder config value any more, so we nope out here if its undefined.
101 | * Instead, we rely on the new exclude option logic done in the getCypressExcludePatterns function.
102 | */
103 | if (!integrationFolder) {
104 | return totalCoverage
105 | }
106 |
107 | const supportFile = config('supportFile')
108 |
109 | /** @type {string} Cypress run-time config has the support folder string */
110 | // @ts-ignore
111 | const supportFolder = config('supportFolder')
112 |
113 | const isSupportFile = (filename) => filename === supportFile
114 |
115 | let coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
116 | isSupportFile(filename)
117 | )
118 |
119 | // check the edge case
120 | // if we have files from support folder AND the support folder is not same
121 | // as the integration, or its prefix (this might remove all app source files)
122 | // then remove all files from the support folder
123 | if (!integrationFolder.startsWith(supportFolder)) {
124 | // remove all covered files from support folder
125 | coverage = Cypress._.omitBy(totalCoverage, (fileCoverage, filename) =>
126 | filename.startsWith(supportFolder)
127 | )
128 | }
129 | return coverage
130 | }
131 |
132 | /**
133 | * Replace source-map's path by the corresponding absolute file path
134 | * (coverage report wouldn't work with source-map path being relative
135 | * or containing Webpack loaders and query parameters)
136 | */
137 | function fixSourcePaths(coverage) {
138 | Object.values(coverage).forEach((file) => {
139 | const { path: absolutePath, inputSourceMap } = file
140 | if (!inputSourceMap) return
141 | const fileName = /([^\/\\]+)$/.exec(absolutePath)[1]
142 | if (!fileName) return
143 |
144 | if (inputSourceMap.sourceRoot) inputSourceMap.sourceRoot = ''
145 | inputSourceMap.sources = inputSourceMap.sources.map((source) =>
146 | source.includes(fileName) ? absolutePath : source
147 | )
148 | })
149 | }
150 |
151 | /**
152 | * Validates and returns the configured batch size for
153 | * sending coverage to the backend
154 | */
155 | function getSendCoverageBatchSize() {
156 | const batchSize = Cypress.env('sendCoverageBatchSize')
157 | const parsedBatchSize = parseInt(batchSize)
158 | const isValid = !isNaN(parsedBatchSize) && parsedBatchSize > 0
159 | return isValid ? parsedBatchSize : null
160 | }
161 |
162 | module.exports = {
163 | fixSourcePaths,
164 | filterFilesFromCoverage,
165 | getSendCoverageBatchSize
166 | }
167 |
--------------------------------------------------------------------------------
/support.js:
--------------------------------------------------------------------------------
1 | ///
2 | // @ts-check
3 |
4 | const dayjs = require('dayjs')
5 | var duration = require('dayjs/plugin/duration')
6 | const {
7 | filterFilesFromCoverage,
8 | getSendCoverageBatchSize
9 | } = require('./support-utils')
10 |
11 | dayjs.extend(duration)
12 |
13 | /**
14 | * Sends collected code coverage object to the backend code
15 | * via "cy.task".
16 | */
17 | const sendCoverage = (coverage, pathname = '/') => {
18 | logMessage(`Saving code coverage for **${pathname}**`)
19 |
20 | const totalCoverage = filterFilesFromCoverage(coverage)
21 |
22 | const envBatchSize = getSendCoverageBatchSize()
23 | const keys = Object.keys(totalCoverage)
24 |
25 | if (envBatchSize && envBatchSize < keys.length) {
26 | sendBatchCoverage(totalCoverage, envBatchSize)
27 | } else {
28 | cy.task('combineCoverage', JSON.stringify(totalCoverage), {
29 | log: false
30 | })
31 | }
32 | }
33 |
34 | /**
35 | * Sends collected code coverage object to the backend code
36 | * in batches via "cy.task".
37 | */
38 | const sendBatchCoverage = (totalCoverage, batchSize) => {
39 | const keys = Object.keys(totalCoverage)
40 |
41 | for (let i = 0; i < keys.length; i += batchSize) {
42 | const batchKeys = keys.slice(i, i + batchSize)
43 | const batchCoverage = {}
44 |
45 | batchKeys.forEach((key) => {
46 | batchCoverage[key] = totalCoverage[key]
47 | })
48 |
49 | cy.task('combineCoverage', JSON.stringify(batchCoverage), {
50 | log: false
51 | })
52 | }
53 | }
54 |
55 | /**
56 | * Consistently logs the given string to the Command Log
57 | * so the user knows the log message is coming from this plugin.
58 | * @param {string} s Message to log.
59 | */
60 | const logMessage = (s) => {
61 | cy.log(`${s} \`[@cypress/code-coverage]\``)
62 | }
63 |
64 | const registerHooks = () => {
65 | let windowCoverageObjects
66 |
67 | const hasE2ECoverage = () => Boolean(windowCoverageObjects.length)
68 |
69 | // @ts-ignore
70 | const hasUnitTestCoverage = () => Boolean(window.__coverage__)
71 |
72 | before(() => {
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 | const logInstance = Cypress.log({
77 | name: 'Coverage',
78 | message: ['Reset [@cypress/code-coverage]']
79 | })
80 |
81 | cy.task(
82 | 'resetCoverage',
83 | {
84 | // @ts-ignore
85 | isInteractive: Cypress.config('isInteractive')
86 | },
87 | { log: false }
88 | ).then(() => {
89 | logInstance.end()
90 | })
91 | })
92 |
93 | beforeEach(() => {
94 | // each object will have the coverage and url pathname
95 | // to let the user know the coverage has been collected
96 | windowCoverageObjects = []
97 |
98 | const saveCoverageObject = (win) => {
99 | // if the application code has been instrumented, then the app iframe "win.__coverage__" will be available,
100 | // in addition, accessing win.__coverage__ can throw when testing cross-origin code,
101 | // because we don't control the cross-origin code, we can safely return
102 | let applicationSourceCoverage
103 | try {
104 | // Note that we are purposefully not supporting the optional chaining syntax here to
105 | // support a wide range of projects (some of which are not set up to support the optional
106 | // chaining syntax due to current Cypress limitations). See:
107 | // https://github.com/cypress-io/cypress/issues/20753
108 | if (win) {
109 | applicationSourceCoverage = win.__coverage__
110 | }
111 | } catch {}
112 |
113 | if (!applicationSourceCoverage) {
114 | return
115 | }
116 |
117 | const existingCoverage = Cypress._.find(windowCoverageObjects, {
118 | coverage: applicationSourceCoverage
119 | })
120 | if (existingCoverage) {
121 | // this application code coverage object is already known
122 | // which can happen when combining `window:load` and `before` callbacks,
123 | // it can also happen when the user navigates away and then returns to the page
124 | // in which case we need to use new applicationSourceCoverage, because the old will not be updated anymore.
125 | existingCoverage.coverage = applicationSourceCoverage
126 | return
127 | }
128 |
129 | windowCoverageObjects.push({
130 | coverage: applicationSourceCoverage,
131 | pathname: win.location.pathname
132 | })
133 | }
134 |
135 | // save reference to coverage for each app window loaded in the test
136 | cy.on('window:load', saveCoverageObject)
137 |
138 | // save reference if visiting a page inside a before() hook
139 | cy.window({ log: false }).then(saveCoverageObject)
140 | })
141 |
142 | afterEach(() => {
143 | // save coverage after the test
144 | // because now the window coverage objects have been updated
145 | windowCoverageObjects.forEach((cover) => {
146 | sendCoverage(cover.coverage, cover.pathname)
147 | })
148 |
149 | if (!hasE2ECoverage()) {
150 | if (hasUnitTestCoverage()) {
151 | logMessage(`👉 Only found unit test code coverage.`)
152 | } else {
153 | const expectBackendCoverageOnly = Cypress._.get(
154 | Cypress.env('codeCoverage'),
155 | 'expectBackendCoverageOnly',
156 | false
157 | )
158 | if (!expectBackendCoverageOnly) {
159 | logMessage(`
160 | ⚠️ Could not find any coverage information in your application
161 | by looking at the window coverage object.
162 | Did you forget to instrument your application?
163 | See [code-coverage#instrument-your-application](https://github.com/cypress-io/code-coverage#instrument-your-application)
164 | `)
165 | }
166 | }
167 | }
168 | })
169 |
170 | after(function collectBackendCoverage() {
171 | // I wish I could fail the tests if there is no code coverage information
172 | // but throwing an error here does not fail the test run due to
173 | // https://github.com/cypress-io/cypress/issues/2296
174 |
175 | // there might be server-side code coverage information
176 | // we should grab it once after all tests finish
177 | // @ts-ignore
178 | const baseUrl = Cypress.config('baseUrl') || cy.state('window').origin
179 | // @ts-ignore
180 | const runningEndToEndTests = baseUrl !== Cypress.config('proxyUrl')
181 | const expectFrontendCoverageOnly = Cypress._.get(
182 | Cypress.env('codeCoverage'),
183 | 'expectFrontendCoverageOnly',
184 | false
185 | )
186 | const specType = Cypress._.get(Cypress.spec, 'specType', 'integration')
187 | const isIntegrationSpec = specType === 'integration'
188 |
189 | if (
190 | !expectFrontendCoverageOnly &&
191 | runningEndToEndTests &&
192 | isIntegrationSpec
193 | ) {
194 | // we can only request server-side code coverage
195 | // if we are running end-to-end tests,
196 | // otherwise where do we send the request?
197 | const captureUrls = Cypress._.get(
198 | Cypress.env('codeCoverage'),
199 | 'url',
200 | '/__coverage__'
201 | )
202 | function captureCoverage(url, suffix = '') {
203 | cy.request({
204 | url,
205 | log: false,
206 | failOnStatusCode: false
207 | })
208 | .then((r) => {
209 | return Cypress._.get(r, 'body.coverage', null)
210 | })
211 | .then((coverage) => {
212 | if (!coverage) {
213 | // we did not get code coverage - this is the
214 | // original failed request
215 | const expectBackendCoverageOnly = Cypress._.get(
216 | Cypress.env('codeCoverage'),
217 | 'expectBackendCoverageOnly',
218 | false
219 | )
220 | if (expectBackendCoverageOnly) {
221 | throw new Error(
222 | `Expected to collect backend code coverage from ${url}`
223 | )
224 | } else {
225 | // we did not really expect to collect the backend code coverage
226 | return
227 | }
228 | }
229 | sendCoverage(coverage, `backend${suffix}`)
230 | })
231 | }
232 |
233 | if (Array.isArray(captureUrls)) {
234 | for (const [index, url] of captureUrls.entries()) {
235 | captureCoverage(url, `_${index}`)
236 | }
237 | } else {
238 | captureCoverage(captureUrls)
239 | }
240 | }
241 | })
242 |
243 | after(function mergeUnitTestCoverage() {
244 | // collect and merge frontend coverage
245 |
246 | // if spec bundle has been instrumented (using Cypress preprocessor)
247 | // then we will have unit test coverage
248 | // NOTE: spec iframe is NOT reset between the tests, so we can grab
249 | // the coverage information only once after all tests have finished
250 | // @ts-ignore
251 | const unitTestCoverage = window.__coverage__
252 | if (unitTestCoverage) {
253 | sendCoverage(unitTestCoverage, 'unit')
254 | }
255 | })
256 |
257 | after(function generateReport() {
258 | // when all tests finish, lets generate the coverage report
259 | const logInstance = Cypress.log({
260 | name: 'Coverage',
261 | message: ['Generating report [@cypress/code-coverage]']
262 | })
263 | cy.task('coverageReport', null, {
264 | timeout: dayjs.duration(3, 'minutes').asMilliseconds(),
265 | log: false
266 | }).then((coverageReportFolder) => {
267 | logInstance.set('consoleProps', () => ({
268 | 'coverage report folder': coverageReportFolder
269 | }))
270 | logInstance.end()
271 | return coverageReportFolder
272 | })
273 | })
274 | }
275 |
276 | // to disable code coverage commands and save time
277 | // pass environment variable coverage=false
278 | // cypress run --env coverage=false
279 | // or
280 | // CYPRESS_COVERAGE=false cypress run
281 | // see https://on.cypress.io/environment-variables
282 |
283 | // to avoid "coverage" env variable being case-sensitive, convert to lowercase
284 | const cyEnvs = Cypress._.mapKeys(Cypress.env(), (value, key) =>
285 | key.toLowerCase()
286 | )
287 |
288 | if (cyEnvs.coverage === false) {
289 | console.log('Skipping code coverage hooks')
290 | } else if (Cypress.env('codeCoverageTasksRegistered') !== true) {
291 | // register a hook just to log a message
292 | before(() => {
293 | logMessage(`
294 | ⚠️ Code coverage tasks were not registered by the plugins file.
295 | See [support issue](https://github.com/cypress-io/code-coverage/issues/179)
296 | for possible workarounds.
297 | `)
298 | })
299 | } else {
300 | registerHooks()
301 | }
302 |
--------------------------------------------------------------------------------
/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 nycConfigCommonJsFilename = join(workingDirectory, 'nyc.config.cjs')
66 | let nycConfigCommonJs = {}
67 | if (existsSync(nycConfigCommonJsFilename)) {
68 | try {
69 | nycConfigCommonJs = require(nycConfigCommonJsFilename)
70 | } catch (error) {
71 | throw new Error(`Failed to load nyc.config.cjs: ${error.message}`)
72 | }
73 | }
74 |
75 | const nycOptions = combineNycOptions(
76 | defaultNycOptions,
77 | nycrc,
78 | nycrcJson,
79 | nycrcYaml,
80 | nycrcYml,
81 | nycConfig,
82 | nycConfigCommonJs,
83 | pkgNycOptions
84 | )
85 | debug('combined NYC options %o', nycOptions)
86 |
87 | return nycOptions
88 | }
89 |
90 | function checkAllPathsNotFound(nycFilename) {
91 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
92 |
93 | const coverageKeys = Object.keys(nycCoverage)
94 | if (!coverageKeys.length) {
95 | debug('⚠️ file %s has no coverage information', nycFilename)
96 | return
97 | }
98 |
99 | const allFilesAreMissing = coverageKeys.every((key, k) => {
100 | const coverage = nycCoverage[key]
101 | return !existsSync(coverage.path)
102 | })
103 |
104 | debug(
105 | 'in file %s all files are not found? %o',
106 | nycFilename,
107 | allFilesAreMissing
108 | )
109 | return allFilesAreMissing
110 | }
111 |
112 | /**
113 | * A small debug utility to inspect paths saved in NYC output JSON file
114 | */
115 | function showNycInfo(nycFilename) {
116 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
117 |
118 | const coverageKeys = Object.keys(nycCoverage)
119 | if (!coverageKeys.length) {
120 | console.error(
121 | '⚠️ file %s has no coverage information',
122 | chalk.yellow(nycFilename)
123 | )
124 | console.error(
125 | 'Did you forget to instrument your web application? Read %s',
126 | chalk.blue(
127 | 'https://github.com/cypress-io/code-coverage#instrument-your-application'
128 | )
129 | )
130 | return
131 | }
132 | debug('NYC file %s has %d key(s)', nycFilename, coverageKeys.length)
133 |
134 | const maxPrintKeys = 3
135 | const showKeys = coverageKeys.slice(0, maxPrintKeys)
136 |
137 | showKeys.forEach((key, k) => {
138 | const coverage = nycCoverage[key]
139 |
140 | // printing a few found keys and file paths from the coverage file
141 | // will make debugging any problems much much easier
142 | if (k < maxPrintKeys) {
143 | debug('%d key %s file path %s', k + 1, key, coverage.path)
144 | }
145 | })
146 | }
147 |
148 | /**
149 | * Looks at all coverage objects in the given JSON coverage file
150 | * and if the file is relative, and exists, changes its path to
151 | * be absolute.
152 | */
153 | function resolveRelativePaths(nycFilename) {
154 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
155 |
156 | const coverageKeys = Object.keys(nycCoverage)
157 | if (!coverageKeys.length) {
158 | debug('⚠️ file %s has no coverage information', nycFilename)
159 | return
160 | }
161 | debug('NYC file %s has %d key(s)', nycFilename, coverageKeys.length)
162 |
163 | let changed
164 |
165 | coverageKeys.forEach((key, k) => {
166 | const coverage = nycCoverage[key]
167 |
168 | if (!coverage.path) {
169 | debug('key %s does not have path', key)
170 | return
171 | }
172 |
173 | if (!isAbsolute(coverage.path)) {
174 | if (existsSync(coverage.path)) {
175 | debug('resolving path %s', coverage.path)
176 | coverage.path = resolve(coverage.path)
177 | changed = true
178 | }
179 | return
180 | }
181 |
182 | // path is absolute, let's check if it exists
183 | if (!existsSync(coverage.path)) {
184 | debug('⚠️ cannot find file %s with hash %s', coverage.path, coverage.hash)
185 | }
186 | })
187 |
188 | if (changed) {
189 | debug('resolveRelativePaths saving updated file %s', nycFilename)
190 | debug('there are %d keys in the file', coverageKeys.length)
191 | writeFileSync(
192 | nycFilename,
193 | JSON.stringify(nycCoverage, null, 2) + '\n',
194 | 'utf8'
195 | )
196 | }
197 | }
198 |
199 | /**
200 | * @param {string[]} filepaths
201 | * @returns {string | undefined} common prefix that corresponds to current folder
202 | */
203 | function findCommonRoot(filepaths) {
204 | if (!filepaths.length) {
205 | debug('cannot find common root without any files')
206 | return
207 | }
208 |
209 | // assuming / as file separator
210 | const splitParts = filepaths.map((name) => name.split('/'))
211 | const lengths = splitParts.map((arr) => arr.length)
212 | const shortestLength = Math.min.apply(null, lengths)
213 | debug('shorted file path has %d parts', shortestLength)
214 |
215 | const cwd = process.cwd()
216 | let commonPrefix = []
217 | let foundCurrentFolder
218 |
219 | for (let k = 0; k < shortestLength; k += 1) {
220 | const part = splitParts[0][k]
221 | const prefix = commonPrefix.concat(part).join('/')
222 | debug('testing prefix %o', prefix)
223 | const allFilesStart = filepaths.every((name) => name.startsWith(prefix))
224 | if (!allFilesStart) {
225 | debug('stopped at non-common prefix %s', prefix)
226 | break
227 | }
228 |
229 | commonPrefix.push(part)
230 |
231 | const removedPrefixNames = filepaths.map((filepath) =>
232 | filepath.slice(prefix.length)
233 | )
234 | debug('removedPrefix %o', removedPrefixNames)
235 | const foundAllPaths = removedPrefixNames.every((filepath) =>
236 | existsSync(join(cwd, filepath))
237 | )
238 | debug('all files found at %s? %o', prefix, foundAllPaths)
239 | if (foundAllPaths) {
240 | debug('found prefix that matches current folder: %s', prefix)
241 | foundCurrentFolder = prefix
242 | break
243 | }
244 | }
245 |
246 | return foundCurrentFolder
247 | }
248 |
249 | function tryFindingLocalFiles(nycFilename) {
250 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
251 | const coverageKeys = Object.keys(nycCoverage)
252 | const filenames = coverageKeys.map((key) => nycCoverage[key].path)
253 | const commonFolder = findCommonRoot(filenames)
254 | if (!commonFolder) {
255 | debug('could not find common folder %s', commonFolder)
256 | return
257 | }
258 | const cwd = process.cwd()
259 | debug(
260 | 'found common folder %s that matches current working directory %s',
261 | commonFolder,
262 | cwd
263 | )
264 | const length = commonFolder.length
265 | let changed
266 |
267 | coverageKeys.forEach((key) => {
268 | const from = nycCoverage[key].path
269 | if (from.startsWith(commonFolder)) {
270 | const to = join(cwd, from.slice(length))
271 | // ? Do we need to replace the "key" in the coverage object or can we just replace the "path"?
272 | nycCoverage[key].path = to
273 | debug('replaced %s -> %s', from, to)
274 | changed = true
275 | }
276 | })
277 |
278 | if (changed) {
279 | debug('tryFindingLocalFiles saving updated file %s', nycFilename)
280 | debug('there are %d keys in the file', coverageKeys.length)
281 | writeFileSync(
282 | nycFilename,
283 | JSON.stringify(nycCoverage, null, 2) + '\n',
284 | 'utf8'
285 | )
286 | }
287 | }
288 |
289 | /**
290 | * Tries to find source files to be included in the final coverage report
291 | * using NYC options: extension list, include and exclude.
292 | */
293 | function findSourceFiles(nycOptions) {
294 | debug('include all files options: %o', {
295 | all: nycOptions.all,
296 | include: nycOptions.include,
297 | exclude: nycOptions.exclude,
298 | extension: nycOptions.extension
299 | })
300 |
301 | if (!Array.isArray(nycOptions.extension)) {
302 | console.error(
303 | 'Expected NYC "extension" option to be a list of file extensions'
304 | )
305 | console.error(nycOptions)
306 | return []
307 | }
308 |
309 | let patterns = []
310 | if (Array.isArray(nycOptions.include)) {
311 | patterns = patterns.concat(nycOptions.include)
312 | } else if (typeof nycOptions.include === 'string') {
313 | patterns.push(nycOptions.include)
314 | } else {
315 | debug('using default list of extensions')
316 | nycOptions.extension.forEach((extension) => {
317 | patterns.push('**/*' + extension)
318 | })
319 | }
320 |
321 | if (Array.isArray(nycOptions.exclude)) {
322 | const negated = nycOptions.exclude.map((s) => '!' + s)
323 | patterns = patterns.concat(negated)
324 | } else if (typeof nycOptions.exclude === 'string') {
325 | patterns.push('!' + nycOptions.exclude)
326 | }
327 | // always exclude node_modules
328 | // https://github.com/istanbuljs/nyc#including-files-within-node_modules
329 | patterns.push('!**/node_modules/**')
330 |
331 | debug('searching files to include using patterns %o', patterns)
332 |
333 | const allFiles = globby.sync(patterns, { absolute: true })
334 | return allFiles
335 | }
336 | /**
337 | * If the website or unit tests did not load ALL files we need to
338 | * include, then we should include the missing files ourselves
339 | * before generating the report.
340 | *
341 | * @see https://github.com/cypress-io/code-coverage/issues/207
342 | */
343 | function includeAllFiles(nycFilename, nycOptions) {
344 | if (!nycOptions.all) {
345 | debug('NYC "all" option is not set, skipping including all files')
346 | return
347 | }
348 |
349 | const allFiles = findSourceFiles(nycOptions)
350 | if (debug.enabled) {
351 | debug('found %d file(s)', allFiles.length)
352 | console.error(allFiles.join('\n'))
353 | }
354 | if (!allFiles.length) {
355 | debug('no files found, hoping for the best')
356 | return
357 | }
358 |
359 | const nycCoverage = JSON.parse(readFileSync(nycFilename, 'utf8'))
360 | const coverageKeys = Object.keys(nycCoverage)
361 | const coveredPaths = coverageKeys.map((key) =>
362 | nycCoverage[key].path.replace(/\\/g, '/')
363 | )
364 |
365 | debug('coverage has %d record(s)', coveredPaths.length)
366 | // report on first couple of entries
367 | if (debug.enabled) {
368 | console.error('coverage has the following first paths')
369 | console.error(coveredPaths.slice(0, 4).join('\n'))
370 | }
371 |
372 | let changed
373 | allFiles.forEach((fullPath) => {
374 | if (coveredPaths.includes(fullPath)) {
375 | // all good, this file exists in coverage object
376 | return
377 | }
378 | debug('adding empty coverage for file %s', fullPath)
379 | changed = true
380 | // insert placeholder object for now
381 | const placeholder = fileCoveragePlaceholder(fullPath)
382 | nycCoverage[fullPath] = placeholder
383 | })
384 |
385 | if (changed) {
386 | debug('includeAllFiles saving updated file %s', nycFilename)
387 | debug('there are %d keys in the file', Object.keys(nycCoverage).length)
388 |
389 | writeFileSync(
390 | nycFilename,
391 | JSON.stringify(nycCoverage, null, 2) + '\n',
392 | 'utf8'
393 | )
394 | }
395 | }
396 |
397 | module.exports = {
398 | showNycInfo,
399 | resolveRelativePaths,
400 | checkAllPathsNotFound,
401 | tryFindingLocalFiles,
402 | readNycOptions,
403 | includeAllFiles
404 | }
405 |
--------------------------------------------------------------------------------
/task.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | export default function registerCodeCoverageTasks(
4 | on: Cypress.PluginEvents,
5 | config: Cypress.PluginConfigOptions,
6 | ): void;
7 |
--------------------------------------------------------------------------------
/task.js:
--------------------------------------------------------------------------------
1 | // @ts-check
2 | const istanbul = require('istanbul-lib-coverage')
3 | const { join, resolve } = require('path')
4 | const { existsSync, mkdirSync, readFileSync, writeFileSync } = require('fs')
5 | const execa = require('execa')
6 | const {
7 | showNycInfo,
8 | resolveRelativePaths,
9 | checkAllPathsNotFound,
10 | tryFindingLocalFiles,
11 | readNycOptions,
12 | includeAllFiles
13 | } = require('./task-utils')
14 | const { fixSourcePaths } = require('./support-utils')
15 |
16 | const debug = require('debug')('code-coverage')
17 |
18 | // these are standard folder and file names used by NYC tools
19 | const processWorkingDirectory = process.cwd()
20 |
21 | // there might be custom "nyc" options in the user package.json
22 | // see https://github.com/istanbuljs/nyc#configuring-nyc
23 | // potentially there might be "nyc" options in other configuration files
24 | // it allows, but for now ignore those options
25 | const pkgFilename = join(processWorkingDirectory, 'package.json')
26 | const pkg = existsSync(pkgFilename)
27 | ? JSON.parse(readFileSync(pkgFilename, 'utf8'))
28 | : {}
29 | const scripts = pkg.scripts || {}
30 | const DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME = 'coverage:report'
31 | const customNycReportScript = scripts[DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME]
32 |
33 | const nycReportOptions = (function getNycOption() {
34 | // https://github.com/istanbuljs/nyc#common-configuration-options
35 | const nycReportOptions = readNycOptions(processWorkingDirectory)
36 |
37 | if (nycReportOptions.exclude && !Array.isArray(nycReportOptions.exclude)) {
38 | console.error('NYC options: %o', nycReportOptions)
39 | throw new Error('Expected "exclude" to be an array')
40 | }
41 |
42 | if (nycReportOptions['temp-dir']) {
43 | nycReportOptions['temp-dir'] = resolve(nycReportOptions['temp-dir'])
44 | } else {
45 | nycReportOptions['temp-dir'] = join(processWorkingDirectory, '.nyc_output')
46 | }
47 |
48 | nycReportOptions.tempDir = nycReportOptions['temp-dir']
49 |
50 | if (nycReportOptions['report-dir']) {
51 | nycReportOptions['report-dir'] = resolve(nycReportOptions['report-dir'])
52 | }
53 | // seems nyc API really is using camel cased version
54 | nycReportOptions.reportDir = nycReportOptions['report-dir']
55 |
56 | return nycReportOptions
57 | })()
58 |
59 | const nycFilename = join(nycReportOptions['temp-dir'], 'out.json')
60 |
61 | let coverageMap = (() => {
62 | const previousCoverage = existsSync(nycFilename)
63 | ? JSON.parse(readFileSync(nycFilename, 'utf8'))
64 | : {}
65 | return istanbul.createCoverageMap(previousCoverage)
66 | })()
67 |
68 | function saveCoverage(coverage) {
69 | if (!existsSync(nycReportOptions.tempDir)) {
70 | mkdirSync(nycReportOptions.tempDir, { recursive: true })
71 | debug('created folder %s for output coverage', nycReportOptions.tempDir)
72 | }
73 |
74 | writeFileSync(nycFilename, JSON.stringify(coverage, null, 2))
75 | }
76 |
77 | function maybePrintFinalCoverageFiles(folder) {
78 | const jsonReportFilename = join(folder, 'coverage-final.json')
79 | if (!existsSync(jsonReportFilename)) {
80 | debug('Did not find final coverage file %s', jsonReportFilename)
81 | return
82 | }
83 |
84 | debug('Final coverage in %s', jsonReportFilename)
85 | const finalCoverage = JSON.parse(readFileSync(jsonReportFilename, 'utf8'))
86 | const finalCoverageKeys = Object.keys(finalCoverage)
87 | debug(
88 | 'There are %d key(s) in %s',
89 | finalCoverageKeys.length,
90 | jsonReportFilename
91 | )
92 |
93 | finalCoverageKeys.forEach((key) => {
94 | const s = finalCoverage[key].s || {}
95 | const statements = Object.keys(s)
96 | const totalStatements = statements.length
97 | let coveredStatements = 0
98 | statements.forEach((statementKey) => {
99 | if (s[statementKey]) {
100 | coveredStatements += 1
101 | }
102 | })
103 |
104 | const hasStatements = totalStatements > 0
105 | const allCovered = coveredStatements === totalStatements
106 | const coverageStatus = hasStatements ? (allCovered ? '✅' : '⚠️') : '❓'
107 |
108 | debug(
109 | '%s %s statements covered %d/%d',
110 | coverageStatus,
111 | key,
112 | coveredStatements,
113 | totalStatements
114 | )
115 | })
116 | }
117 |
118 | const tasks = {
119 | /**
120 | * Clears accumulated code coverage information.
121 | *
122 | * Interactive mode with "cypress open"
123 | * - running a single spec or "Run all specs" needs to reset coverage
124 | * Headless mode with "cypress run"
125 | * - runs EACH spec separately, so we cannot reset the coverage
126 | * or we will lose the coverage from previous specs.
127 | */
128 | resetCoverage({ isInteractive }) {
129 | if (isInteractive) {
130 | debug('reset code coverage in interactive mode')
131 | coverageMap = istanbul.createCoverageMap({})
132 | saveCoverage(coverageMap)
133 | }
134 | /*
135 | Else:
136 | in headless mode, assume the coverage file was deleted
137 | before the `cypress run` command was called
138 | example: rm -rf .nyc_output || true
139 | */
140 |
141 | return null
142 | },
143 |
144 | /**
145 | * Combines coverage information from single test
146 | * with previously collected coverage.
147 | *
148 | * @param {string} sentCoverage Stringified coverage object sent by the test runner
149 | * @returns {null} Nothing is returned from this task
150 | */
151 | combineCoverage(sentCoverage) {
152 | const coverage = JSON.parse(sentCoverage)
153 | debug('parsed sent coverage')
154 |
155 | fixSourcePaths(coverage)
156 |
157 | coverageMap.merge(coverage)
158 |
159 | return null
160 | },
161 |
162 | /**
163 | * Saves coverage information as a JSON file and calls
164 | * NPM script to generate HTML report
165 | */
166 | coverageReport() {
167 | saveCoverage(coverageMap)
168 | if (!existsSync(nycFilename)) {
169 | console.warn('Cannot find coverage file %s', nycFilename)
170 | console.warn('Skipping coverage report')
171 | return null
172 | }
173 |
174 | showNycInfo(nycFilename)
175 |
176 | const allSourceFilesMissing = checkAllPathsNotFound(nycFilename)
177 | if (allSourceFilesMissing) {
178 | tryFindingLocalFiles(nycFilename)
179 | }
180 |
181 | resolveRelativePaths(nycFilename)
182 |
183 | if (customNycReportScript) {
184 | debug(
185 | 'saving coverage report using script "%s" from package.json, command: %s',
186 | DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME,
187 | customNycReportScript
188 | )
189 | debug('current working directory is %s', process.cwd())
190 | return execa('npm', ['run', DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME], {
191 | stdio: 'inherit'
192 | })
193 | }
194 |
195 | if (nycReportOptions.all) {
196 | debug('nyc needs to report on all included files')
197 | includeAllFiles(nycFilename, nycReportOptions)
198 | }
199 |
200 | debug('calling NYC reporter with options %o', nycReportOptions)
201 | debug('current working directory is %s', process.cwd())
202 | const NYC = require('nyc')
203 | const nyc = new NYC(nycReportOptions)
204 |
205 | const returnReportFolder = () => {
206 | const reportFolder = nycReportOptions['report-dir']
207 | debug(
208 | 'after reporting, returning the report folder name %s',
209 | reportFolder
210 | )
211 |
212 | maybePrintFinalCoverageFiles(reportFolder)
213 |
214 | return reportFolder
215 | }
216 | return nyc.report().then(returnReportFolder)
217 | }
218 | }
219 |
220 | /**
221 | * Registers code coverage collection and reporting tasks.
222 | * Sets an environment variable to tell the browser code that it can
223 | * send the coverage.
224 | * @example
225 | ```
226 | // your plugins file
227 | module.exports = (on, config) => {
228 | require('cypress/code-coverage/task')(on, config)
229 | // IMPORTANT to return the config object
230 | // with the any changed environment variables
231 | return config
232 | }
233 | ```
234 | */
235 | function registerCodeCoverageTasks(on, config) {
236 | on('task', tasks)
237 |
238 | // set a variable to let the hooks running in the browser
239 | // know that they can send coverage commands
240 | config.env.codeCoverageTasksRegistered = true
241 |
242 | return config
243 | }
244 |
245 | module.exports = registerCodeCoverageTasks
246 |
--------------------------------------------------------------------------------
/test-apps/all-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/all-files/README.md:
--------------------------------------------------------------------------------
1 | # example: all files
2 |
--------------------------------------------------------------------------------
/test-apps/all-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234'
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/all-files/cypress/e2e/spec.cy.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 |
--------------------------------------------------------------------------------
/test-apps/all-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/all-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/all-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/test-apps/all-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
17 |
18 |
--------------------------------------------------------------------------------
/test-apps/all-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/all-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-all-files",
3 | "description": "Report all files",
4 | "private": true,
5 | "scripts": {
6 | "cy:run": "cypress run",
7 | "start": "parcel serve index.html",
8 | "start:windows": "npx bin-up parcel serve index.html",
9 | "pretest": "rimraf .nyc_output .cache coverage dist",
10 | "test": "start-test 1234 cy:run",
11 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
12 | "coverage:check-files": "check-coverage main.js && check-coverage second.js && check-coverage not-covered.js && check-coverage cypress.config.js && only-covered --from coverage/coverage-final.json main.js second.js not-covered.js cypress.config.js"
13 | },
14 | "nyc": {
15 | "all": true,
16 | "include": "*.js"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.22.15"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/backend/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/backend/README.md:
--------------------------------------------------------------------------------
1 | # example: backend
2 |
3 | > Getting code coverage from backend
4 |
--------------------------------------------------------------------------------
/test-apps/backend/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | env: {
6 | codeCoverage: {
7 | url: 'http://localhost:3003/__coverage__',
8 | expectBackendCoverageOnly: true,
9 | },
10 | },
11 | e2e: {
12 | setupNodeEvents(on, config) {
13 | return require('./cypress/plugins/index.js')(on, config)
14 | },
15 | baseUrl: 'http://localhost:3003',
16 | },
17 | })
18 |
--------------------------------------------------------------------------------
/test-apps/backend/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('has backend code coverage', () => {
3 | cy.visit('/')
4 | cy.request('/hello')
5 | })
6 |
--------------------------------------------------------------------------------
/test-apps/backend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/backend/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/backend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-backend",
3 | "description": "Code coverage for backend",
4 | "private": true,
5 | "scripts": {
6 | "cy:run": "cypress run",
7 | "start": "nyc --silent node server/server",
8 | "pretest": "rimraf .nyc_output .cache coverage dist",
9 | "test": "start-test 3003 cy:run",
10 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
11 | "coverage:check-files": "check-coverage server.js && only-covered server.js"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test-apps/backend/server/index.html:
--------------------------------------------------------------------------------
1 |
2 | test backend
3 |
4 |
--------------------------------------------------------------------------------
/test-apps/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('@cypress/code-coverage/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 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/README.md:
--------------------------------------------------------------------------------
1 | # Test Case: Batch Send Coverage
2 | This test app tests that all expected files are covered when using
3 | the `sendCoverageBatchSize` environment variable in the Cypress
4 | configuration file.
5 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | env: {
6 | sendCoverageBatchSize: 1
7 | },
8 | e2e: {
9 | setupNodeEvents(on, config) {
10 | return require('./cypress/plugins/index.js')(on, config)
11 | },
12 | baseUrl: 'http://localhost:1234'
13 | }
14 | })
15 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/cypress/e2e/spec.cy.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 | cy.window()
11 | .invoke('numsTimesTwo', [1, 2, 3])
12 | .should('deep.equal', [2, 4, 6])
13 |
14 | cy.window()
15 | .invoke('add', 2, 3)
16 | .should('equal', 5)
17 |
18 | cy.window()
19 | .invoke('sub', 5, 2)
20 | .should('equal', 3)
21 |
22 | // application's code should be instrumented
23 | cy.window().should('have.property', '__coverage__')
24 | })
25 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
6 | // use functions creates in "main.js"
7 | if (add(2, 3) !== 5) {
8 | throw new Error('wrong addition')
9 | }
10 | if (sub(2, 3) !== -1) {
11 | throw new Error('wrong subtraction')
12 | }
13 | if (reverse('foo') !== 'oof') {
14 | throw new Error('wrong string reverse')
15 | }
16 |
17 |
18 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/package-lock.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-all-files",
3 | "lockfileVersion": 3,
4 | "requires": true,
5 | "packages": {
6 | "": {
7 | "name": "example-all-files",
8 | "devDependencies": {
9 | "@babel/core": "^7.22.15"
10 | }
11 | },
12 | "node_modules/@ampproject/remapping": {
13 | "version": "2.3.0",
14 | "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
15 | "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
16 | "dev": true,
17 | "license": "Apache-2.0",
18 | "dependencies": {
19 | "@jridgewell/gen-mapping": "^0.3.5",
20 | "@jridgewell/trace-mapping": "^0.3.24"
21 | },
22 | "engines": {
23 | "node": ">=6.0.0"
24 | }
25 | },
26 | "node_modules/@babel/code-frame": {
27 | "version": "7.27.1",
28 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
29 | "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==",
30 | "dev": true,
31 | "license": "MIT",
32 | "dependencies": {
33 | "@babel/helper-validator-identifier": "^7.27.1",
34 | "js-tokens": "^4.0.0",
35 | "picocolors": "^1.1.1"
36 | },
37 | "engines": {
38 | "node": ">=6.9.0"
39 | }
40 | },
41 | "node_modules/@babel/compat-data": {
42 | "version": "7.27.1",
43 | "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.27.1.tgz",
44 | "integrity": "sha512-Q+E+rd/yBzNQhXkG+zQnF58e4zoZfBedaxwzPmicKsiK3nt8iJYrSrDbjwFFDGC4f+rPafqRaPH6TsDoSvMf7A==",
45 | "dev": true,
46 | "license": "MIT",
47 | "engines": {
48 | "node": ">=6.9.0"
49 | }
50 | },
51 | "node_modules/@babel/core": {
52 | "version": "7.27.1",
53 | "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.27.1.tgz",
54 | "integrity": "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ==",
55 | "dev": true,
56 | "license": "MIT",
57 | "dependencies": {
58 | "@ampproject/remapping": "^2.2.0",
59 | "@babel/code-frame": "^7.27.1",
60 | "@babel/generator": "^7.27.1",
61 | "@babel/helper-compilation-targets": "^7.27.1",
62 | "@babel/helper-module-transforms": "^7.27.1",
63 | "@babel/helpers": "^7.27.1",
64 | "@babel/parser": "^7.27.1",
65 | "@babel/template": "^7.27.1",
66 | "@babel/traverse": "^7.27.1",
67 | "@babel/types": "^7.27.1",
68 | "convert-source-map": "^2.0.0",
69 | "debug": "^4.1.0",
70 | "gensync": "^1.0.0-beta.2",
71 | "json5": "^2.2.3",
72 | "semver": "^6.3.1"
73 | },
74 | "engines": {
75 | "node": ">=6.9.0"
76 | },
77 | "funding": {
78 | "type": "opencollective",
79 | "url": "https://opencollective.com/babel"
80 | }
81 | },
82 | "node_modules/@babel/generator": {
83 | "version": "7.27.1",
84 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.1.tgz",
85 | "integrity": "sha512-UnJfnIpc/+JO0/+KRVQNGU+y5taA5vCbwN8+azkX6beii/ZF+enZJSOKo11ZSzGJjlNfJHfQtmQT8H+9TXPG2w==",
86 | "dev": true,
87 | "license": "MIT",
88 | "dependencies": {
89 | "@babel/parser": "^7.27.1",
90 | "@babel/types": "^7.27.1",
91 | "@jridgewell/gen-mapping": "^0.3.5",
92 | "@jridgewell/trace-mapping": "^0.3.25",
93 | "jsesc": "^3.0.2"
94 | },
95 | "engines": {
96 | "node": ">=6.9.0"
97 | }
98 | },
99 | "node_modules/@babel/helper-compilation-targets": {
100 | "version": "7.27.1",
101 | "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.1.tgz",
102 | "integrity": "sha512-2YaDd/Rd9E598B5+WIc8wJPmWETiiJXFYVE60oX8FDohv7rAUU3CQj+A1MgeEmcsk2+dQuEjIe/GDvig0SqL4g==",
103 | "dev": true,
104 | "license": "MIT",
105 | "dependencies": {
106 | "@babel/compat-data": "^7.27.1",
107 | "@babel/helper-validator-option": "^7.27.1",
108 | "browserslist": "^4.24.0",
109 | "lru-cache": "^5.1.1",
110 | "semver": "^6.3.1"
111 | },
112 | "engines": {
113 | "node": ">=6.9.0"
114 | }
115 | },
116 | "node_modules/@babel/helper-module-imports": {
117 | "version": "7.27.1",
118 | "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz",
119 | "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==",
120 | "dev": true,
121 | "license": "MIT",
122 | "dependencies": {
123 | "@babel/traverse": "^7.27.1",
124 | "@babel/types": "^7.27.1"
125 | },
126 | "engines": {
127 | "node": ">=6.9.0"
128 | }
129 | },
130 | "node_modules/@babel/helper-module-transforms": {
131 | "version": "7.27.1",
132 | "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.1.tgz",
133 | "integrity": "sha512-9yHn519/8KvTU5BjTVEEeIM3w9/2yXNKoD82JifINImhpKkARMJKPP59kLo+BafpdN5zgNeIcS4jsGDmd3l58g==",
134 | "dev": true,
135 | "license": "MIT",
136 | "dependencies": {
137 | "@babel/helper-module-imports": "^7.27.1",
138 | "@babel/helper-validator-identifier": "^7.27.1",
139 | "@babel/traverse": "^7.27.1"
140 | },
141 | "engines": {
142 | "node": ">=6.9.0"
143 | },
144 | "peerDependencies": {
145 | "@babel/core": "^7.0.0"
146 | }
147 | },
148 | "node_modules/@babel/helper-string-parser": {
149 | "version": "7.27.1",
150 | "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
151 | "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
152 | "dev": true,
153 | "license": "MIT",
154 | "engines": {
155 | "node": ">=6.9.0"
156 | }
157 | },
158 | "node_modules/@babel/helper-validator-identifier": {
159 | "version": "7.27.1",
160 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz",
161 | "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==",
162 | "dev": true,
163 | "license": "MIT",
164 | "engines": {
165 | "node": ">=6.9.0"
166 | }
167 | },
168 | "node_modules/@babel/helper-validator-option": {
169 | "version": "7.27.1",
170 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
171 | "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
172 | "dev": true,
173 | "license": "MIT",
174 | "engines": {
175 | "node": ">=6.9.0"
176 | }
177 | },
178 | "node_modules/@babel/helpers": {
179 | "version": "7.27.1",
180 | "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.1.tgz",
181 | "integrity": "sha512-FCvFTm0sWV8Fxhpp2McP5/W53GPllQ9QeQ7SiqGWjMf/LVG07lFa5+pgK05IRhVwtvafT22KF+ZSnM9I545CvQ==",
182 | "dev": true,
183 | "license": "MIT",
184 | "dependencies": {
185 | "@babel/template": "^7.27.1",
186 | "@babel/types": "^7.27.1"
187 | },
188 | "engines": {
189 | "node": ">=6.9.0"
190 | }
191 | },
192 | "node_modules/@babel/parser": {
193 | "version": "7.27.1",
194 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.1.tgz",
195 | "integrity": "sha512-I0dZ3ZpCrJ1c04OqlNsQcKiZlsrXf/kkE4FXzID9rIOYICsAbA8mMDzhW/luRNAHdCNt7os/u8wenklZDlUVUQ==",
196 | "dev": true,
197 | "license": "MIT",
198 | "dependencies": {
199 | "@babel/types": "^7.27.1"
200 | },
201 | "bin": {
202 | "parser": "bin/babel-parser.js"
203 | },
204 | "engines": {
205 | "node": ">=6.0.0"
206 | }
207 | },
208 | "node_modules/@babel/template": {
209 | "version": "7.27.1",
210 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.1.tgz",
211 | "integrity": "sha512-Fyo3ghWMqkHHpHQCoBs2VnYjR4iWFFjguTDEqA5WgZDOrFesVjMhMM2FSqTKSoUSDO1VQtavj8NFpdRBEvJTtg==",
212 | "dev": true,
213 | "license": "MIT",
214 | "dependencies": {
215 | "@babel/code-frame": "^7.27.1",
216 | "@babel/parser": "^7.27.1",
217 | "@babel/types": "^7.27.1"
218 | },
219 | "engines": {
220 | "node": ">=6.9.0"
221 | }
222 | },
223 | "node_modules/@babel/traverse": {
224 | "version": "7.27.1",
225 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.1.tgz",
226 | "integrity": "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg==",
227 | "dev": true,
228 | "license": "MIT",
229 | "dependencies": {
230 | "@babel/code-frame": "^7.27.1",
231 | "@babel/generator": "^7.27.1",
232 | "@babel/parser": "^7.27.1",
233 | "@babel/template": "^7.27.1",
234 | "@babel/types": "^7.27.1",
235 | "debug": "^4.3.1",
236 | "globals": "^11.1.0"
237 | },
238 | "engines": {
239 | "node": ">=6.9.0"
240 | }
241 | },
242 | "node_modules/@babel/types": {
243 | "version": "7.27.1",
244 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.1.tgz",
245 | "integrity": "sha512-+EzkxvLNfiUeKMgy/3luqfsCWFRXLb7U6wNQTk60tovuckwB15B191tJWvpp4HjiQWdJkCxO3Wbvc6jlk3Xb2Q==",
246 | "dev": true,
247 | "license": "MIT",
248 | "dependencies": {
249 | "@babel/helper-string-parser": "^7.27.1",
250 | "@babel/helper-validator-identifier": "^7.27.1"
251 | },
252 | "engines": {
253 | "node": ">=6.9.0"
254 | }
255 | },
256 | "node_modules/@jridgewell/gen-mapping": {
257 | "version": "0.3.5",
258 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
259 | "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
260 | "dev": true,
261 | "license": "MIT",
262 | "dependencies": {
263 | "@jridgewell/set-array": "^1.2.1",
264 | "@jridgewell/sourcemap-codec": "^1.4.10",
265 | "@jridgewell/trace-mapping": "^0.3.24"
266 | },
267 | "engines": {
268 | "node": ">=6.0.0"
269 | }
270 | },
271 | "node_modules/@jridgewell/resolve-uri": {
272 | "version": "3.1.2",
273 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
274 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
275 | "dev": true,
276 | "license": "MIT",
277 | "engines": {
278 | "node": ">=6.0.0"
279 | }
280 | },
281 | "node_modules/@jridgewell/set-array": {
282 | "version": "1.2.1",
283 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
284 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
285 | "dev": true,
286 | "license": "MIT",
287 | "engines": {
288 | "node": ">=6.0.0"
289 | }
290 | },
291 | "node_modules/@jridgewell/sourcemap-codec": {
292 | "version": "1.5.0",
293 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
294 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
295 | "dev": true,
296 | "license": "MIT"
297 | },
298 | "node_modules/@jridgewell/trace-mapping": {
299 | "version": "0.3.25",
300 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
301 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
302 | "dev": true,
303 | "license": "MIT",
304 | "dependencies": {
305 | "@jridgewell/resolve-uri": "^3.1.0",
306 | "@jridgewell/sourcemap-codec": "^1.4.14"
307 | }
308 | },
309 | "node_modules/browserslist": {
310 | "version": "4.24.0",
311 | "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.0.tgz",
312 | "integrity": "sha512-Rmb62sR1Zpjql25eSanFGEhAxcFwfA1K0GuQcLoaJBAcENegrQut3hYdhXFF1obQfiDyqIW/cLM5HSJ/9k884A==",
313 | "dev": true,
314 | "funding": [
315 | {
316 | "type": "opencollective",
317 | "url": "https://opencollective.com/browserslist"
318 | },
319 | {
320 | "type": "tidelift",
321 | "url": "https://tidelift.com/funding/github/npm/browserslist"
322 | },
323 | {
324 | "type": "github",
325 | "url": "https://github.com/sponsors/ai"
326 | }
327 | ],
328 | "license": "MIT",
329 | "dependencies": {
330 | "caniuse-lite": "^1.0.30001663",
331 | "electron-to-chromium": "^1.5.28",
332 | "node-releases": "^2.0.18",
333 | "update-browserslist-db": "^1.1.0"
334 | },
335 | "bin": {
336 | "browserslist": "cli.js"
337 | },
338 | "engines": {
339 | "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
340 | }
341 | },
342 | "node_modules/caniuse-lite": {
343 | "version": "1.0.30001666",
344 | "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001666.tgz",
345 | "integrity": "sha512-gD14ICmoV5ZZM1OdzPWmpx+q4GyefaK06zi8hmfHV5xe4/2nOQX3+Dw5o+fSqOws2xVwL9j+anOPFwHzdEdV4g==",
346 | "dev": true,
347 | "funding": [
348 | {
349 | "type": "opencollective",
350 | "url": "https://opencollective.com/browserslist"
351 | },
352 | {
353 | "type": "tidelift",
354 | "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
355 | },
356 | {
357 | "type": "github",
358 | "url": "https://github.com/sponsors/ai"
359 | }
360 | ],
361 | "license": "CC-BY-4.0"
362 | },
363 | "node_modules/convert-source-map": {
364 | "version": "2.0.0",
365 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
366 | "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
367 | "dev": true,
368 | "license": "MIT"
369 | },
370 | "node_modules/debug": {
371 | "version": "4.3.7",
372 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz",
373 | "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==",
374 | "dev": true,
375 | "license": "MIT",
376 | "dependencies": {
377 | "ms": "^2.1.3"
378 | },
379 | "engines": {
380 | "node": ">=6.0"
381 | },
382 | "peerDependenciesMeta": {
383 | "supports-color": {
384 | "optional": true
385 | }
386 | }
387 | },
388 | "node_modules/electron-to-chromium": {
389 | "version": "1.5.31",
390 | "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.31.tgz",
391 | "integrity": "sha512-QcDoBbQeYt0+3CWcK/rEbuHvwpbT/8SV9T3OSgs6cX1FlcUAkgrkqbg9zLnDrMM/rLamzQwal4LYFCiWk861Tg==",
392 | "dev": true,
393 | "license": "ISC"
394 | },
395 | "node_modules/escalade": {
396 | "version": "3.2.0",
397 | "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
398 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
399 | "dev": true,
400 | "license": "MIT",
401 | "engines": {
402 | "node": ">=6"
403 | }
404 | },
405 | "node_modules/gensync": {
406 | "version": "1.0.0-beta.2",
407 | "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
408 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
409 | "dev": true,
410 | "license": "MIT",
411 | "engines": {
412 | "node": ">=6.9.0"
413 | }
414 | },
415 | "node_modules/globals": {
416 | "version": "11.12.0",
417 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
418 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
419 | "dev": true,
420 | "license": "MIT",
421 | "engines": {
422 | "node": ">=4"
423 | }
424 | },
425 | "node_modules/js-tokens": {
426 | "version": "4.0.0",
427 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
428 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
429 | "dev": true,
430 | "license": "MIT"
431 | },
432 | "node_modules/jsesc": {
433 | "version": "3.0.2",
434 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.0.2.tgz",
435 | "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==",
436 | "dev": true,
437 | "license": "MIT",
438 | "bin": {
439 | "jsesc": "bin/jsesc"
440 | },
441 | "engines": {
442 | "node": ">=6"
443 | }
444 | },
445 | "node_modules/json5": {
446 | "version": "2.2.3",
447 | "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
448 | "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
449 | "dev": true,
450 | "license": "MIT",
451 | "bin": {
452 | "json5": "lib/cli.js"
453 | },
454 | "engines": {
455 | "node": ">=6"
456 | }
457 | },
458 | "node_modules/lru-cache": {
459 | "version": "5.1.1",
460 | "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
461 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
462 | "dev": true,
463 | "license": "ISC",
464 | "dependencies": {
465 | "yallist": "^3.0.2"
466 | }
467 | },
468 | "node_modules/ms": {
469 | "version": "2.1.3",
470 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
471 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
472 | "dev": true,
473 | "license": "MIT"
474 | },
475 | "node_modules/node-releases": {
476 | "version": "2.0.18",
477 | "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
478 | "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
479 | "dev": true,
480 | "license": "MIT"
481 | },
482 | "node_modules/picocolors": {
483 | "version": "1.1.1",
484 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
485 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
486 | "dev": true,
487 | "license": "ISC"
488 | },
489 | "node_modules/semver": {
490 | "version": "6.3.1",
491 | "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
492 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
493 | "dev": true,
494 | "license": "ISC",
495 | "bin": {
496 | "semver": "bin/semver.js"
497 | }
498 | },
499 | "node_modules/update-browserslist-db": {
500 | "version": "1.1.1",
501 | "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
502 | "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
503 | "dev": true,
504 | "funding": [
505 | {
506 | "type": "opencollective",
507 | "url": "https://opencollective.com/browserslist"
508 | },
509 | {
510 | "type": "tidelift",
511 | "url": "https://tidelift.com/funding/github/npm/browserslist"
512 | },
513 | {
514 | "type": "github",
515 | "url": "https://github.com/sponsors/ai"
516 | }
517 | ],
518 | "license": "MIT",
519 | "dependencies": {
520 | "escalade": "^3.2.0",
521 | "picocolors": "^1.1.0"
522 | },
523 | "bin": {
524 | "update-browserslist-db": "cli.js"
525 | },
526 | "peerDependencies": {
527 | "browserslist": ">= 4.21.0"
528 | }
529 | },
530 | "node_modules/yallist": {
531 | "version": "3.1.1",
532 | "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
533 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
534 | "dev": true,
535 | "license": "ISC"
536 | }
537 | }
538 | }
539 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-all-files",
3 | "description": "Report all files",
4 | "private": true,
5 | "scripts": {
6 | "cy:run": "cypress run",
7 | "start": "parcel serve index.html",
8 | "start:windows": "npx bin-up parcel serve index.html",
9 | "pretest": "rimraf .nyc_output .cache coverage dist",
10 | "test": "DEBUG=code-coverage DEBUG_DEPTH=10 start-test 1234 cy:run",
11 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
12 | "coverage:check-files": "check-coverage main.js second.js third.js cypress.config.js --from coverage/coverage-final.json"
13 | },
14 | "nyc": {
15 | "all": true,
16 | "include": "*.js"
17 | },
18 | "devDependencies": {
19 | "@babel/core": "^7.22.15"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/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 |
--------------------------------------------------------------------------------
/test-apps/batch-send-coverage/third.js:
--------------------------------------------------------------------------------
1 | window.numsTimesTwo = nums => nums.map(n => n * 2)
--------------------------------------------------------------------------------
/test-apps/before-all-visit/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/before-all-visit/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234',
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/before-all-visit/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('coverage information', () => {
3 | before(() => {
4 | cy.log('visiting /')
5 | cy.visit('/')
6 | })
7 |
8 | it('calls add & sub', () => {
9 | cy.window()
10 | .invoke('add', 2, 3)
11 | .should('equal', 5)
12 |
13 | cy.window()
14 | .invoke('sub', 2, 3)
15 | .should('equal', -1)
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/test-apps/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 | }
--------------------------------------------------------------------------------
/test-apps/before-all-visit/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | // IMPORTANT to return the config object
4 | // with the any changed environment variables
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/test-apps/before-all-visit/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/before-all-visit/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/test-apps/before-all-visit/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/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 | "scripts": {
5 | "start": "parcel serve index.html",
6 | "cy:run": "cypress run",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage main.js && only-covered main.js"
11 | },
12 | "devDependencies": {
13 | "@babel/core": "^7.22.15"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test-apps/before-each-visit/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/before-each-visit/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234',
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/before-each-visit/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | describe('coverage information', () => {
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 |
--------------------------------------------------------------------------------
/test-apps/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 | }
--------------------------------------------------------------------------------
/test-apps/before-each-visit/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | // IMPORTANT to return the config object
4 | // with the any changed environment variables
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/test-apps/before-each-visit/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/before-each-visit/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/test-apps/before-each-visit/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/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 | "scripts": {
5 | "start": "parcel serve index.html",
6 | "cy:run": "cypress run",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage main.js && only-covered --from coverage/coverage-final.json main.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/README.md:
--------------------------------------------------------------------------------
1 | # example: create react app with both e2e and component testing examples
2 |
3 | This example has both e2e and CT tests, which are run sequentially in the `cy:run` script.
4 | It uses the [@cypress/instrument-cra](https://github.com/cypress-io/instrument-cra) package to instrument the
5 | React code before the tests run.
6 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/cypress.config.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'cypress'
2 | import '@cypress/instrument-cra'
3 |
4 | export default defineConfig({
5 | env: {
6 | codeCoverage: {
7 | exclude: 'cypress/**/*.*'
8 | }
9 | },
10 | e2e: {
11 | setupNodeEvents(on, config) {
12 | require('@cypress/code-coverage/task')(on, config)
13 | return config
14 | }
15 | },
16 |
17 | component: {
18 | devServer: {
19 | framework: 'create-react-app',
20 | bundler: 'webpack'
21 | },
22 | setupNodeEvents(on, config) {
23 | require('@cypress/code-coverage/task')(on, config)
24 | return config
25 | }
26 | }
27 | })
28 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/cypress/e2e/spec.cy.ts:
--------------------------------------------------------------------------------
1 | describe('empty spec', () => {
2 | it('passes', () => {
3 | cy.visit('http://localhost:3000')
4 | })
5 | })
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/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 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/cypress/support/commands.ts:
--------------------------------------------------------------------------------
1 | ///
2 | // ***********************************************
3 | // This example commands.ts shows you how to
4 | // create various custom commands and overwrite
5 | // existing commands.
6 | //
7 | // For more comprehensive examples of custom
8 | // commands please read more here:
9 | // https://on.cypress.io/custom-commands
10 | // ***********************************************
11 | //
12 | //
13 | // -- This is a parent command --
14 | // Cypress.Commands.add('login', (email, password) => { ... })
15 | //
16 | //
17 | // -- This is a child command --
18 | // Cypress.Commands.add('drag', { prevSubject: 'element'}, (subject, options) => { ... })
19 | //
20 | //
21 | // -- This is a dual command --
22 | // Cypress.Commands.add('dismiss', { prevSubject: 'optional'}, (subject, options) => { ... })
23 | //
24 | //
25 | // -- This will overwrite an existing command --
26 | // Cypress.Commands.overwrite('visit', (originalFn, url, options) => { ... })
27 | //
28 | // declare global {
29 | // namespace Cypress {
30 | // interface Chainable {
31 | // login(email: string, password: string): Chainable
32 | // drag(subject: string, options?: Partial): Chainable
33 | // dismiss(subject: string, options?: Partial): Chainable
34 | // visit(originalFn: CommandOriginalFn, url: string, options: Partial): Chainable
35 | // }
36 | // }
37 | // }
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/cypress/support/component-index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Components App
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/cypress/support/component.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/component.ts is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 | import '@cypress/code-coverage/support'
19 |
20 | // Alternatively you can use CommonJS syntax:
21 | // require('./commands')
22 |
23 | import { mount } from 'cypress/react'
24 |
25 | // Augment the Cypress namespace to include type definitions for
26 | // your custom command.
27 | // Alternatively, can be defined in cypress/support/component.d.ts
28 | // with a at the top of your spec.
29 | declare global {
30 | namespace Cypress {
31 | interface Chainable {
32 | mount: typeof mount
33 | }
34 | }
35 | }
36 |
37 | Cypress.Commands.add('mount', mount)
38 |
39 | // Example use:
40 | // cy.mount()
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/cypress/support/e2e.ts:
--------------------------------------------------------------------------------
1 | // ***********************************************************
2 | // This example support/e2e.ts is processed and
3 | // loaded automatically before your test files.
4 | //
5 | // This is a great place to put global configuration and
6 | // behavior that modifies Cypress.
7 | //
8 | // You can change the location of this file or turn off
9 | // automatically serving support files with the
10 | // 'supportFile' configuration option.
11 | //
12 | // You can read more here:
13 | // https://on.cypress.io/configuration
14 | // ***********************************************************
15 |
16 | // Import commands.js using ES2015 syntax:
17 | import './commands'
18 | import '@cypress/code-coverage/support'
19 |
20 | // Alternatively you can use CommonJS syntax:
21 | // require('./commands')
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cra-e2e-and-ct",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@cypress/instrument-cra": "^1.4.0",
7 | "@testing-library/jest-dom": "^5.16.4",
8 | "@testing-library/react": "^13.3.0",
9 | "@testing-library/user-event": "^13.5.0",
10 | "@types/jest": "^29.0.0",
11 | "@types/node": "^22.0.0",
12 | "@types/react": "^18.0.12",
13 | "@types/react-dom": "^18.0.5",
14 | "react": "^18.1.0",
15 | "react-dom": "^18.1.0",
16 | "react-scripts": "5.0.1",
17 | "typescript": "^4.7.3",
18 | "web-vitals": "^2.1.4"
19 | },
20 | "scripts": {
21 | "start": "react-scripts -r @cypress/instrument-cra start",
22 | "build": "react-scripts build",
23 | "cy:run": "cypress run --e2e && cypress run --component",
24 | "pretest": "npm ci && rimraf .nyc_output .cache coverage dist",
25 | "test": "start-test 3000 cy:run",
26 | "eject": "react-scripts eject",
27 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
28 | "coverage:check-files": "check-coverage src/index.tsx && check-coverage src/App.tsx && check-coverage src/components/Button.tsx && check-coverage src/components/Stepper.tsx && only-covered src/index.tsx src/App.tsx src/components/Button.tsx src/components/Stepper.tsx"
29 | },
30 | "eslintConfig": {
31 | "extends": [
32 | "react-app",
33 | "react-app/jest"
34 | ]
35 | },
36 | "browserslist": {
37 | "production": [
38 | ">0.2%",
39 | "not dead",
40 | "not op_mini all"
41 | ],
42 | "development": [
43 | "last 1 chrome version",
44 | "last 1 firefox version",
45 | "last 1 safari version"
46 | ]
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/test-apps/cra-e2e-and-ct/public/favicon.ico
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/test-apps/cra-e2e-and-ct/public/logo192.png
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/test-apps/cra-e2e-and-ct/public/logo512.png
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render();
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/App.tsx:
--------------------------------------------------------------------------------
1 | import './App.css';
2 | import Stepper from './components/Stepper';
3 |
4 | function App() {
5 | return (
6 |
7 |
8 |
9 | );
10 | }
11 |
12 | export default App;
13 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/components/Button.cy.tsx:
--------------------------------------------------------------------------------
1 | import Button from './Button'
2 |
3 | describe('Button', () => {
4 | it('should have text', () => {
5 | cy.mount()
6 | cy.get('button').should('contain.text', 'Click me!')
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 |
3 | interface ButtonProps extends React.ButtonHTMLAttributes {}
4 |
5 | const Button: React.FC = ({ children }) => {
6 | return
7 | }
8 |
9 | export default Button
10 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/components/Stepper.cy.tsx:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import Stepper from './Stepper'
4 |
5 | describe('Stepper.cy.js', () => {
6 | const stepperSelector = '[data-testid=stepper]'
7 | const incrementSelector = '[aria-label=increment]'
8 | const decrementSelector = '[aria-label=decrement]'
9 |
10 | it('playground', () => {
11 | cy.mount()
12 | })
13 |
14 | it('stepper should default to 0', () => {
15 | // Arrange
16 | cy.mount()
17 | // Assert
18 | cy.get(stepperSelector).should('contain.text', 0)
19 | })
20 |
21 | it('can be incremented', () => {
22 | // Arrange
23 | cy.mount()
24 | // Act
25 | cy.get(incrementSelector).click()
26 | // Assert
27 | cy.get(stepperSelector).should('contain.text', 1)
28 | })
29 |
30 | it('can be decremented', () => {
31 | // Arrange
32 | cy.mount()
33 | // Act
34 | cy.get(decrementSelector).click()
35 | // Assert
36 | cy.get(stepperSelector).should('contain.text', -1)
37 | })
38 | })
39 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/components/Stepper.tsx:
--------------------------------------------------------------------------------
1 | import { useState } from 'react'
2 |
3 | export default function Stepper({ initial = 0, onChange = (num: any) => {} }) {
4 | const [count, setCount] = useState(initial)
5 |
6 | const increment = () => {
7 | const newCount = count + 1
8 | setCount(newCount)
9 | onChange(newCount)
10 | }
11 |
12 | const decrement = () => {
13 | const newCount = count - 1
14 | setCount(newCount)
15 | onChange(newCount)
16 | }
17 |
18 | return (
19 |
20 |
23 | {count}
24 |
27 |
28 | )
29 | }
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | }
9 |
10 | code {
11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
12 | monospace;
13 | }
14 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 |
6 | const root = ReactDOM.createRoot(
7 | document.getElementById('root') as HTMLElement
8 | );
9 | root.render(
10 |
11 |
12 |
13 | );
14 |
15 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/test-apps/cra-e2e-and-ct/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "allowJs": true,
10 | "skipLibCheck": true,
11 | "esModuleInterop": true,
12 | "allowSyntheticDefaultImports": true,
13 | "strict": true,
14 | "forceConsistentCasingInFileNames": true,
15 | "noFallthroughCasesInSwitch": true,
16 | "module": "esnext",
17 | "moduleResolution": "node",
18 | "resolveJsonModule": true,
19 | "isolatedModules": true,
20 | "noEmit": true,
21 | "jsx": "react-jsx"
22 | },
23 | "include": [
24 | "src", "cypress"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234',
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/cypress/e2e/spec.cy.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 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
17 |
18 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/exclude-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-exclude-files",
3 | "description": "Exclude some files from final coverage report",
4 | "scripts": {
5 | "cy:run": "cypress run",
6 | "start": "parcel serve index.html",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage main.js && only-covered --from coverage/coverage-final.json main.js"
11 | },
12 | "nyc": {
13 | "exclude": [
14 | "second.js"
15 | ],
16 | "excludeAfterRemap": true
17 | },
18 | "devDependencies": {
19 | "@babel/core": "7.27.1"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/frontend/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"],
3 | "ignore": ["**/*.cy.js"]
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/frontend/README.md:
--------------------------------------------------------------------------------
1 | # example: frontend
2 |
3 | Tests a frontend app
4 |
--------------------------------------------------------------------------------
/test-apps/frontend/about.html:
--------------------------------------------------------------------------------
1 |
2 | About
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/test-apps/frontend/about.js:
--------------------------------------------------------------------------------
1 | document
2 | .getElementById('content')
3 | .appendChild(document.createTextNode('Est. 2019'))
4 |
--------------------------------------------------------------------------------
/test-apps/frontend/app.js:
--------------------------------------------------------------------------------
1 | import { map } from 'lodash'
2 |
3 | const list = [{ name: 'joe' }, { name: 'mary' }]
4 | const names = map(list, 'name')
5 | console.log('just names', names)
6 |
--------------------------------------------------------------------------------
/test-apps/frontend/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | hosts: {
10 | 'foobar.com': '127.0.0.1',
11 | },
12 | baseUrl: 'http://localhost:1234',
13 | env: {
14 | codeCoverage: {
15 | exclude: ['cypress/**/*.*']
16 | }
17 | }
18 | }
19 | })
20 |
--------------------------------------------------------------------------------
/test-apps/frontend/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 |
7 | context('Page test', () => {
8 | beforeEach(() => {
9 | cy.visit('/', {
10 | onBeforeLoad(win) {
11 | cy.spy(win.console, 'log').as('log')
12 | }
13 | })
14 | })
15 |
16 | it('logs names', function() {
17 | cy.get('@log')
18 | .should('have.been.calledOnce')
19 | .should('have.been.calledWith', 'just names', ['joe', 'mary'])
20 | })
21 |
22 | it('loads About page', () => {
23 | cy.contains('About').click()
24 | cy.url().should('match', /\/about/)
25 | cy.contains('h2', 'About')
26 | cy.contains('Est. 2019')
27 | })
28 |
29 | it('loads cross origin page using cy.origin', () => {
30 | cy.origin('http://foobar.com:1234', () => {
31 | cy.visit('/')
32 | })
33 | })
34 |
35 | it('loads cross origin page without cy.origin', () => {
36 | cy.visit('http://foobar.com:1234')
37 | })
38 | })
39 |
40 | context('Unit tests', () => {
41 | it('adds numbers', () => {
42 | expect(add(2, 3)).to.equal(5)
43 | })
44 |
45 | it('concatenates strings', () => {
46 | expect(add('foo', 'Bar')).to.equal('fooBar')
47 | })
48 |
49 | })
50 |
--------------------------------------------------------------------------------
/test-apps/frontend/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | //Used to instrument code tested like unit tests
4 | on('file:preprocessor', require('@cypress/code-coverage/use-babelrc'))
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/test-apps/frontend/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/test-apps/frontend/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/test-apps/frontend/index.html:
--------------------------------------------------------------------------------
1 |
2 |
5 | Test page
6 | Open the DevTools to see console messages
7 |
8 |
9 |
--------------------------------------------------------------------------------
/test-apps/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-frontend",
3 | "description": "Tests a frontend app",
4 | "scripts": {
5 | "cy:run": "cypress run",
6 | "start": "parcel serve index.html",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage app.js && check-coverage about.js && check-coverage unit.js && only-covered app.js about.js unit.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test-apps/frontend/unit.js:
--------------------------------------------------------------------------------
1 | export const add = (a, b) => a + b
2 |
--------------------------------------------------------------------------------
/test-apps/fullstack/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/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 test
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 |
--------------------------------------------------------------------------------
/test-apps/fullstack/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | env: {
6 | codeCoverage: {
7 | url: 'http://localhost:1234/__coverage__',
8 | exclude: 'cypress/**/*.*'
9 | }
10 | },
11 | e2e: {
12 | setupNodeEvents(on, config) {
13 | return require('./cypress/plugins/index.js')(on, config)
14 | },
15 | baseUrl: 'http://localhost:1234'
16 | }
17 | })
18 |
--------------------------------------------------------------------------------
/test-apps/fullstack/cypress/e2e/spec.cy.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 |
--------------------------------------------------------------------------------
/test-apps/fullstack/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | //Used to instrument code ran like unit tests
4 | on('file:preprocessor', require('@cypress/code-coverage/use-babelrc'))
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/test-apps/fullstack/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/fullstack/images/fullstack.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cypress-io/code-coverage/127b589b25e5a9a22657ac6c89277044886bfb9e/test-apps/fullstack/images/fullstack.png
--------------------------------------------------------------------------------
/test-apps/fullstack/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test-apps/fullstack/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/fullstack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-fullstack",
3 | "description": "Combined code coverage from the backend code, and e2e and unit tests",
4 | "scripts": {
5 | "prestart": "parcel build index.html",
6 | "start": "nyc --silent node server/server",
7 | "cy:run": "cypress run",
8 | "pretest": "rimraf .nyc_output .cache coverage dist",
9 | "test": "start-test 1234 cy:run",
10 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
11 | "coverage:check-files": "check-coverage fullstack/server/server.js && check-coverage fullstack/main.js && check-coverage fullstack/string-utils.js && only-covered server.js main.js string-utils.js"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test-apps/fullstack/server/server.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const path = require('path')
3 | const app = express()
4 | const port = 1234
5 |
6 | // if there is code coverage information
7 | // then expose an endpoint that returns it
8 | /* istanbul ignore next */
9 | if (global.__coverage__) {
10 | console.log('have code coverage, will add middleware for express')
11 | console.log(`to fetch: GET :${port}/__coverage__`)
12 | require('@cypress/code-coverage/middleware/express')(app)
13 | }
14 |
15 | app.use(express.static(path.join(__dirname, '../dist')))
16 |
17 | app.get('/hello', (req, res) => {
18 | console.log('sending hello world')
19 | res.send('Hello World!')
20 | })
21 |
22 | app.listen(port, () => console.log(`Example app listening on port ${port}!`))
23 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/README.md:
--------------------------------------------------------------------------------
1 | # example: multiple backends
2 |
3 | > Getting code coverage from multiple backends
4 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | env: {
6 | codeCoverage: {
7 | url: ['http://localhost:3003/__coverage__', 'http://localhost:3004/__coverage__'],
8 | expectBackendCoverageOnly: true,
9 | },
10 | },
11 | e2e: {
12 | setupNodeEvents(on, config) {
13 | return require('./cypress/plugins/index.js')(on, config)
14 | },
15 | baseUrl: 'http://localhost:3003',
16 | },
17 | })
18 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('has multiple backends with code coverage', () => {
3 | cy.visit('/')
4 | cy.request('/hello')
5 | cy.request('http://localhost:3004/world')
6 | })
7 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-multiple-backends",
3 | "description": "Code coverage for multiple backends",
4 | "private": true,
5 | "scripts": {
6 | "cy:run": "cypress run",
7 | "start": "nyc --silent node server/server-3003 & nyc --silent node server/server-3004",
8 | "pretest": "rimraf .nyc_output .cache coverage dist",
9 | "test": "start-test \"3003|3004\" cy:run",
10 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
11 | "coverage:check-files": "check-coverage server-3003.js server-3004.js && only-covered server-3003.js server-3004.js"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/server/index.html:
--------------------------------------------------------------------------------
1 |
2 | test multiple backends
3 |
4 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/server/server-3003.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('@cypress/code-coverage/middleware/express')(app)
12 | }
13 |
14 | app.use(express.static(__dirname))
15 |
16 | app.get('/hello', (req, res) => {
17 | console.log('sending hello')
18 | res.send('Hello')
19 | })
20 |
21 | app.listen(port, () => console.log(`Example app listening on port ${port}!`))
22 |
--------------------------------------------------------------------------------
/test-apps/multiple-backends/server/server-3004.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const app = express()
3 | const port = 3004
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('@cypress/code-coverage/middleware/express')(app)
12 | }
13 |
14 | app.use(express.static(__dirname))
15 |
16 | app.get('/world', (req, res) => {
17 | console.log('sending world')
18 | res.send('World!')
19 | })
20 |
21 | app.listen(port, () => console.log(`Example app listening on port ${port}!`))
22 |
--------------------------------------------------------------------------------
/test-apps/one-spec/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/one-spec/README.md:
--------------------------------------------------------------------------------
1 | # example: one-spec
2 |
3 | Only running a single spec using a specFiles pattern
4 |
--------------------------------------------------------------------------------
/test-apps/one-spec/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234',
10 | specPattern: 'cypress/e2e/spec-a.cy.js',
11 | },
12 | })
13 |
--------------------------------------------------------------------------------
/test-apps/one-spec/cypress/e2e/spec-a.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('spec a', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 |
6 | cy.window()
7 | .invoke('add', 2, 3)
8 | .should('equal', 5)
9 |
10 | cy.window()
11 | .invoke('sub', 2, 3)
12 | .should('equal', -1)
13 | })
14 |
--------------------------------------------------------------------------------
/test-apps/one-spec/cypress/e2e/spec-b.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('spec b', () => {
3 | // should not run
4 | throw new Error('Spec b should not run')
5 | })
6 |
--------------------------------------------------------------------------------
/test-apps/one-spec/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/one-spec/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/one-spec/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/test-apps/one-spec/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/one-spec/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-one-spec",
3 | "description": "Only running a single spec",
4 | "scripts": {
5 | "cy:run": "cypress run",
6 | "start": "parcel serve index.html",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage main.js && only-covered main.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test-apps/redirect/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/redirect/.nycrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "exclude": [
3 | "app.js"
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/test-apps/redirect/README.md:
--------------------------------------------------------------------------------
1 | # example: redirect
2 |
3 | Tests a frontend app that redirects, through un-instrumented code, back to itself.
4 |
--------------------------------------------------------------------------------
/test-apps/redirect/app.js:
--------------------------------------------------------------------------------
1 | // This redirect code needs to be un-instrumented (excluded in .nycrc.json)
2 | // - If the redirect code is instrumented, Cypress would then treat them as different coverage objects and merge the code coverage (not testing what we want).
3 | // - If the redirect code is un-instrumented, Cypress can't tell them apart and will update the existing coverage object to point to the correct one (testing what we want).
4 |
5 | import { returnToApp } from './utils'
6 |
7 | // Timeouts are necessary to allow cypress to pick up the "initial" coverage object and compare it to the existing coverage objects.
8 | new Promise((resolve) => {
9 | if (window.location.port === '1234' && !localStorage.getItem('visited')) {
10 | localStorage.setItem('visited', true)
11 | console.log('Not visited. Redirecting')
12 | setTimeout(() => {
13 | window.location.href = 'http://localhost:1235'
14 | }, 500)
15 | } else if (window.location.port === '1235') {
16 | console.log('Redirecting back.')
17 | setTimeout(() => {
18 | window.location.href = 'http://localhost:1234'
19 | }, 500)
20 | } else {
21 | console.log('Visited');
22 | setTimeout(() => {
23 | resolve()
24 | }, 500)
25 | }
26 | }).then(() => {
27 | returnToApp()
28 | })
29 |
--------------------------------------------------------------------------------
/test-apps/redirect/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | require('@cypress/code-coverage/task')(on, config)
8 | return config
9 | },
10 | baseUrl: 'http://localhost:1234',
11 | env: {
12 | codeCoverage: {
13 | exclude: ['cypress/**/*.*']
14 | }
15 | },
16 | chromeWebSecurity: false
17 | }
18 | })
19 |
--------------------------------------------------------------------------------
/test-apps/redirect/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | // enables intelligent code completion for Cypress commands
2 | // https://on.cypress.io/intelligent-code-completion
3 | ///
4 |
5 | context('Page test', () => {
6 | it('redirects back to the app', function() {
7 | cy.clearLocalStorage()
8 | cy.visit("http://localhost:1234")
9 | cy.contains("Returned to app")
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/redirect/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/redirect/index.html:
--------------------------------------------------------------------------------
1 |
2 | Test page
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test-apps/redirect/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-redirect",
3 | "description": "Tests a frontend app that redirects, through un-instrumented code, back to itself.",
4 | "devDependencies": {
5 | "@babel/core": "^7.12.0"
6 | },
7 | "scripts": {
8 | "cy:run": "cypress run",
9 | "start:app": "parcel serve -p 1234 index.html",
10 | "start:other-app": "parcel serve -p 1235 index.html",
11 | "pretest": "rimraf .nyc_output .cache coverage dist",
12 | "test": "start-test start:app http://localhost:1234 start:other-app http://localhost:1235 cy:run",
13 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
14 | "coverage:check-files": "check-coverage utils.js && only-covered utils.js"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/test-apps/redirect/utils.js:
--------------------------------------------------------------------------------
1 | export const returnToApp = () => {
2 | document.body
3 | .appendChild(document.createTextNode('Returned to app'))
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/same-folder/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/same-folder/README.md:
--------------------------------------------------------------------------------
1 | # example: same-folder
2 |
3 | Check if test files are correctly filtered out
4 |
--------------------------------------------------------------------------------
/test-apps/same-folder/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | e2e: {
5 | setupNodeEvents(on, config) {
6 | return require('./plugins.js')(on, config)
7 | },
8 | specPattern: './**/spec.js',
9 | supportFile: 'support.js',
10 | baseUrl: 'http://localhost:1234',
11 | env: {
12 | codeCoverage: {
13 | exclude: ['spec.js', 'support.js']
14 | }
15 | }
16 | },
17 | })
18 |
--------------------------------------------------------------------------------
/test-apps/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 | }
--------------------------------------------------------------------------------
/test-apps/same-folder/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test-apps/same-folder/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/same-folder/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-same-folder",
3 | "description": "Check if test files are correctly filtered out",
4 | "scripts": {
5 | "cy:run": "cypress run",
6 | "start": "parcel serve index.html",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage main.js && check-coverage unit-utils.js && only-covered main.js unit-utils.js"
11 | },
12 | "devDependencies": {
13 | "@babel/core": "^7.22.15"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test-apps/same-folder/plugins.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | on('file:preprocessor', require('@cypress/code-coverage/use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/test-apps/same-folder/spec.js:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import { reverse } from './unit-utils'
4 |
5 | describe('coverage information', () => {
6 | beforeEach(() => {
7 | cy.log('visiting /')
8 | cy.visit('/')
9 | })
10 |
11 | it('calls add', () => {
12 | cy.window()
13 | .invoke('add', 2, 3)
14 | .should('equal', 5)
15 | })
16 |
17 | it('calls sub', () => {
18 | cy.window()
19 | .invoke('sub', 2, 3)
20 | .should('equal', -1)
21 | })
22 |
23 | it('reverses a string', () => {
24 | expect(reverse('Hello')).to.equal('olleH')
25 | })
26 | })
27 |
--------------------------------------------------------------------------------
/test-apps/same-folder/support.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/same-folder/unit-utils.js:
--------------------------------------------------------------------------------
1 | export const reverse = s =>
2 | s
3 | .split('')
4 | .reverse()
5 | .join('')
6 |
--------------------------------------------------------------------------------
/test-apps/support-files/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/support-files/README.md:
--------------------------------------------------------------------------------
1 | # example: support-files
2 |
3 | Filtering out support files
4 |
--------------------------------------------------------------------------------
/test-apps/support-files/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234',
10 | env: {
11 | codeCoverage: {
12 | exclude: ['cypress/**/**.*']
13 | }
14 | }
15 | },
16 | })
17 |
--------------------------------------------------------------------------------
/test-apps/support-files/cypress/e2e/spec.cy.js:
--------------------------------------------------------------------------------
1 | ///
2 | it('works', () => {
3 | cy.visit('/')
4 | cy.contains('Page body')
5 | })
6 |
--------------------------------------------------------------------------------
/test-apps/support-files/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | on('file:preprocessor', require('@cypress/code-coverage/use-babelrc'))
4 | return config
5 | }
6 |
--------------------------------------------------------------------------------
/test-apps/support-files/cypress/support/commands.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 | console.log('this is commands file')
3 |
--------------------------------------------------------------------------------
/test-apps/support-files/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | require('./commands')
2 |
--------------------------------------------------------------------------------
/test-apps/support-files/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
13 |
14 |
--------------------------------------------------------------------------------
/test-apps/support-files/main.js:
--------------------------------------------------------------------------------
1 | window.add = (a, b) => a + b
2 |
3 | window.sub = (a, b) => a - b
4 |
--------------------------------------------------------------------------------
/test-apps/support-files/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-support-files",
3 | "description": "Filtering out support files",
4 | "scripts": {
5 | "cy:run": "cypress run",
6 | "start": "parcel serve index.html",
7 | "pretest": "rimraf .nyc_output .cache coverage dist",
8 | "test": "start-test 1234 cy:run",
9 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
10 | "coverage:check-files": "check-coverage main.js && only-covered main.js"
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test-apps/ts-example/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/ts-example/calc.ts:
--------------------------------------------------------------------------------
1 | export const add = (a: number, b: number) => {
2 | return a + b
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/ts-example/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | baseUrl: 'http://localhost:1234',
10 | },
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/ts-example/cypress/e2e/spec.cy.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 |
--------------------------------------------------------------------------------
/test-apps/ts-example/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | return config
4 | }
5 |
--------------------------------------------------------------------------------
/test-apps/ts-example/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/ts-example/index.html:
--------------------------------------------------------------------------------
1 |
2 | Page body
3 |
4 |
5 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/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 | "typescript": "^4.5.5"
6 | },
7 | "scripts": {
8 | "cy:run": "cypress run",
9 | "start": "parcel serve index.html",
10 | "pretest": "rimraf .nyc_output .cache coverage dist",
11 | "test": "start-test 1234 cy:run",
12 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
13 | "coverage:check-files": "check-coverage main.ts && check-coverage calc.ts && only-covered main.ts calc.ts"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/README.md:
--------------------------------------------------------------------------------
1 | # example: unit-tests-js
2 |
3 | Examples that only run unit tests written using JavaScript
4 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | e2e: {
6 | setupNodeEvents(on, config) {
7 | return require('./cypress/plugins/index.js')(on, config)
8 | },
9 | env: {
10 | codeCoverage: {
11 | exclude: 'cypress/**/*.*'
12 | }
13 | }
14 | },
15 | })
16 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/cypress/e2e/spec-a.cy.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 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/cypress/e2e/spec-b.cy.js:
--------------------------------------------------------------------------------
1 | // enables intelligent code completion for Cypress commands
2 | // https://on.cypress.io/intelligent-code-completion
3 | ///
4 |
5 | import { sub } from '../../src/utils/math'
6 |
7 | describe('Unit tests', () => {
8 | it('subtracts numbers', () => {
9 | expect(sub(10, 4)).to.equal(6)
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/cypress/plugins/index.js:
--------------------------------------------------------------------------------
1 | module.exports = (on, config) => {
2 | require('@cypress/code-coverage/task')(on, config)
3 | //used to instrument files included as unit tests
4 | on('file:preprocessor', require('@cypress/code-coverage/use-babelrc'))
5 | return config
6 | }
7 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 | import '@cypress/code-coverage/support'
2 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "example-unit-tests-js",
3 | "description": "Run unit tests written using JavaScript",
4 | "scripts": {
5 | "pretest": "rimraf .nyc_output .cache coverage dist",
6 | "test": "cypress run",
7 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
8 | "coverage:check-files": "check-coverage misc.js math.js && only-covered misc.js math.js"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/test-apps/unit-tests-js/src/utils/math.js:
--------------------------------------------------------------------------------
1 | export const sub = (a, b) => a - b
2 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/use-webpack/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": ["istanbul"]
3 | }
4 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/use-webpack/cypress.config.js:
--------------------------------------------------------------------------------
1 | const { defineConfig } = require('cypress')
2 |
3 | module.exports = defineConfig({
4 | fixturesFolder: false,
5 | viewportHeight: 400,
6 | viewportWidth: 400,
7 | e2e: {
8 | setupNodeEvents(on, config) {
9 | return require('./cypress/plugins/index.js')(on, config)
10 | },
11 | baseUrl: 'http://localhost:1234',
12 | env: {
13 | codeCoverage: {
14 | exclude: ['cypress/**/*.*']
15 | }
16 | }
17 | },
18 | })
19 |
--------------------------------------------------------------------------------
/test-apps/use-webpack/cypress/e2e/spec.cy.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 |
--------------------------------------------------------------------------------
/test-apps/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('@cypress/code-coverage/task')(on, config)
17 | return config
18 | }
19 |
--------------------------------------------------------------------------------
/test-apps/use-webpack/cypress/support/e2e.js:
--------------------------------------------------------------------------------
1 |
2 | import '@cypress/code-coverage/support'
--------------------------------------------------------------------------------
/test-apps/use-webpack/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Webpack example
5 |
20 |
21 |
22 | Webpack page
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/test-apps/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:run": "cypress run",
8 | "build": "webpack && cp ./index.html ./dist/index.html",
9 | "start": "serve -p 1234 dist",
10 | "pretest": "rimraf .nyc_output .cache coverage dist",
11 | "test": "npm run build && start-test 1234 cy:run",
12 | "coverage:verify": "npx nyc report --check-coverage true --lines 100",
13 | "coverage:check-files": "check-coverage src/index.js && check-coverage src/calc.js && only-covered src/index.js src/calc.js"
14 | },
15 | "keywords": [],
16 | "author": "",
17 | "license": "ISC"
18 | }
19 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/test-apps/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 |
--------------------------------------------------------------------------------
/use-babelrc.js:
--------------------------------------------------------------------------------
1 | const webpackPreprocessor = require('@cypress/webpack-preprocessor')
2 | const defaults = webpackPreprocessor.defaultOptions
3 | // remove presets so the babelrc file will be used
4 | delete defaults.webpackOptions.module.rules[0].use[0].options.presets
5 | module.exports = webpackPreprocessor(defaults)
6 |
--------------------------------------------------------------------------------