Skip to content

Commit a5a95d5

Browse files
authored
Batch coverage data sent to combineCoverage to prevent timeouts (#877)
1 parent bf696bb commit a5a95d5

17 files changed

+814
-7
lines changed

.circleci/config.yml

+2
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ workflows:
146146
jobname:
147147
- all-files
148148
- backend
149+
- batch-send-coverage
149150
- before-all-visit
150151
- before-each-visit
151152
- cra-e2e-and-ct
@@ -174,6 +175,7 @@ workflows:
174175
- test-code-coverage-plugin
175176
- test-all-files
176177
- test-backend
178+
- test-batch-send-coverage
177179
- test-before-all-visit
178180
- test-before-each-visit
179181
- test-cra-e2e-and-ct

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -549,14 +549,21 @@ Deeply nested objects will sometimes have `[object Object]` values printed. You
549549
DEBUG_DEPTH=10 DEBUG=code-coverage npm run dev
550550
```
551551

552-
### Common issues
552+
## Common issues
553553

554554
Common issue: [not instrumenting your application when running Cypress](#instrument-your-application).
555555

556556
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.
557557

558558
If you decide to open an issue in this repository, please fill in all information the [issue template](https://github.com/cypress-io/code-coverage/blob/master/.github/ISSUE_TEMPLATE/bug_report.md) asks for. The issues most likely to be resolved have debug logs, screenshots, and hopefully public repository links so we can try running the tests ourselves.
559559

560+
### Coverage reporting timeouts
561+
562+
If the plugin times out when sending coverage report data to be merged, this may be due to a very large
563+
report being sent across processes. You can batch the report by setting the `sendCoverageBatchSize` environment
564+
variable in your `cypress.config.js` file's 'env' section. Assign the variable an integer value representing
565+
the number of report keys to send per batch.
566+
560567
## Contributing
561568

562569
You can test changes locally by running tests and confirming that the code coverage has been calculated and saved.

support-utils.js

+13-1
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,19 @@ function fixSourcePaths(coverage) {
148148
})
149149
}
150150

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+
151162
module.exports = {
152163
fixSourcePaths,
153-
filterFilesFromCoverage
164+
filterFilesFromCoverage,
165+
getSendCoverageBatchSize
154166
}

support.js

+35-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@
33

44
const dayjs = require('dayjs')
55
var duration = require('dayjs/plugin/duration')
6-
const { filterFilesFromCoverage } = require('./support-utils')
6+
const {
7+
filterFilesFromCoverage,
8+
getSendCoverageBatchSize
9+
} = require('./support-utils')
710

811
dayjs.extend(duration)
912

@@ -16,10 +19,37 @@ const sendCoverage = (coverage, pathname = '/') => {
1619

1720
const totalCoverage = filterFilesFromCoverage(coverage)
1821

19-
// stringify coverage object for speed
20-
cy.task('combineCoverage', JSON.stringify(totalCoverage), {
21-
log: false
22-
})
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+
}
2353
}
2454

2555
/**
+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"plugins": ["istanbul"]
3+
}
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
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.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
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+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/// <reference types="cypress" />
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+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module.exports = (on, config) => {
2+
require('@cypress/code-coverage/task')(on, config)
3+
return config
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import '@cypress/code-coverage/support'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('./commands')
+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<body>
2+
Page body
3+
<script src="main.js"></script>
4+
<script src="second.js"></script>
5+
<script src="third.js"></script>
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+
</script>
17+
</body>

test-apps/batch-send-coverage/main.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
window.add = (a, b) => a + b
2+
3+
window.sub = (a, b) => a - b

0 commit comments

Comments
 (0)