forked from cypress-io/code-coverage
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathtask.js
190 lines (168 loc) · 5.95 KB
/
task.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// @ts-check
const istanbul = require('istanbul-lib-coverage')
const { join, resolve } = require('path')
const { existsSync, mkdirSync, readFileSync, writeFileSync } = require('fs')
const execa = require('execa')
const {
showNycInfo,
resolveRelativePaths,
checkAllPathsNotFound,
tryFindingLocalFiles
} = require('./task-utils')
const { fixSourcePaths } = require('./support-utils')
const NYC = require('nyc')
const debug = require('debug')('code-coverage')
// these are standard folder and file names used by NYC tools
const processWorkingDirectory = process.cwd()
const outputFolder = '.nyc_output'
const coverageFolder = join(processWorkingDirectory, outputFolder)
const nycFilename = join(coverageFolder, 'out.json')
// there might be custom "nyc" options in the user package.json
// see https://github.com/istanbuljs/nyc#configuring-nyc
// potentially there might be "nyc" options in other configuration files
// it allows, but for now ignore those options
const pkgFilename = join(processWorkingDirectory, 'package.json')
const pkg = existsSync(pkgFilename)
? JSON.parse(readFileSync(pkgFilename, 'utf8'))
: {}
const nycOptions = pkg.nyc || {}
const scripts = pkg.scripts || {}
const DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME = 'coverage:report'
const customNycReportScript = scripts[DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME]
function saveCoverage(coverage) {
if (!existsSync(coverageFolder)) {
mkdirSync(coverageFolder)
debug('created folder %s for output coverage', coverageFolder)
}
writeFileSync(nycFilename, JSON.stringify(coverage, null, 2))
}
const tasks = {
/**
* Clears accumulated code coverage information.
*
* Interactive mode with "cypress open"
* - running a single spec or "Run all specs" needs to reset coverage
* Headless mode with "cypress run"
* - runs EACH spec separately, so we cannot reset the coverage
* or we will lose the coverage from previous specs.
*/
resetCoverage({ isInteractive }) {
if (isInteractive) {
debug('reset code coverage in interactive mode')
const coverageMap = istanbul.createCoverageMap({})
saveCoverage(coverageMap)
}
/*
Else:
in headless mode, assume the coverage file was deleted
before the `cypress run` command was called
example: rm -rf .nyc_output || true
*/
return null
},
/**
* Combines coverage information from single test
* with previously collected coverage.
*
* @param {string} sentCoverage Stringified coverage object sent by the test runner
* @returns {null} Nothing is returned from this task
*/
combineCoverage(sentCoverage) {
const coverage = JSON.parse(sentCoverage)
debug('parsed sent coverage')
fixSourcePaths(coverage)
const previous = existsSync(nycFilename)
? JSON.parse(readFileSync(nycFilename, 'utf8'))
: istanbul.createCoverageMap({})
const coverageMap = istanbul.createCoverageMap(previous)
coverageMap.merge(coverage)
saveCoverage(coverageMap)
debug('wrote coverage file %s', nycFilename)
return null
},
/**
* Saves coverage information as a JSON file and calls
* NPM script to generate HTML report
*/
coverageReport() {
if (!existsSync(nycFilename)) {
console.warn('Cannot find coverage file %s', nycFilename)
console.warn('Skipping coverage report')
return null
}
showNycInfo(nycFilename)
const allSourceFilesMissing = checkAllPathsNotFound(nycFilename)
if (allSourceFilesMissing) {
tryFindingLocalFiles(nycFilename)
}
resolveRelativePaths(nycFilename)
if (customNycReportScript) {
debug(
'saving coverage report using script "%s" from package.json, command: %s',
DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME,
customNycReportScript
)
debug('current working directory is %s', process.cwd())
return execa('npm', ['run', DEFAULT_CUSTOM_COVERAGE_SCRIPT_NAME], {
stdio: 'inherit'
})
}
const reportFolder = nycOptions['report-dir'] || './coverage'
const reportDir = resolve(reportFolder)
const reporter = nycOptions['reporter'] || ['lcov', 'clover', 'json']
// TODO we could look at how NYC is parsing its CLI arguments
// I am mostly worried about additional NYC options that are stored in
// package.json and .nycrc resource files.
// for now let's just camel case all options
// https://github.com/istanbuljs/nyc#common-configuration-options
const nycReportOptions = {
reportDir,
tempDir: coverageFolder,
reporter: [].concat(reporter), // make sure this is a list
include: nycOptions.include,
exclude: nycOptions.exclude,
// from working with TypeScript code seems we need these settings too
excludeAfterRemap: true,
extension: nycOptions.extension || [
'.js',
'.cjs',
'.mjs',
'.ts',
'.tsx',
'.jsx'
],
all: nycOptions.all
}
debug('calling NYC reporter with options %o', nycReportOptions)
debug('current working directory is %s', process.cwd())
const nyc = new NYC(nycReportOptions)
const returnReportFolder = () => {
debug('after reporting, returning the report folder name %s', reportDir)
return reportDir
}
return nyc.report().then(returnReportFolder)
}
}
/**
* Registers code coverage collection and reporting tasks.
* Sets an environment variable to tell the browser code that it can
* send the coverage.
* @example
```
// your plugins file
module.exports = (on, config) => {
require('cypress/code-coverage/task')(on, config)
// IMPORTANT to return the config object
// with the any changed environment variables
return config
}
```
*/
function registerCodeCoverageTasks(on, config) {
on('task', tasks)
// set a variable to let the hooks running in the browser
// know that they can send coverage commands
config.env.codeCoverageTasksRegistered = true
return config
}
module.exports = registerCodeCoverageTasks