@jsenv/core 23.3.0 → 23.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +5 -5
- package/readme.md +4 -4
- package/src/execute.js +0 -6
- package/src/executeTestPlan.js +12 -10
- package/src/internal/browser-launcher/executeHtmlFile.js +20 -16
- package/src/internal/compiling/createCompiledFileService.js +13 -1
- package/src/internal/executing/coverage/babel_plugin_instrument.js +1 -0
- package/src/internal/executing/coverage/reportToCoverage.js +160 -119
- package/src/internal/executing/{coverage → coverage_missing}/createEmptyCoverage.js +0 -0
- package/src/internal/executing/coverage_missing/list_files_not_covered.js +20 -0
- package/src/internal/executing/coverage_missing/missing_coverage.js +46 -0
- package/src/internal/executing/{coverage → coverage_missing}/relativeUrlToEmptyCoverage.js +12 -8
- package/src/internal/executing/{coverage/generateCoverageHtmlDirectory.js → coverage_reporter/coverage_reporter_html_directory.js} +11 -4
- package/src/internal/executing/{coverage/generateCoverageJsonFile.js → coverage_reporter/coverage_reporter_json_file.js} +0 -0
- package/src/internal/executing/{coverage/generateCoverageTextLog.js → coverage_reporter/coverage_reporter_text_log.js} +4 -2
- package/src/internal/executing/{coverage → coverage_reporter}/istanbulCoverageMapFromCoverage.js +0 -0
- package/src/internal/executing/{coverage/normalizeIstanbulCoverage.js → coverage_utils/file_by_file_coverage.js} +9 -7
- package/src/internal/executing/coverage_utils/istanbul_coverage_composition.js +28 -0
- package/src/internal/executing/coverage_utils/v8_and_istanbul.js +38 -0
- package/src/internal/executing/coverage_utils/v8_coverage_composition.js +23 -0
- package/src/internal/executing/coverage_utils/v8_coverage_from_directory.js +77 -0
- package/src/internal/executing/{coverage/istanbulCoverageFromV8Coverage.js → coverage_utils/v8_coverage_to_istanbul.js} +38 -17
- package/src/internal/executing/createSummaryLog.js +5 -9
- package/src/internal/executing/executeConcurrently.js +199 -115
- package/src/internal/executing/executePlan.js +8 -5
- package/src/internal/executing/executionLogs.js +66 -37
- package/src/internal/executing/execution_colors.js +2 -1
- package/src/internal/executing/launchAndExecute.js +63 -58
- package/src/internal/logs/msAsDuration.js +4 -3
- package/src/launchBrowser.js +8 -8
- package/src/launchNode.js +5 -93
- package/src/internal/executing/coverage/composeIstanbulCoverages.js +0 -108
- package/src/internal/executing/coverage/composeV8Coverages.js +0 -20
- package/src/internal/executing/coverage/istanbulCoverageFromCoverages.js +0 -43
- package/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js +0 -40
- package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +0 -67
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jsenv/core",
|
|
3
|
-
"version": "23.
|
|
3
|
+
"version": "23.5.0",
|
|
4
4
|
"description": "Tool to develop, test and build js projects",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -63,12 +63,12 @@
|
|
|
63
63
|
"@babel/plugin-syntax-numeric-separator": "7.10.4",
|
|
64
64
|
"@babel/plugin-transform-modules-systemjs": "7.15.4",
|
|
65
65
|
"@c88/v8-coverage": "0.1.1",
|
|
66
|
-
"@jsenv/abort": "4.
|
|
67
|
-
"@jsenv/filesystem": "2.5.
|
|
66
|
+
"@jsenv/abort": "4.1.1",
|
|
67
|
+
"@jsenv/filesystem": "2.5.1",
|
|
68
68
|
"@jsenv/importmap": "1.1.0",
|
|
69
|
-
"@jsenv/log": "1.
|
|
69
|
+
"@jsenv/log": "1.4.0",
|
|
70
70
|
"@jsenv/logger": "4.0.1",
|
|
71
|
-
"@jsenv/server": "10.0.
|
|
71
|
+
"@jsenv/server": "10.0.1",
|
|
72
72
|
"@jsenv/uneval": "1.6.0",
|
|
73
73
|
"@jsenv/workers": "1.2.0",
|
|
74
74
|
"@rollup/plugin-commonjs": "21.0.0",
|
package/readme.md
CHANGED
|
@@ -99,16 +99,16 @@ executeTestPlan({
|
|
|
99
99
|
✔ execution 1 of 2 completed (all completed)
|
|
100
100
|
file: animals.test.html
|
|
101
101
|
runtime: chromium/82.0.4057.0
|
|
102
|
-
duration: 1.
|
|
102
|
+
duration: 1.2 seconds
|
|
103
103
|
|
|
104
104
|
✔ execution 2 of 2 completed (all completed)
|
|
105
105
|
file: animals.test.html
|
|
106
106
|
runtime: firefox/73.0b13
|
|
107
|
-
duration: 2.
|
|
107
|
+
duration: 2.4 seconds
|
|
108
108
|
|
|
109
109
|
-------------- summary -----------------
|
|
110
|
-
2
|
|
111
|
-
total duration: 3.
|
|
110
|
+
2 executions: all completed
|
|
111
|
+
total duration: 3.6 seconds
|
|
112
112
|
----------------------------------------
|
|
113
113
|
```
|
|
114
114
|
|
package/src/execute.js
CHANGED
|
@@ -27,11 +27,8 @@ export const execute = async ({
|
|
|
27
27
|
runtimeParams,
|
|
28
28
|
|
|
29
29
|
allocatedMs,
|
|
30
|
-
measureDuration,
|
|
31
30
|
mirrorConsole = true,
|
|
32
31
|
captureConsole,
|
|
33
|
-
collectRuntimeName,
|
|
34
|
-
collectRuntimeVersion,
|
|
35
32
|
inheritCoverage,
|
|
36
33
|
collectCoverage,
|
|
37
34
|
measurePerformance,
|
|
@@ -139,11 +136,8 @@ export const execute = async ({
|
|
|
139
136
|
},
|
|
140
137
|
|
|
141
138
|
allocatedMs,
|
|
142
|
-
measureDuration,
|
|
143
139
|
mirrorConsole,
|
|
144
140
|
captureConsole,
|
|
145
|
-
collectRuntimeName,
|
|
146
|
-
collectRuntimeVersion,
|
|
147
141
|
inheritCoverage,
|
|
148
142
|
collectCoverage,
|
|
149
143
|
measurePerformance,
|
package/src/executeTestPlan.js
CHANGED
|
@@ -14,9 +14,9 @@ import {
|
|
|
14
14
|
} from "./internal/argUtils.js"
|
|
15
15
|
import { executePlan } from "./internal/executing/executePlan.js"
|
|
16
16
|
import { executionIsPassed } from "./internal/executing/executionIsPassed.js"
|
|
17
|
-
import { generateCoverageJsonFile } from "./internal/executing/
|
|
18
|
-
import { generateCoverageHtmlDirectory } from "./internal/executing/
|
|
19
|
-
import { generateCoverageTextLog } from "./internal/executing/
|
|
17
|
+
import { generateCoverageJsonFile } from "./internal/executing/coverage_reporter/coverage_reporter_json_file.js"
|
|
18
|
+
import { generateCoverageHtmlDirectory } from "./internal/executing/coverage_reporter/coverage_reporter_html_directory.js"
|
|
19
|
+
import { generateCoverageTextLog } from "./internal/executing/coverage_reporter/coverage_reporter_text_log.js"
|
|
20
20
|
import { jsenvCoverageConfig } from "./jsenvCoverageConfig.js"
|
|
21
21
|
|
|
22
22
|
export const executeTestPlan = async ({
|
|
@@ -33,29 +33,29 @@ export const executeTestPlan = async ({
|
|
|
33
33
|
|
|
34
34
|
testPlan,
|
|
35
35
|
defaultMsAllocatedPerExecution,
|
|
36
|
+
cooldownBetweenExecutions,
|
|
36
37
|
|
|
37
38
|
maxExecutionsInParallel,
|
|
38
39
|
|
|
39
40
|
completedExecutionLogAbbreviation = false,
|
|
40
41
|
completedExecutionLogMerging = false,
|
|
41
42
|
logSummary = true,
|
|
42
|
-
measureGlobalDuration = true,
|
|
43
43
|
updateProcessExitCode = true,
|
|
44
44
|
|
|
45
45
|
coverage = process.argv.includes("--cover") ||
|
|
46
46
|
process.argv.includes("--coverage"),
|
|
47
|
+
coverageTempDirectoryRelativeUrl = "./coverage/tmp/",
|
|
47
48
|
coverageConfig = jsenvCoverageConfig,
|
|
48
49
|
coverageIncludeMissing = true,
|
|
49
50
|
coverageAndExecutionAllowed = false,
|
|
50
51
|
coverageForceIstanbul = false,
|
|
51
|
-
|
|
52
|
-
|
|
52
|
+
coverageV8ConflictWarning = true,
|
|
53
53
|
coverageTextLog = true,
|
|
54
54
|
coverageJsonFile = Boolean(process.env.CI),
|
|
55
55
|
coverageJsonFileLog = true,
|
|
56
56
|
coverageJsonFileRelativeUrl = "./coverage/coverage.json",
|
|
57
57
|
coverageHtmlDirectory = !process.env.CI,
|
|
58
|
-
coverageHtmlDirectoryRelativeUrl = "./coverage",
|
|
58
|
+
coverageHtmlDirectoryRelativeUrl = "./coverage/",
|
|
59
59
|
coverageHtmlDirectoryIndexLog = true,
|
|
60
60
|
// skip empty means empty files won't appear in the coverage reports (log and html)
|
|
61
61
|
coverageSkipEmpty = false,
|
|
@@ -144,16 +144,17 @@ export const executeTestPlan = async ({
|
|
|
144
144
|
|
|
145
145
|
defaultMsAllocatedPerExecution,
|
|
146
146
|
maxExecutionsInParallel,
|
|
147
|
+
cooldownBetweenExecutions,
|
|
147
148
|
completedExecutionLogMerging,
|
|
148
149
|
completedExecutionLogAbbreviation,
|
|
149
150
|
logSummary,
|
|
150
|
-
measureGlobalDuration,
|
|
151
151
|
|
|
152
152
|
coverage,
|
|
153
153
|
coverageConfig,
|
|
154
154
|
coverageIncludeMissing,
|
|
155
155
|
coverageForceIstanbul,
|
|
156
|
-
|
|
156
|
+
coverageV8ConflictWarning,
|
|
157
|
+
coverageTempDirectoryRelativeUrl,
|
|
157
158
|
|
|
158
159
|
jsenvDirectoryClean,
|
|
159
160
|
compileServerProtocol,
|
|
@@ -173,7 +174,7 @@ export const executeTestPlan = async ({
|
|
|
173
174
|
}
|
|
174
175
|
|
|
175
176
|
const planCoverage = result.planCoverage
|
|
176
|
-
// planCoverage can be null when execution is
|
|
177
|
+
// planCoverage can be null when execution is aborted
|
|
177
178
|
if (planCoverage) {
|
|
178
179
|
const promises = []
|
|
179
180
|
// keep this one first because it does ensureEmptyDirectory
|
|
@@ -224,6 +225,7 @@ export const executeTestPlan = async ({
|
|
|
224
225
|
}
|
|
225
226
|
|
|
226
227
|
return {
|
|
228
|
+
testPlanAborted: result.aborted,
|
|
227
229
|
testPlanSummary: result.planSummary,
|
|
228
230
|
testPlanReport: result.planReport,
|
|
229
231
|
testPlanCoverage: planCoverage,
|
|
@@ -6,8 +6,8 @@ import {
|
|
|
6
6
|
} from "@jsenv/filesystem"
|
|
7
7
|
|
|
8
8
|
import { jsenvCompileProxyHtmlFileInfo } from "@jsenv/core/src/internal/jsenvInternalFiles.js"
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
9
|
+
import { filterV8Coverage } from "@jsenv/core/src/internal/executing/coverage_utils/v8_coverage_from_directory.js"
|
|
10
|
+
import { composeTwoFileByFileIstanbulCoverages } from "@jsenv/core/src/internal/executing/coverage_utils/istanbul_coverage_composition.js"
|
|
11
11
|
import { evalSource } from "../runtime/createNodeRuntime/evalSource.js"
|
|
12
12
|
import { escapeRegexpSpecialCharacters } from "../escapeRegexpSpecialCharacters.js"
|
|
13
13
|
|
|
@@ -23,7 +23,7 @@ export const executeHtmlFile = async (
|
|
|
23
23
|
// measurePerformance,
|
|
24
24
|
collectPerformance,
|
|
25
25
|
collectCoverage,
|
|
26
|
-
|
|
26
|
+
coverageIgnorePredicate,
|
|
27
27
|
coverageForceIstanbul,
|
|
28
28
|
coveragePlaywrightAPIAvailable,
|
|
29
29
|
transformErrorHook,
|
|
@@ -76,7 +76,7 @@ export const executeHtmlFile = async (
|
|
|
76
76
|
fileRelativeUrl,
|
|
77
77
|
page,
|
|
78
78
|
collectCoverage,
|
|
79
|
-
|
|
79
|
+
coverageIgnorePredicate,
|
|
80
80
|
transformErrorHook,
|
|
81
81
|
})
|
|
82
82
|
} else {
|
|
@@ -149,7 +149,7 @@ const executeSource = async ({
|
|
|
149
149
|
fileRelativeUrl,
|
|
150
150
|
page,
|
|
151
151
|
collectCoverage,
|
|
152
|
-
|
|
152
|
+
coverageIgnorePredicate,
|
|
153
153
|
transformErrorHook,
|
|
154
154
|
}) => {
|
|
155
155
|
let transformResult = (result) => result
|
|
@@ -175,11 +175,12 @@ const executeSource = async ({
|
|
|
175
175
|
}
|
|
176
176
|
},
|
|
177
177
|
)
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
const coverage = filterV8Coverage(
|
|
179
|
+
{ result: v8CoveragesWithFsUrls },
|
|
180
|
+
{
|
|
181
|
+
coverageIgnorePredicate,
|
|
182
|
+
},
|
|
183
|
+
)
|
|
183
184
|
return {
|
|
184
185
|
...result,
|
|
185
186
|
coverage,
|
|
@@ -297,15 +298,18 @@ const executeCompiledVersion = async ({
|
|
|
297
298
|
}
|
|
298
299
|
|
|
299
300
|
const generateCoverageForPage = (fileExecutionResultMap) => {
|
|
300
|
-
|
|
301
|
+
let istanbulCoverageComposed = null
|
|
301
302
|
Object.keys(fileExecutionResultMap).forEach((fileRelativeUrl) => {
|
|
302
303
|
const istanbulCoverage = fileExecutionResultMap[fileRelativeUrl].coverage
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
304
|
+
istanbulCoverageComposed = istanbulCoverageComposed
|
|
305
|
+
? composeTwoFileByFileIstanbulCoverages(
|
|
306
|
+
istanbulCoverageComposed,
|
|
307
|
+
istanbulCoverage,
|
|
308
|
+
)
|
|
309
|
+
: istanbulCoverage
|
|
306
310
|
})
|
|
307
|
-
|
|
308
|
-
return
|
|
311
|
+
|
|
312
|
+
return istanbulCoverageComposed
|
|
309
313
|
}
|
|
310
314
|
|
|
311
315
|
const evalException = (
|
|
@@ -71,7 +71,10 @@ export const createCompiledFileService = ({
|
|
|
71
71
|
|
|
72
72
|
return (request) => {
|
|
73
73
|
const { origin, ressource } = request
|
|
74
|
-
|
|
74
|
+
// we use "ressourceToPathname" to remove eventual query param from the url
|
|
75
|
+
// Without this a pattern like "**/*.js" would not match "file.js?t=1"
|
|
76
|
+
// This would result in file not being compiled when they should
|
|
77
|
+
const requestUrl = `${origin}${ressourceToPathname(ressource)}`
|
|
75
78
|
|
|
76
79
|
const requestCompileInfo = serverUrlToCompileInfo(requestUrl, {
|
|
77
80
|
outDirectoryRelativeUrl,
|
|
@@ -276,3 +279,12 @@ const babelPluginMapFromCompileId = (
|
|
|
276
279
|
|
|
277
280
|
return babelPluginMapForGroup
|
|
278
281
|
}
|
|
282
|
+
|
|
283
|
+
const ressourceToPathname = (ressource) => {
|
|
284
|
+
const searchSeparatorIndex = ressource.indexOf("?")
|
|
285
|
+
const pathname =
|
|
286
|
+
searchSeparatorIndex === -1
|
|
287
|
+
? ressource
|
|
288
|
+
: ressource.slice(0, searchSeparatorIndex)
|
|
289
|
+
return pathname
|
|
290
|
+
}
|
|
@@ -1,146 +1,187 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import { readFile } from "@jsenv/filesystem"
|
|
2
|
+
import { Abort } from "@jsenv/abort"
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
visitNodeV8Directory,
|
|
6
|
+
filterV8Coverage,
|
|
7
|
+
} from "../coverage_utils/v8_coverage_from_directory.js"
|
|
8
|
+
import { composeTwoV8Coverages } from "../coverage_utils/v8_coverage_composition.js"
|
|
9
|
+
import { composeTwoFileByFileIstanbulCoverages } from "../coverage_utils/istanbul_coverage_composition.js"
|
|
10
|
+
import { v8CoverageToIstanbul } from "../coverage_utils/v8_coverage_to_istanbul.js"
|
|
11
|
+
import { composeV8AndIstanbul } from "../coverage_utils/v8_and_istanbul.js"
|
|
12
|
+
import { normalizeFileByFileCoveragePaths } from "../coverage_utils/file_by_file_coverage.js"
|
|
13
|
+
import { getMissingFileByFileCoverage } from "../coverage_missing/missing_coverage.js"
|
|
6
14
|
|
|
7
15
|
export const reportToCoverage = async (
|
|
8
16
|
report,
|
|
9
17
|
{
|
|
10
|
-
|
|
18
|
+
signal,
|
|
11
19
|
logger,
|
|
12
20
|
projectDirectoryUrl,
|
|
13
21
|
babelPluginMap,
|
|
14
22
|
coverageConfig,
|
|
15
23
|
coverageIncludeMissing,
|
|
16
|
-
|
|
24
|
+
coverageIgnorePredicate,
|
|
25
|
+
coverageForceIstanbul,
|
|
26
|
+
coverageV8ConflictWarning,
|
|
17
27
|
},
|
|
18
28
|
) => {
|
|
19
|
-
//
|
|
20
|
-
|
|
21
|
-
|
|
29
|
+
// collect v8 and istanbul coverage from executions
|
|
30
|
+
let { v8Coverage, fileByFileIstanbulCoverage } = await getCoverageFromReport({
|
|
31
|
+
signal,
|
|
22
32
|
report,
|
|
23
|
-
{
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
33
|
+
onMissing: ({ file, executionResult, executionName }) => {
|
|
34
|
+
// several reasons not to have coverage here:
|
|
35
|
+
// 1. the file we executed did not import an instrumented file.
|
|
36
|
+
// - a test file without import
|
|
37
|
+
// - a test file importing only file excluded from coverage
|
|
38
|
+
// - a coverDescription badly configured so that we don't realize
|
|
39
|
+
// a file should be covered
|
|
40
|
+
|
|
41
|
+
// 2. the file we wanted to executed timedout
|
|
42
|
+
// - infinite loop
|
|
43
|
+
// - too extensive operation
|
|
44
|
+
// - a badly configured or too low allocatedMs for that execution.
|
|
45
|
+
|
|
46
|
+
// 3. the file we wanted to execute contains syntax-error
|
|
47
|
+
|
|
48
|
+
// in any scenario we are fine because
|
|
49
|
+
// coverDescription will generate empty coverage for files
|
|
50
|
+
// that were suppose to be coverage but were not.
|
|
51
|
+
if (executionResult.status === "completed") {
|
|
52
|
+
logger.debug(
|
|
53
|
+
`No execution.coverageFileUrl from execution named "${executionName}" of ${file}`,
|
|
54
|
+
)
|
|
55
|
+
}
|
|
27
56
|
},
|
|
28
|
-
)
|
|
57
|
+
})
|
|
29
58
|
|
|
30
|
-
if (!
|
|
31
|
-
|
|
59
|
+
if (!coverageForceIstanbul && process.env.NODE_V8_COVERAGE) {
|
|
60
|
+
await visitNodeV8Directory({
|
|
61
|
+
signal,
|
|
62
|
+
NODE_V8_COVERAGE: process.env.NODE_V8_COVERAGE,
|
|
63
|
+
onV8Coverage: (nodeV8Coverage) => {
|
|
64
|
+
const nodeV8CoverageLight = filterV8Coverage(nodeV8Coverage, {
|
|
65
|
+
coverageIgnorePredicate,
|
|
66
|
+
})
|
|
67
|
+
v8Coverage = v8Coverage
|
|
68
|
+
? composeTwoV8Coverages(v8Coverage, nodeV8CoverageLight)
|
|
69
|
+
: nodeV8CoverageLight
|
|
70
|
+
},
|
|
71
|
+
})
|
|
32
72
|
}
|
|
33
73
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
74
|
+
// try to merge v8 with istanbul, if any
|
|
75
|
+
let fileByFileCoverage
|
|
76
|
+
if (v8Coverage) {
|
|
77
|
+
let v8FileByFileCoverage = await v8CoverageToIstanbul(v8Coverage, {
|
|
78
|
+
signal,
|
|
79
|
+
})
|
|
39
80
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
return key !== `./${relativeFileUrlToCover}`
|
|
44
|
-
}),
|
|
81
|
+
v8FileByFileCoverage = normalizeFileByFileCoveragePaths(
|
|
82
|
+
v8FileByFileCoverage,
|
|
83
|
+
projectDirectoryUrl,
|
|
45
84
|
)
|
|
46
85
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
),
|
|
66
|
-
)
|
|
67
|
-
|
|
68
|
-
return {
|
|
69
|
-
...istanbulCoverageFromExecution, // already normalized
|
|
70
|
-
...normalizeIstanbulCoverage(
|
|
71
|
-
istanbulCoverageFromMissedFiles,
|
|
86
|
+
if (fileByFileIstanbulCoverage) {
|
|
87
|
+
fileByFileIstanbulCoverage = normalizeFileByFileCoveragePaths(
|
|
88
|
+
fileByFileIstanbulCoverage,
|
|
89
|
+
projectDirectoryUrl,
|
|
90
|
+
)
|
|
91
|
+
fileByFileCoverage = composeV8AndIstanbul(
|
|
92
|
+
v8FileByFileCoverage,
|
|
93
|
+
fileByFileIstanbulCoverage,
|
|
94
|
+
{ coverageV8ConflictWarning },
|
|
95
|
+
)
|
|
96
|
+
} else {
|
|
97
|
+
fileByFileCoverage = v8FileByFileCoverage
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// get istanbul only
|
|
101
|
+
else if (fileByFileIstanbulCoverage) {
|
|
102
|
+
fileByFileCoverage = normalizeFileByFileCoveragePaths(
|
|
103
|
+
fileByFileIstanbulCoverage,
|
|
72
104
|
projectDirectoryUrl,
|
|
73
|
-
)
|
|
105
|
+
)
|
|
74
106
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
multipleExecutionsOperation,
|
|
79
|
-
projectDirectoryUrl,
|
|
80
|
-
coverageConfig,
|
|
81
|
-
}) => {
|
|
82
|
-
const structuredMetaMapForCoverage = {
|
|
83
|
-
cover: coverageConfig,
|
|
107
|
+
// no coverage found in execution (or zero file where executed)
|
|
108
|
+
else {
|
|
109
|
+
fileByFileCoverage = {}
|
|
84
110
|
}
|
|
85
111
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const executionReportToCoverage = async (
|
|
97
|
-
report,
|
|
98
|
-
{ logger, projectDirectoryUrl, coverageV8MergeConflictIsExpected },
|
|
99
|
-
) => {
|
|
100
|
-
const coverages = []
|
|
101
|
-
|
|
102
|
-
Object.keys(report).forEach((file) => {
|
|
103
|
-
const executionResultForFile = report[file]
|
|
104
|
-
Object.keys(executionResultForFile).forEach((executionName) => {
|
|
105
|
-
const executionResultForFileOnRuntime =
|
|
106
|
-
executionResultForFile[executionName]
|
|
107
|
-
|
|
108
|
-
const { status, coverage } = executionResultForFileOnRuntime
|
|
109
|
-
if (!coverage) {
|
|
110
|
-
// several reasons not to have coverage here:
|
|
111
|
-
// 1. the file we executed did not import an instrumented file.
|
|
112
|
-
// - a test file without import
|
|
113
|
-
// - a test file importing only file excluded from coverage
|
|
114
|
-
// - a coverDescription badly configured so that we don't realize
|
|
115
|
-
// a file should be covered
|
|
116
|
-
|
|
117
|
-
// 2. the file we wanted to executed timedout
|
|
118
|
-
// - infinite loop
|
|
119
|
-
// - too extensive operation
|
|
120
|
-
// - a badly configured or too low allocatedMs for that execution.
|
|
121
|
-
|
|
122
|
-
// 3. the file we wanted to execute contains syntax-error
|
|
123
|
-
|
|
124
|
-
// in any scenario we are fine because
|
|
125
|
-
// coverDescription will generate empty coverage for files
|
|
126
|
-
// that were suppose to be coverage but were not.
|
|
127
|
-
|
|
128
|
-
if (status === "completed") {
|
|
129
|
-
logger.warn(
|
|
130
|
-
`No execution.coverage from execution named "${executionName}" of ${file}`,
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
return
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
coverages.push(coverage)
|
|
112
|
+
// now add coverage for file not covered
|
|
113
|
+
if (coverageIncludeMissing) {
|
|
114
|
+
const missingFileByFileCoverage = await getMissingFileByFileCoverage({
|
|
115
|
+
signal,
|
|
116
|
+
projectDirectoryUrl,
|
|
117
|
+
coverageConfig,
|
|
118
|
+
fileByFileCoverage,
|
|
119
|
+
babelPluginMap,
|
|
137
120
|
})
|
|
138
|
-
|
|
121
|
+
Object.assign(fileByFileCoverage, missingFileByFileCoverage)
|
|
122
|
+
}
|
|
139
123
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
coverageV8MergeConflictIsExpected,
|
|
143
|
-
})
|
|
124
|
+
return fileByFileCoverage
|
|
125
|
+
}
|
|
144
126
|
|
|
145
|
-
|
|
127
|
+
const getCoverageFromReport = async ({ signal, report, onMissing }) => {
|
|
128
|
+
const operation = Abort.startOperation()
|
|
129
|
+
operation.addAbortSignal(signal)
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
let v8Coverage
|
|
133
|
+
let fileByFileIstanbulCoverage
|
|
134
|
+
|
|
135
|
+
// collect v8 and istanbul coverage from executions
|
|
136
|
+
await Object.keys(report).reduce(async (previous, file) => {
|
|
137
|
+
operation.throwIfAborted()
|
|
138
|
+
await previous
|
|
139
|
+
|
|
140
|
+
const executionResultForFile = report[file]
|
|
141
|
+
await Object.keys(executionResultForFile).reduce(
|
|
142
|
+
async (previous, executionName) => {
|
|
143
|
+
operation.throwIfAborted()
|
|
144
|
+
await previous
|
|
145
|
+
|
|
146
|
+
const executionResultForFileOnRuntime =
|
|
147
|
+
executionResultForFile[executionName]
|
|
148
|
+
const { coverageFileUrl } = executionResultForFileOnRuntime
|
|
149
|
+
if (!coverageFileUrl) {
|
|
150
|
+
onMissing({
|
|
151
|
+
executionName,
|
|
152
|
+
file,
|
|
153
|
+
executionResult: executionResultForFileOnRuntime,
|
|
154
|
+
})
|
|
155
|
+
return
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const executionCoverage = await readFile(coverageFileUrl, {
|
|
159
|
+
as: "json",
|
|
160
|
+
})
|
|
161
|
+
if (isV8Coverage(executionCoverage)) {
|
|
162
|
+
v8Coverage = v8Coverage
|
|
163
|
+
? composeTwoV8Coverages(v8Coverage, executionCoverage)
|
|
164
|
+
: executionCoverage
|
|
165
|
+
} else {
|
|
166
|
+
fileByFileIstanbulCoverage = fileByFileIstanbulCoverage
|
|
167
|
+
? composeTwoFileByFileIstanbulCoverages(
|
|
168
|
+
fileByFileIstanbulCoverage,
|
|
169
|
+
executionCoverage,
|
|
170
|
+
)
|
|
171
|
+
: executionCoverage
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
Promise.resolve(),
|
|
175
|
+
)
|
|
176
|
+
}, Promise.resolve())
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
v8Coverage,
|
|
180
|
+
fileByFileIstanbulCoverage,
|
|
181
|
+
}
|
|
182
|
+
} finally {
|
|
183
|
+
await operation.end()
|
|
184
|
+
}
|
|
146
185
|
}
|
|
186
|
+
|
|
187
|
+
const isV8Coverage = (coverage) => Boolean(coverage.result)
|
|
File without changes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { collectFiles } from "@jsenv/filesystem"
|
|
2
|
+
|
|
3
|
+
export const listRelativeFileUrlToCover = async ({
|
|
4
|
+
signal,
|
|
5
|
+
projectDirectoryUrl,
|
|
6
|
+
coverageConfig,
|
|
7
|
+
}) => {
|
|
8
|
+
const structuredMetaMapForCoverage = {
|
|
9
|
+
cover: coverageConfig,
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const matchingFileResultArray = await collectFiles({
|
|
13
|
+
signal,
|
|
14
|
+
directoryUrl: projectDirectoryUrl,
|
|
15
|
+
structuredMetaMap: structuredMetaMapForCoverage,
|
|
16
|
+
predicate: ({ cover }) => cover,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
return matchingFileResultArray.map(({ relativeUrl }) => relativeUrl)
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { Abort } from "@jsenv/abort"
|
|
2
|
+
|
|
3
|
+
import { listRelativeFileUrlToCover } from "./list_files_not_covered.js"
|
|
4
|
+
import { relativeUrlToEmptyCoverage } from "./relativeUrlToEmptyCoverage.js"
|
|
5
|
+
|
|
6
|
+
export const getMissingFileByFileCoverage = async ({
|
|
7
|
+
signal,
|
|
8
|
+
projectDirectoryUrl,
|
|
9
|
+
coverageConfig,
|
|
10
|
+
fileByFileCoverage,
|
|
11
|
+
babelPluginMap,
|
|
12
|
+
}) => {
|
|
13
|
+
const relativeUrlsToCover = await listRelativeFileUrlToCover({
|
|
14
|
+
signal,
|
|
15
|
+
projectDirectoryUrl,
|
|
16
|
+
coverageConfig,
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
const relativeUrlsMissing = relativeUrlsToCover.filter((relativeUrlToCover) =>
|
|
20
|
+
Object.keys(fileByFileCoverage).every((key) => {
|
|
21
|
+
return key !== `./${relativeUrlToCover}`
|
|
22
|
+
}),
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
const operation = Abort.startOperation()
|
|
26
|
+
operation.addAbortSignal(signal)
|
|
27
|
+
|
|
28
|
+
const missingFileByFileCoverage = {}
|
|
29
|
+
await relativeUrlsMissing.reduce(async (previous, relativeUrlMissing) => {
|
|
30
|
+
operation.throwIfAborted()
|
|
31
|
+
await previous
|
|
32
|
+
await operation.withSignal(async (signal) => {
|
|
33
|
+
const emptyCoverage = await relativeUrlToEmptyCoverage(
|
|
34
|
+
relativeUrlMissing,
|
|
35
|
+
{
|
|
36
|
+
signal,
|
|
37
|
+
projectDirectoryUrl,
|
|
38
|
+
babelPluginMap,
|
|
39
|
+
},
|
|
40
|
+
)
|
|
41
|
+
missingFileByFileCoverage[`./${relativeUrlMissing}`] = emptyCoverage
|
|
42
|
+
})
|
|
43
|
+
}, Promise.resolve())
|
|
44
|
+
|
|
45
|
+
return missingFileByFileCoverage
|
|
46
|
+
}
|