@jsenv/core 23.3.0 → 23.4.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 +1 -1
- package/readme.md +1 -1
- package/src/executeTestPlan.js +9 -8
- package/src/internal/browser-launcher/executeHtmlFile.js +20 -16
- package/src/internal/executing/coverage/babel_plugin_instrument.js +1 -0
- package/src/internal/executing/coverage/reportToCoverage.js +147 -120
- package/src/internal/executing/{coverage → coverage_empty}/createEmptyCoverage.js +0 -0
- package/src/internal/executing/coverage_empty/list_files_not_covered.js +20 -0
- package/src/internal/executing/{coverage → coverage_empty}/relativeUrlToEmptyCoverage.js +1 -1
- 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 +59 -0
- package/src/internal/executing/coverage_utils/v8_coverage_to_istanbul.js +90 -0
- package/src/internal/executing/createSummaryLog.js +3 -1
- package/src/internal/executing/executeConcurrently.js +85 -27
- package/src/internal/executing/executePlan.js +5 -2
- package/src/internal/executing/launchAndExecute.js +47 -24
- 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/istanbulCoverageFromV8Coverage.js +0 -79
- package/src/internal/executing/coverage/v8CoverageFromAllV8Coverages.js +0 -40
- package/src/internal/executing/coverage/v8CoverageFromNodeV8Directory.js +0 -67
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
assertAndNormalizeDirectoryUrl,
|
|
3
|
+
readDirectory,
|
|
4
|
+
readFile,
|
|
5
|
+
resolveUrl,
|
|
6
|
+
} from "@jsenv/filesystem"
|
|
7
|
+
import { createDetailedMessage } from "@jsenv/logger"
|
|
8
|
+
|
|
9
|
+
export const visitNodeV8Directory = async ({
|
|
10
|
+
// signal
|
|
11
|
+
NODE_V8_COVERAGE,
|
|
12
|
+
onV8Coverage,
|
|
13
|
+
}) => {
|
|
14
|
+
const tryReadDirectory = async () => {
|
|
15
|
+
const dirContent = await readDirectory(NODE_V8_COVERAGE)
|
|
16
|
+
if (dirContent.length > 0) {
|
|
17
|
+
return dirContent
|
|
18
|
+
}
|
|
19
|
+
console.warn(`v8 coverage directory is empty at ${NODE_V8_COVERAGE}`)
|
|
20
|
+
return dirContent
|
|
21
|
+
}
|
|
22
|
+
const dirContent = await tryReadDirectory()
|
|
23
|
+
|
|
24
|
+
const coverageDirectoryUrl = assertAndNormalizeDirectoryUrl(NODE_V8_COVERAGE)
|
|
25
|
+
await dirContent.reduce(async (previous, dirEntry) => {
|
|
26
|
+
await previous
|
|
27
|
+
|
|
28
|
+
const dirEntryUrl = resolveUrl(dirEntry, coverageDirectoryUrl)
|
|
29
|
+
const tryReadJsonFile = async () => {
|
|
30
|
+
try {
|
|
31
|
+
const fileContent = await readFile(dirEntryUrl, { as: "json" })
|
|
32
|
+
return fileContent
|
|
33
|
+
} catch (e) {
|
|
34
|
+
console.warn(
|
|
35
|
+
createDetailedMessage(`Error while reading coverage file`, {
|
|
36
|
+
"error stack": e.stack,
|
|
37
|
+
"file": dirEntryUrl,
|
|
38
|
+
}),
|
|
39
|
+
)
|
|
40
|
+
return null
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const fileContent = await tryReadJsonFile()
|
|
45
|
+
if (fileContent) {
|
|
46
|
+
onV8Coverage(fileContent)
|
|
47
|
+
}
|
|
48
|
+
}, Promise.resolve())
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export const filterV8Coverage = (v8Coverage, { coverageIgnorePredicate }) => {
|
|
52
|
+
const v8CoverageFiltered = {
|
|
53
|
+
...v8Coverage,
|
|
54
|
+
result: v8Coverage.result.filter((fileReport) => {
|
|
55
|
+
return !coverageIgnorePredicate(fileReport.url)
|
|
56
|
+
}),
|
|
57
|
+
}
|
|
58
|
+
return v8CoverageFiltered
|
|
59
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { urlToFileSystemPath } from "@jsenv/filesystem"
|
|
2
|
+
|
|
3
|
+
import { require } from "@jsenv/core/src/internal/require.js"
|
|
4
|
+
|
|
5
|
+
import { composeTwoFileByFileIstanbulCoverages } from "./istanbul_coverage_composition.js"
|
|
6
|
+
|
|
7
|
+
export const v8CoverageToIstanbul = async (v8Coverage) => {
|
|
8
|
+
const v8ToIstanbul = require("v8-to-istanbul")
|
|
9
|
+
const sourcemapCache = v8Coverage["source-map-cache"]
|
|
10
|
+
let istanbulCoverageComposed = null
|
|
11
|
+
await v8Coverage.result.reduce(async (previous, fileV8Coverage) => {
|
|
12
|
+
await previous
|
|
13
|
+
|
|
14
|
+
const { source } = fileV8Coverage
|
|
15
|
+
let sources
|
|
16
|
+
// when v8 coverage comes from playwright (chromium) v8Coverage.source is set
|
|
17
|
+
if (typeof source === "string") {
|
|
18
|
+
sources = { source }
|
|
19
|
+
}
|
|
20
|
+
// when v8 coverage comes from Node.js, the source can be read from sourcemapCache
|
|
21
|
+
else if (sourcemapCache) {
|
|
22
|
+
sources = sourcesFromSourceMapCache(fileV8Coverage.url, sourcemapCache)
|
|
23
|
+
}
|
|
24
|
+
const path = urlToFileSystemPath(fileV8Coverage.url)
|
|
25
|
+
|
|
26
|
+
const converter = v8ToIstanbul(
|
|
27
|
+
path,
|
|
28
|
+
// wrapperLength is undefined we don't need it
|
|
29
|
+
// https://github.com/istanbuljs/v8-to-istanbul/blob/2b54bc97c5edf8a37b39a171ec29134ba9bfd532/lib/v8-to-istanbul.js#L27
|
|
30
|
+
undefined,
|
|
31
|
+
sources,
|
|
32
|
+
)
|
|
33
|
+
await converter.load()
|
|
34
|
+
|
|
35
|
+
converter.applyCoverage(fileV8Coverage.functions)
|
|
36
|
+
const istanbulCoverage = converter.toIstanbul()
|
|
37
|
+
|
|
38
|
+
istanbulCoverageComposed = istanbulCoverageComposed
|
|
39
|
+
? composeTwoFileByFileIstanbulCoverages(
|
|
40
|
+
istanbulCoverageComposed,
|
|
41
|
+
istanbulCoverage,
|
|
42
|
+
)
|
|
43
|
+
: istanbulCoverage
|
|
44
|
+
}, Promise.resolve())
|
|
45
|
+
|
|
46
|
+
if (!istanbulCoverageComposed) {
|
|
47
|
+
return {}
|
|
48
|
+
}
|
|
49
|
+
istanbulCoverageComposed = markAsConvertedFromV8(istanbulCoverageComposed)
|
|
50
|
+
return istanbulCoverageComposed
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const markAsConvertedFromV8 = (fileByFileCoverage) => {
|
|
54
|
+
const fileByFileMarked = {}
|
|
55
|
+
Object.keys(fileByFileCoverage).forEach((key) => {
|
|
56
|
+
const fileCoverage = fileByFileCoverage[key]
|
|
57
|
+
fileByFileMarked[key] = {
|
|
58
|
+
...fileCoverage,
|
|
59
|
+
fromV8: true,
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
return fileByFileMarked
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const sourcesFromSourceMapCache = (url, sourceMapCache) => {
|
|
66
|
+
const sourceMapAndLineLengths = sourceMapCache[url]
|
|
67
|
+
if (!sourceMapAndLineLengths) {
|
|
68
|
+
return {}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const { data, lineLengths } = sourceMapAndLineLengths
|
|
72
|
+
// See: https://github.com/nodejs/node/pull/34305
|
|
73
|
+
if (!data) {
|
|
74
|
+
return undefined
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const sources = {
|
|
78
|
+
sourcemap: data,
|
|
79
|
+
...(lineLengths ? { source: sourcesFromLineLengths(lineLengths) } : {}),
|
|
80
|
+
}
|
|
81
|
+
return sources
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const sourcesFromLineLengths = (lineLengths) => {
|
|
85
|
+
let source = ""
|
|
86
|
+
lineLengths.forEach((length) => {
|
|
87
|
+
source += `${"".padEnd(length, ".")}\n`
|
|
88
|
+
})
|
|
89
|
+
return source
|
|
90
|
+
}
|
|
@@ -21,7 +21,9 @@ const createSummaryMessage = ({
|
|
|
21
21
|
return `no execution`
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
const executionLabel =
|
|
25
|
+
executionCount === 1 ? `1 execution` : `${executionCount} executions`
|
|
26
|
+
return `${executionLabel}: ${createSummaryDetails({
|
|
25
27
|
executionCount,
|
|
26
28
|
abortedCount,
|
|
27
29
|
timedoutCount,
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
import { stat } from "node:fs"
|
|
2
2
|
import wrapAnsi from "wrap-ansi"
|
|
3
|
+
import cuid from "cuid"
|
|
3
4
|
import { loggerToLevels, createDetailedMessage } from "@jsenv/logger"
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
urlToFileSystemPath,
|
|
7
|
+
resolveUrl,
|
|
8
|
+
writeDirectory,
|
|
9
|
+
ensureEmptyDirectory,
|
|
10
|
+
normalizeStructuredMetaMap,
|
|
11
|
+
urlToMeta,
|
|
12
|
+
} from "@jsenv/filesystem"
|
|
5
13
|
import { createLog } from "@jsenv/log"
|
|
14
|
+
import { Abort } from "@jsenv/abort"
|
|
6
15
|
|
|
7
16
|
import { launchAndExecute } from "../executing/launchAndExecute.js"
|
|
8
17
|
import { reportToCoverage } from "./coverage/reportToCoverage.js"
|
|
@@ -33,7 +42,9 @@ export const executeConcurrently = async (
|
|
|
33
42
|
coverageConfig,
|
|
34
43
|
coverageIncludeMissing,
|
|
35
44
|
coverageForceIstanbul,
|
|
36
|
-
|
|
45
|
+
coverageV8ConflictWarning,
|
|
46
|
+
coverageTempDirectoryRelativeUrl,
|
|
47
|
+
runtimeSupport,
|
|
37
48
|
|
|
38
49
|
mainFileNotFoundCallback = ({ fileRelativeUrl }) => {
|
|
39
50
|
logger.error(
|
|
@@ -55,6 +66,73 @@ export const executeConcurrently = async (
|
|
|
55
66
|
const report = {}
|
|
56
67
|
const executionCount = executionSteps.length
|
|
57
68
|
|
|
69
|
+
let transformReturnValue = (value) => value
|
|
70
|
+
|
|
71
|
+
const coverageTempDirectoryUrl = resolveUrl(
|
|
72
|
+
coverageTempDirectoryRelativeUrl,
|
|
73
|
+
projectDirectoryUrl,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
const structuredMetaMapForCover = normalizeStructuredMetaMap(
|
|
77
|
+
{
|
|
78
|
+
cover: coverageConfig,
|
|
79
|
+
},
|
|
80
|
+
projectDirectoryUrl,
|
|
81
|
+
)
|
|
82
|
+
const coverageIgnorePredicate = (url) => {
|
|
83
|
+
return !urlToMeta({
|
|
84
|
+
url: resolveUrl(url, projectDirectoryUrl),
|
|
85
|
+
structuredMetaMap: structuredMetaMapForCover,
|
|
86
|
+
}).cover
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (coverage) {
|
|
90
|
+
// in case runned multiple times, we don't want to keep writing lot of files in this directory
|
|
91
|
+
if (!process.env.NODE_V8_COVERAGE) {
|
|
92
|
+
await ensureEmptyDirectory(coverageTempDirectoryUrl)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (runtimeSupport.node) {
|
|
96
|
+
// v8 coverage is written in a directoy and auto propagate to subprocesses
|
|
97
|
+
// through process.env.NODE_V8_COVERAGE.
|
|
98
|
+
if (!coverageForceIstanbul && !process.env.NODE_V8_COVERAGE) {
|
|
99
|
+
const v8CoverageDirectory = resolveUrl(
|
|
100
|
+
`./node_v8/${cuid()}`,
|
|
101
|
+
coverageTempDirectoryUrl,
|
|
102
|
+
)
|
|
103
|
+
await writeDirectory(v8CoverageDirectory, { allowUseless: true })
|
|
104
|
+
process.env.NODE_V8_COVERAGE = urlToFileSystemPath(v8CoverageDirectory)
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
transformReturnValue = async (value) => {
|
|
109
|
+
if (multipleExecutionsOperation.signal.aborted) {
|
|
110
|
+
// don't try to do the coverage stuff
|
|
111
|
+
return value
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
try {
|
|
115
|
+
value.coverage = await reportToCoverage(value.report, {
|
|
116
|
+
multipleExecutionsOperation,
|
|
117
|
+
logger,
|
|
118
|
+
projectDirectoryUrl,
|
|
119
|
+
babelPluginMap,
|
|
120
|
+
coverageConfig,
|
|
121
|
+
coverageIncludeMissing,
|
|
122
|
+
coverageForceIstanbul,
|
|
123
|
+
coverageIgnorePredicate,
|
|
124
|
+
coverageV8ConflictWarning,
|
|
125
|
+
})
|
|
126
|
+
} catch (e) {
|
|
127
|
+
if (Abort.isAbortError(e)) {
|
|
128
|
+
return value
|
|
129
|
+
}
|
|
130
|
+
throw e
|
|
131
|
+
}
|
|
132
|
+
return value
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
58
136
|
let previousExecutionResult
|
|
59
137
|
let previousExecutionLog
|
|
60
138
|
let abortedCount = 0
|
|
@@ -116,12 +194,13 @@ export const executeConcurrently = async (
|
|
|
116
194
|
|
|
117
195
|
...executionParams,
|
|
118
196
|
collectCoverage: coverage,
|
|
197
|
+
coverageTempDirectoryUrl,
|
|
119
198
|
runtimeParams: {
|
|
120
199
|
projectDirectoryUrl,
|
|
121
200
|
compileServerOrigin,
|
|
122
201
|
outDirectoryRelativeUrl,
|
|
123
202
|
collectCoverage: coverage,
|
|
124
|
-
|
|
203
|
+
coverageIgnorePredicate,
|
|
125
204
|
coverageForceIstanbul,
|
|
126
205
|
...executionParams.runtimeParams,
|
|
127
206
|
},
|
|
@@ -129,7 +208,7 @@ export const executeConcurrently = async (
|
|
|
129
208
|
fileRelativeUrl,
|
|
130
209
|
...executionParams.executeParams,
|
|
131
210
|
},
|
|
132
|
-
|
|
211
|
+
coverageV8ConflictWarning,
|
|
133
212
|
})
|
|
134
213
|
const afterExecutionInfo = {
|
|
135
214
|
...beforeExecutionInfo,
|
|
@@ -208,31 +287,10 @@ export const executeConcurrently = async (
|
|
|
208
287
|
logger.info(createSummaryLog(summary))
|
|
209
288
|
}
|
|
210
289
|
|
|
211
|
-
|
|
212
|
-
// don't try to do the coverage stuff
|
|
213
|
-
return {
|
|
214
|
-
summary,
|
|
215
|
-
report,
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
return {
|
|
290
|
+
return transformReturnValue({
|
|
220
291
|
summary,
|
|
221
292
|
report,
|
|
222
|
-
|
|
223
|
-
? {
|
|
224
|
-
coverage: await reportToCoverage(report, {
|
|
225
|
-
multipleExecutionsOperation,
|
|
226
|
-
logger,
|
|
227
|
-
projectDirectoryUrl,
|
|
228
|
-
babelPluginMap,
|
|
229
|
-
coverageConfig,
|
|
230
|
-
coverageIncludeMissing,
|
|
231
|
-
coverageV8MergeConflictIsExpected,
|
|
232
|
-
}),
|
|
233
|
-
}
|
|
234
|
-
: {}),
|
|
235
|
-
}
|
|
293
|
+
})
|
|
236
294
|
}
|
|
237
295
|
|
|
238
296
|
const executeInParallel = async ({
|
|
@@ -34,7 +34,8 @@ export const executePlan = async (
|
|
|
34
34
|
coverageConfig,
|
|
35
35
|
coverageIncludeMissing,
|
|
36
36
|
coverageForceIstanbul,
|
|
37
|
-
|
|
37
|
+
coverageV8ConflictWarning,
|
|
38
|
+
coverageTempDirectoryRelativeUrl,
|
|
38
39
|
|
|
39
40
|
compileServerProtocol,
|
|
40
41
|
compileServerPrivateKey,
|
|
@@ -156,7 +157,9 @@ export const executePlan = async (
|
|
|
156
157
|
coverageConfig,
|
|
157
158
|
coverageIncludeMissing,
|
|
158
159
|
coverageForceIstanbul,
|
|
159
|
-
|
|
160
|
+
coverageV8ConflictWarning,
|
|
161
|
+
coverageTempDirectoryRelativeUrl,
|
|
162
|
+
runtimeSupport,
|
|
160
163
|
})
|
|
161
164
|
|
|
162
165
|
return {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
+
import cuid from "cuid"
|
|
1
2
|
import { createLogger, createDetailedMessage } from "@jsenv/logger"
|
|
2
3
|
import { Abort, raceCallbacks } from "@jsenv/abort"
|
|
4
|
+
import { resolveUrl, writeFile } from "@jsenv/filesystem"
|
|
3
5
|
|
|
4
|
-
import {
|
|
6
|
+
import { composeTwoFileByFileIstanbulCoverages } from "./coverage_utils/istanbul_coverage_composition.js"
|
|
5
7
|
|
|
6
8
|
export const launchAndExecute = async ({
|
|
7
9
|
signal = new AbortController().signal,
|
|
@@ -19,6 +21,7 @@ export const launchAndExecute = async ({
|
|
|
19
21
|
collectRuntimeVersion = false,
|
|
20
22
|
inheritCoverage = false,
|
|
21
23
|
collectCoverage = false,
|
|
24
|
+
coverageTempDirectoryUrl,
|
|
22
25
|
measurePerformance,
|
|
23
26
|
collectPerformance = false,
|
|
24
27
|
|
|
@@ -39,8 +42,6 @@ export const launchAndExecute = async ({
|
|
|
39
42
|
// by default throw on error after execution
|
|
40
43
|
throw error
|
|
41
44
|
},
|
|
42
|
-
|
|
43
|
-
coverageV8MergeConflictIsExpected,
|
|
44
45
|
} = {}) => {
|
|
45
46
|
const logger = createLogger({ logLevel: launchAndExecuteLogLevel })
|
|
46
47
|
|
|
@@ -131,40 +132,61 @@ export const launchAndExecute = async ({
|
|
|
131
132
|
executionResultTransformer = composeTransformer(
|
|
132
133
|
executionResultTransformer,
|
|
133
134
|
(executionResult) => {
|
|
134
|
-
const { coverage
|
|
135
|
-
//
|
|
136
|
-
global.__coverage__ =
|
|
135
|
+
const { coverage } = executionResult
|
|
136
|
+
// propagate coverage from execution to this process
|
|
137
|
+
global.__coverage__ = composeTwoFileByFileIstanbulCoverages(
|
|
137
138
|
global.__coverage__ || {},
|
|
138
139
|
coverage || {},
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
if (!collectCoverageSaved) {
|
|
143
|
+
delete executionResult.coverage
|
|
142
144
|
}
|
|
143
|
-
|
|
145
|
+
|
|
146
|
+
return executionResult
|
|
144
147
|
},
|
|
145
148
|
)
|
|
146
149
|
}
|
|
147
150
|
|
|
148
|
-
// indirectCoverage is a feature making possible to collect
|
|
149
|
-
// coverage generated by executing a node process which executes
|
|
150
|
-
// a browser. The coverage coming the browser executionwould be lost
|
|
151
|
-
// if not propagated somehow.
|
|
152
|
-
// This is possible if the node process collect the browser coverage
|
|
153
|
-
// and write it into global.__indirectCoverage__
|
|
154
|
-
// This is used by jsenv during tests execution
|
|
155
151
|
if (collectCoverage) {
|
|
156
152
|
executionResultTransformer = composeTransformer(
|
|
157
153
|
executionResultTransformer,
|
|
158
|
-
(executionResult) => {
|
|
159
|
-
|
|
154
|
+
async (executionResult) => {
|
|
155
|
+
// we do not keep coverage in memory, it can grow very big
|
|
156
|
+
// instead we store it on the filesystem, they will be read and merged together later on
|
|
157
|
+
// in "executeConcurrently"
|
|
158
|
+
const { coverage } = executionResult
|
|
159
|
+
if (coverage) {
|
|
160
|
+
const coverageFileUrl = resolveUrl(
|
|
161
|
+
`./${runtime.name}/${cuid()}`,
|
|
162
|
+
coverageTempDirectoryUrl,
|
|
163
|
+
)
|
|
164
|
+
await writeFile(coverageFileUrl, JSON.stringify(coverage, null, " "))
|
|
165
|
+
executionResult.coverageFileUrl = coverageFileUrl
|
|
166
|
+
delete executionResult.coverage
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// indirectCoverage is a feature making possible to collect
|
|
170
|
+
// coverage generated by executing a node process which executes
|
|
171
|
+
// a browser. The coverage coming the browser execution would be lost
|
|
172
|
+
// if not propagated somehow.
|
|
173
|
+
// This is possible if the node process collect the browser coverage
|
|
174
|
+
// and write it into global.__indirectCoverage__
|
|
175
|
+
// This is used by jsenv during tests execution
|
|
176
|
+
const { indirectCoverage } = executionResult
|
|
160
177
|
if (indirectCoverage) {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
coverageV8MergeConflictIsExpected,
|
|
165
|
-
},
|
|
178
|
+
const indirectCoverageFileUrl = resolveUrl(
|
|
179
|
+
`./${runtime.name}/${cuid()}`,
|
|
180
|
+
coverageTempDirectoryUrl,
|
|
166
181
|
)
|
|
182
|
+
await writeFile(
|
|
183
|
+
indirectCoverageFileUrl,
|
|
184
|
+
JSON.stringify(indirectCoverage, null, " "),
|
|
185
|
+
)
|
|
186
|
+
executionResult.indirectCoverageFileUrl = indirectCoverageFileUrl
|
|
187
|
+
delete executionResult.indirectCoverage
|
|
167
188
|
}
|
|
189
|
+
|
|
168
190
|
return executionResult
|
|
169
191
|
},
|
|
170
192
|
)
|
|
@@ -176,6 +198,7 @@ export const launchAndExecute = async ({
|
|
|
176
198
|
// executionResult.coverage is undefined or {}
|
|
177
199
|
// we delete it just to have a cleaner object
|
|
178
200
|
delete executionResult.coverage
|
|
201
|
+
delete executionResult.indirectCoverage
|
|
179
202
|
return executionResult
|
|
180
203
|
},
|
|
181
204
|
)
|
package/src/launchBrowser.js
CHANGED
|
@@ -37,7 +37,7 @@ chromiumRuntime.launch = async ({
|
|
|
37
37
|
collectPerformance,
|
|
38
38
|
measurePerformance,
|
|
39
39
|
collectCoverage,
|
|
40
|
-
|
|
40
|
+
coverageIgnorePredicate,
|
|
41
41
|
coverageForceIstanbul,
|
|
42
42
|
|
|
43
43
|
headless = true,
|
|
@@ -115,7 +115,7 @@ chromiumRuntime.launch = async ({
|
|
|
115
115
|
collectPerformance,
|
|
116
116
|
measurePerformance,
|
|
117
117
|
collectCoverage,
|
|
118
|
-
|
|
118
|
+
coverageIgnorePredicate,
|
|
119
119
|
coverageForceIstanbul,
|
|
120
120
|
coveragePlaywrightAPIAvailable: true,
|
|
121
121
|
})
|
|
@@ -151,7 +151,7 @@ firefoxRuntime.launch = async ({
|
|
|
151
151
|
collectPerformance,
|
|
152
152
|
measurePerformance,
|
|
153
153
|
collectCoverage,
|
|
154
|
-
|
|
154
|
+
coverageIgnorePredicate,
|
|
155
155
|
coverageForceIstanbul,
|
|
156
156
|
|
|
157
157
|
headless = true,
|
|
@@ -194,7 +194,7 @@ firefoxRuntime.launch = async ({
|
|
|
194
194
|
collectPerformance,
|
|
195
195
|
measurePerformance,
|
|
196
196
|
collectCoverage,
|
|
197
|
-
|
|
197
|
+
coverageIgnorePredicate,
|
|
198
198
|
coverageForceIstanbul,
|
|
199
199
|
})
|
|
200
200
|
|
|
@@ -229,7 +229,7 @@ webkitRuntime.launch = async ({
|
|
|
229
229
|
collectPerformance,
|
|
230
230
|
measurePerformance,
|
|
231
231
|
collectCoverage,
|
|
232
|
-
|
|
232
|
+
coverageIgnorePredicate,
|
|
233
233
|
coverageForceIstanbul,
|
|
234
234
|
|
|
235
235
|
headless = true,
|
|
@@ -272,7 +272,7 @@ webkitRuntime.launch = async ({
|
|
|
272
272
|
collectPerformance,
|
|
273
273
|
measurePerformance,
|
|
274
274
|
collectCoverage,
|
|
275
|
-
|
|
275
|
+
coverageIgnorePredicate,
|
|
276
276
|
coverageForceIstanbul,
|
|
277
277
|
ignoreErrorHook: (error) => {
|
|
278
278
|
// we catch error during execution but safari throw unhandled rejection
|
|
@@ -378,7 +378,7 @@ const browserToRuntimeHooks = (
|
|
|
378
378
|
collectPerformance,
|
|
379
379
|
measurePerformance,
|
|
380
380
|
collectCoverage,
|
|
381
|
-
|
|
381
|
+
coverageIgnorePredicate,
|
|
382
382
|
coverageForceIstanbul,
|
|
383
383
|
coveragePlaywrightAPIAvailable = false,
|
|
384
384
|
ignoreErrorHook = () => false,
|
|
@@ -451,9 +451,9 @@ const browserToRuntimeHooks = (
|
|
|
451
451
|
measurePerformance,
|
|
452
452
|
collectPerformance,
|
|
453
453
|
collectCoverage,
|
|
454
|
-
coverageConfig,
|
|
455
454
|
coverageForceIstanbul,
|
|
456
455
|
coveragePlaywrightAPIAvailable,
|
|
456
|
+
coverageIgnorePredicate,
|
|
457
457
|
transformErrorHook,
|
|
458
458
|
})
|
|
459
459
|
return result
|
package/src/launchNode.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { Script } from "node:vm"
|
|
2
|
-
import cuid from "cuid"
|
|
3
2
|
import { loggerToLogLevel } from "@jsenv/logger"
|
|
4
|
-
import {
|
|
5
|
-
writeDirectory,
|
|
6
|
-
resolveUrl,
|
|
7
|
-
urlToFileSystemPath,
|
|
8
|
-
removeFileSystemNode,
|
|
9
|
-
moveDirectoryContent,
|
|
10
|
-
} from "@jsenv/filesystem"
|
|
11
3
|
|
|
12
4
|
import { jsenvCoreDirectoryUrl } from "@jsenv/core/src/internal/jsenvCoreDirectoryUrl.js"
|
|
13
5
|
import { escapeRegexpSpecialCharacters } from "./internal/escapeRegexpSpecialCharacters.js"
|
|
14
6
|
import { createControllableNodeProcess } from "./internal/node-launcher/createControllableNodeProcess.js"
|
|
15
|
-
import { v8CoverageFromNodeV8Directory } from "./internal/executing/coverage/v8CoverageFromNodeV8Directory.js"
|
|
16
7
|
|
|
17
8
|
export const nodeRuntime = {
|
|
18
9
|
name: "node",
|
|
@@ -30,8 +21,8 @@ nodeRuntime.launch = async ({
|
|
|
30
21
|
measurePerformance,
|
|
31
22
|
collectPerformance,
|
|
32
23
|
collectCoverage = false,
|
|
33
|
-
coverageConfig,
|
|
34
24
|
coverageForceIstanbul,
|
|
25
|
+
coverageConfig,
|
|
35
26
|
|
|
36
27
|
debugPort,
|
|
37
28
|
debugMode,
|
|
@@ -69,59 +60,10 @@ nodeRuntime.launch = async ({
|
|
|
69
60
|
JSENV: true,
|
|
70
61
|
}
|
|
71
62
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
// through process.env.NODE_V8_COVERAGE.
|
|
77
|
-
|
|
78
|
-
if (coverageForceIstanbul) {
|
|
79
|
-
// if we want to force istanbul, we will set process.env.NODE_V8_COVERAGE = ''
|
|
80
|
-
// into the child_process
|
|
81
|
-
env.NODE_V8_COVERAGE = ""
|
|
82
|
-
}
|
|
83
|
-
// } else if (process.env.NODE_V8_COVERAGE) {
|
|
84
|
-
// // The V8_COVERAGE was already set by a parent process or command line.
|
|
85
|
-
// // It's the caller that is interested into coverage, it's not anymore this script
|
|
86
|
-
// // responsability to set process.env.NODE_V8_COVERAGE nor to read
|
|
87
|
-
// // coverage files in the v8 directory.
|
|
88
|
-
// }
|
|
89
|
-
// In fact it is so that it's possible to go coverageception:
|
|
90
|
-
// jsenv collect coverage for tests
|
|
91
|
-
// which are testing that coverage can be collected for tests
|
|
92
|
-
// this is possible because we overriding the child process NODE_V8_COVERAGE
|
|
93
|
-
else {
|
|
94
|
-
const NODE_V8_COVERAGE = await getNodeV8CoverageDir({
|
|
95
|
-
projectDirectoryUrl,
|
|
96
|
-
})
|
|
97
|
-
env.NODE_V8_COVERAGE = NODE_V8_COVERAGE
|
|
98
|
-
|
|
99
|
-
// the v8 coverage directory is available once the child process is disconnected
|
|
100
|
-
finalizeExecutionResult = async (executionResult) => {
|
|
101
|
-
if (
|
|
102
|
-
executionResult.status === "timedout" ||
|
|
103
|
-
executionResult.status === "aborted"
|
|
104
|
-
) {
|
|
105
|
-
return executionResult
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const coverage = await ensureV8CoverageDirClean(async () => {
|
|
109
|
-
// prefer istanbul if available
|
|
110
|
-
if (executionResult.coverage) {
|
|
111
|
-
return executionResult.coverage
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const v8Coverage = await v8CoverageFromNodeV8Directory({
|
|
115
|
-
projectDirectoryUrl,
|
|
116
|
-
NODE_V8_COVERAGE,
|
|
117
|
-
coverageConfig,
|
|
118
|
-
})
|
|
119
|
-
return v8Coverage
|
|
120
|
-
}, NODE_V8_COVERAGE)
|
|
121
|
-
executionResult.coverage = coverage
|
|
122
|
-
return executionResult
|
|
123
|
-
}
|
|
124
|
-
}
|
|
63
|
+
if (coverageForceIstanbul) {
|
|
64
|
+
// if we want to force istanbul, we will set process.env.NODE_V8_COVERAGE = ''
|
|
65
|
+
// into the child_process
|
|
66
|
+
env.NODE_V8_COVERAGE = ""
|
|
125
67
|
}
|
|
126
68
|
|
|
127
69
|
commandLineOptions = [
|
|
@@ -198,39 +140,9 @@ nodeRuntime.launch = async ({
|
|
|
198
140
|
outputCallbackList,
|
|
199
141
|
stop,
|
|
200
142
|
execute,
|
|
201
|
-
finalizeExecutionResult,
|
|
202
143
|
}
|
|
203
144
|
}
|
|
204
145
|
|
|
205
|
-
const ensureV8CoverageDirClean = async (fn, NODE_V8_COVERAGE) => {
|
|
206
|
-
try {
|
|
207
|
-
return await fn()
|
|
208
|
-
} finally {
|
|
209
|
-
if (process.env.NODE_V8_COVERAGE === NODE_V8_COVERAGE) {
|
|
210
|
-
// do not try to remove or copy coverage
|
|
211
|
-
} else if (process.env.NODE_V8_COVERAGE) {
|
|
212
|
-
await moveDirectoryContent({
|
|
213
|
-
from: NODE_V8_COVERAGE,
|
|
214
|
-
to: process.env.NODE_V8_COVERAGE,
|
|
215
|
-
})
|
|
216
|
-
await removeFileSystemNode(NODE_V8_COVERAGE, {})
|
|
217
|
-
} else {
|
|
218
|
-
await removeFileSystemNode(NODE_V8_COVERAGE, {
|
|
219
|
-
recursive: true,
|
|
220
|
-
})
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const getNodeV8CoverageDir = async ({ projectDirectoryUrl }) => {
|
|
226
|
-
const v8CoverageDirectory = resolveUrl(
|
|
227
|
-
`./coverage-v8/${cuid()}`,
|
|
228
|
-
projectDirectoryUrl,
|
|
229
|
-
)
|
|
230
|
-
await writeDirectory(v8CoverageDirectory, { allowUseless: true })
|
|
231
|
-
return urlToFileSystemPath(v8CoverageDirectory)
|
|
232
|
-
}
|
|
233
|
-
|
|
234
146
|
const transformExecutionResult = (
|
|
235
147
|
executionResult,
|
|
236
148
|
{ compileServerOrigin, projectDirectoryUrl },
|