@rpcbase/test 0.227.0 → 0.229.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/README.md +9 -2
- package/package.json +3 -2
- package/src/cli.js +322 -73
- package/src/coverage/config-loader.js +10 -8
package/README.md
CHANGED
|
@@ -98,6 +98,13 @@ Whenever coverage is enabled, the wrapper appends the shared reporter so Istanbu
|
|
|
98
98
|
|
|
99
99
|
## 4. Run tests with `rb-test`
|
|
100
100
|
|
|
101
|
-
Each package
|
|
101
|
+
Each package uses its `npm test` script as `tsc --noEmit && rb-test`. The CLI runs two stages:
|
|
102
102
|
|
|
103
|
-
|
|
103
|
+
- Playwright, with any CLI flags you pass through untouched; repo defaults are applied unless you provide your own `--config`.
|
|
104
|
+
|
|
105
|
+
Coverage is enforced separately:
|
|
106
|
+
|
|
107
|
+
- Playwright uses `spec/coverage.*` via the shared reporter and will fail the run if thresholds aren’t met.
|
|
108
|
+
- Vitest only reads `src/coverage.json` (JSON object). If that file is missing, Vitest coverage is skipped.
|
|
109
|
+
|
|
110
|
+
Need to debug without coverage? Set `RB_DISABLE_COVERAGE=1 npm test` and the Playwright hooks short-circuit.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpcbase/test",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.229.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -53,7 +53,8 @@
|
|
|
53
53
|
"istanbul-reports": "3.2.0",
|
|
54
54
|
"lodash": "4.17.21",
|
|
55
55
|
"picomatch": "2.3.1",
|
|
56
|
-
"v8-to-istanbul": "9.3.0"
|
|
56
|
+
"v8-to-istanbul": "9.3.0",
|
|
57
|
+
"vitest": "4.0.15"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
59
60
|
"mongoose": "8.20.0"
|
package/src/cli.js
CHANGED
|
@@ -2,113 +2,129 @@
|
|
|
2
2
|
|
|
3
3
|
import { spawn } from "child_process"
|
|
4
4
|
import fs from "fs"
|
|
5
|
+
import fsPromises from "fs/promises"
|
|
5
6
|
import path from "path"
|
|
6
7
|
import { createRequire } from "module"
|
|
7
8
|
import { fileURLToPath } from "url"
|
|
8
9
|
|
|
10
|
+
import { createCoverageConfig } from "./coverage/config.js"
|
|
11
|
+
import { loadCoverageOptions } from "./coverage/config-loader.js"
|
|
12
|
+
import { removeCoverageFiles } from "./coverage/files.js"
|
|
13
|
+
import { CoverageThresholdError, generateCoverageReport } from "./coverage/report.js"
|
|
14
|
+
|
|
9
15
|
|
|
10
16
|
const require = createRequire(import.meta.url)
|
|
11
17
|
const moduleDir = path.dirname(fileURLToPath(import.meta.url))
|
|
12
18
|
|
|
13
19
|
|
|
20
|
+
const TEST_GLOB = "{src,lib}/**/*.test.{js,ts,tsx}"
|
|
21
|
+
const VITEST_COVERAGE_CANDIDATES = ["src/coverage.json"]
|
|
22
|
+
|
|
23
|
+
|
|
14
24
|
const isAider = process.env.IS_AIDER === "yes"
|
|
15
25
|
|
|
16
26
|
if (process.env.IS_AIDER !== undefined && process.env.IS_AIDER !== "yes") {
|
|
17
27
|
console.warn("Warning: IS_AIDER is set to a value other than 'yes'.")
|
|
18
28
|
}
|
|
19
29
|
|
|
20
|
-
function runTests() {
|
|
21
|
-
|
|
22
|
-
const userArgs = process.argv.slice(2)
|
|
30
|
+
async function runTests() {
|
|
31
|
+
const userArgs = process.argv.slice(2)
|
|
23
32
|
|
|
24
|
-
|
|
25
|
-
const configPath = fs.existsSync(
|
|
26
|
-
path.join(process.cwd(), "playwright.config.ts"),
|
|
27
|
-
)
|
|
28
|
-
? path.join(process.cwd(), "playwright.config.ts")
|
|
29
|
-
: path.join(moduleDir, "playwright.config.ts")
|
|
33
|
+
const vitestCoverage = await loadVitestCoverageConfig()
|
|
30
34
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
}
|
|
35
|
+
if (vitestCoverage?.enabled) {
|
|
36
|
+
await cleanCoverageArtifacts(vitestCoverage.config)
|
|
37
|
+
}
|
|
35
38
|
|
|
36
|
-
|
|
37
|
-
})
|
|
39
|
+
let testError = null
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
try {
|
|
42
|
+
await runVitest(vitestCoverage)
|
|
43
|
+
await runPlaywright(userArgs)
|
|
44
|
+
} catch (error) {
|
|
45
|
+
testError = error
|
|
46
|
+
}
|
|
40
47
|
|
|
41
|
-
|
|
42
|
-
|
|
48
|
+
if (vitestCoverage?.enabled) {
|
|
49
|
+
try {
|
|
50
|
+
await finalizeCoverage(vitestCoverage.config)
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (!testError) {
|
|
53
|
+
testError = error
|
|
54
|
+
}
|
|
43
55
|
}
|
|
56
|
+
}
|
|
44
57
|
|
|
45
|
-
|
|
58
|
+
if (testError) {
|
|
59
|
+
throw testError
|
|
60
|
+
}
|
|
61
|
+
}
|
|
46
62
|
|
|
47
|
-
|
|
48
|
-
|
|
63
|
+
runTests()
|
|
64
|
+
.then(() => process.exit(0))
|
|
65
|
+
.catch(() => process.exit(1))
|
|
49
66
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
...process.env,
|
|
55
|
-
NODE_OPTIONS: nodeOptions,
|
|
56
|
-
}
|
|
57
|
-
const playwright = spawn(
|
|
58
|
-
launcher.command,
|
|
59
|
-
[...launcher.args, ...playwrightArgs],
|
|
60
|
-
{
|
|
61
|
-
shell: false,
|
|
62
|
-
env,
|
|
63
|
-
},
|
|
64
|
-
)
|
|
67
|
+
async function runVitest(coverage) {
|
|
68
|
+
const vitestArgs = ["run", TEST_GLOB, "--passWithNoTests"]
|
|
69
|
+
const launcher = resolveVitestLauncher()
|
|
70
|
+
const env = withRegisterShim(process.env)
|
|
65
71
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
stdoutBuffer.push(data.toString())
|
|
71
|
-
})
|
|
72
|
+
if (coverage?.enabled) {
|
|
73
|
+
env.NODE_V8_COVERAGE = coverage.nodeCoverageDir
|
|
74
|
+
}
|
|
72
75
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
await spawnWithLogs({
|
|
77
|
+
name: "Vitest",
|
|
78
|
+
launcher,
|
|
79
|
+
args: vitestArgs,
|
|
80
|
+
env,
|
|
81
|
+
successMessage: "Vitest suite passed!",
|
|
82
|
+
failureMessage: "Vitest failed",
|
|
83
|
+
})
|
|
79
84
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
} else {
|
|
85
|
-
console.error("Tests failed:")
|
|
85
|
+
if (coverage?.enabled) {
|
|
86
|
+
await convertNodeCoverage(coverage)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
86
89
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
90
|
+
function runPlaywright(userArgs) {
|
|
91
|
+
// Determine config file path
|
|
92
|
+
const configPath = fs.existsSync(
|
|
93
|
+
path.join(process.cwd(), "playwright.config.ts"),
|
|
94
|
+
)
|
|
95
|
+
? path.join(process.cwd(), "playwright.config.ts")
|
|
96
|
+
: path.join(moduleDir, "playwright.config.ts")
|
|
97
|
+
|
|
98
|
+
const hasCustomConfig = userArgs.some((arg) => {
|
|
99
|
+
if (arg === "--config" || arg === "-c") {
|
|
100
|
+
return true
|
|
101
|
+
}
|
|
91
102
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
}
|
|
95
|
-
}
|
|
103
|
+
return arg.startsWith("--config=")
|
|
104
|
+
})
|
|
96
105
|
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
})
|
|
106
|
+
const playwrightArgs = ["test"]
|
|
100
107
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
108
|
+
if (!hasCustomConfig) {
|
|
109
|
+
playwrightArgs.push("--config", configPath)
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
playwrightArgs.push(...userArgs)
|
|
113
|
+
|
|
114
|
+
ensureJsxRuntimeShim(process.cwd())
|
|
115
|
+
const launcher = resolvePlaywrightLauncher()
|
|
116
|
+
const env = withRegisterShim(process.env)
|
|
117
|
+
|
|
118
|
+
return spawnWithLogs({
|
|
119
|
+
name: "Playwright",
|
|
120
|
+
launcher,
|
|
121
|
+
args: playwrightArgs,
|
|
122
|
+
env,
|
|
123
|
+
successMessage: "Playwright suite passed!",
|
|
124
|
+
failureMessage: "Playwright failed",
|
|
105
125
|
})
|
|
106
126
|
}
|
|
107
127
|
|
|
108
|
-
runTests()
|
|
109
|
-
.then(() => process.exit(0))
|
|
110
|
-
.catch(() => process.exit(1))
|
|
111
|
-
|
|
112
128
|
function resolvePlaywrightLauncher() {
|
|
113
129
|
const cliPath = resolveCliPath()
|
|
114
130
|
if (cliPath) {
|
|
@@ -150,6 +166,239 @@ function resolveCliPath() {
|
|
|
150
166
|
return null
|
|
151
167
|
}
|
|
152
168
|
|
|
169
|
+
function resolveVitestLauncher() {
|
|
170
|
+
const searchRoots = [process.cwd(), moduleDir]
|
|
171
|
+
|
|
172
|
+
for (const base of searchRoots) {
|
|
173
|
+
try {
|
|
174
|
+
const pkgPath = require.resolve("vitest/package.json", { paths: [base] })
|
|
175
|
+
const pkgDir = path.dirname(pkgPath)
|
|
176
|
+
const pkgJson = JSON.parse(fs.readFileSync(pkgPath, "utf8"))
|
|
177
|
+
const binPath = typeof pkgJson.bin === "string" ? pkgJson.bin : pkgJson.bin?.vitest
|
|
178
|
+
if (binPath) {
|
|
179
|
+
return {
|
|
180
|
+
command: process.execPath,
|
|
181
|
+
args: [path.join(pkgDir, binPath)],
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} catch (_error) {
|
|
185
|
+
// continue searching
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const localBin = path.resolve(process.cwd(), "node_modules/.bin/vitest")
|
|
190
|
+
if (fs.existsSync(localBin)) {
|
|
191
|
+
return {
|
|
192
|
+
command: localBin,
|
|
193
|
+
args: [],
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return {
|
|
198
|
+
command: "vitest",
|
|
199
|
+
args: [],
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function loadVitestCoverageConfig() {
|
|
204
|
+
const options = await loadCoverageOptions({
|
|
205
|
+
optional: true,
|
|
206
|
+
candidates: VITEST_COVERAGE_CANDIDATES,
|
|
207
|
+
defaultTestResultsDir: "test-results-vitest",
|
|
208
|
+
})
|
|
209
|
+
if (!options) {
|
|
210
|
+
return null
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const config = createCoverageConfig(options)
|
|
214
|
+
|
|
215
|
+
return {
|
|
216
|
+
config,
|
|
217
|
+
nodeCoverageDir: path.join(config.testResultsRoot, "node-coverage"),
|
|
218
|
+
enabled: config.coverageEnabled,
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function cleanCoverageArtifacts(config) {
|
|
223
|
+
await removeCoverageFiles(config)
|
|
224
|
+
await fsPromises.rm(config.coverageReportDir, { recursive: true, force: true })
|
|
225
|
+
await fsPromises.rm(path.join(config.testResultsRoot, "node-coverage"), { recursive: true, force: true })
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function convertNodeCoverage(coverage) {
|
|
229
|
+
const { config, nodeCoverageDir } = coverage
|
|
230
|
+
|
|
231
|
+
const entries = await fsPromises.readdir(nodeCoverageDir).catch(() => [])
|
|
232
|
+
const scripts = []
|
|
233
|
+
|
|
234
|
+
for (const entry of entries) {
|
|
235
|
+
if (!entry.endsWith(".json")) {
|
|
236
|
+
continue
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const fullPath = path.join(nodeCoverageDir, entry)
|
|
240
|
+
const payload = await readJson(fullPath)
|
|
241
|
+
const results = Array.isArray(payload?.result) ? payload.result : []
|
|
242
|
+
|
|
243
|
+
for (const script of results) {
|
|
244
|
+
const normalized = normalizeNodeScriptUrl(script.url, config.workspaceRoot)
|
|
245
|
+
if (!normalized) {
|
|
246
|
+
continue
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (!isInsideLib(normalized.absolutePath, config.libRoots)) {
|
|
250
|
+
continue
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
const source = await fsPromises.readFile(normalized.absolutePath, "utf8").catch(() => "")
|
|
254
|
+
|
|
255
|
+
scripts.push({
|
|
256
|
+
absolutePath: normalized.absolutePath,
|
|
257
|
+
relativePath: normalized.relativePath,
|
|
258
|
+
source,
|
|
259
|
+
functions: script.functions ?? [],
|
|
260
|
+
url: script.url,
|
|
261
|
+
})
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (scripts.length === 0) {
|
|
266
|
+
return
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const outDir = path.join(config.testResultsRoot, "vitest")
|
|
270
|
+
await fsPromises.mkdir(outDir, { recursive: true })
|
|
271
|
+
const outputFile = path.join(outDir, config.coverageFileName)
|
|
272
|
+
await fsPromises.writeFile(outputFile, JSON.stringify({ testId: "vitest", scripts }, null, 2), "utf8")
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function finalizeCoverage(config) {
|
|
276
|
+
try {
|
|
277
|
+
await generateCoverageReport(config)
|
|
278
|
+
} catch (error) {
|
|
279
|
+
if (error instanceof CoverageThresholdError) {
|
|
280
|
+
console.error(error.message)
|
|
281
|
+
}
|
|
282
|
+
throw error
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
async function readJson(filePath) {
|
|
287
|
+
try {
|
|
288
|
+
const raw = await fsPromises.readFile(filePath, "utf8")
|
|
289
|
+
return JSON.parse(raw)
|
|
290
|
+
} catch {
|
|
291
|
+
return null
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function normalizeNodeScriptUrl(rawUrl, workspaceRoot) {
|
|
296
|
+
if (!rawUrl || rawUrl.startsWith("node:")) {
|
|
297
|
+
return null
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
let absolutePath = null
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
if (rawUrl.startsWith("file://")) {
|
|
304
|
+
absolutePath = fileURLToPath(rawUrl)
|
|
305
|
+
}
|
|
306
|
+
} catch (_err) {
|
|
307
|
+
// ignore invalid URLs
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!absolutePath && path.isAbsolute(rawUrl)) {
|
|
311
|
+
absolutePath = rawUrl
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!absolutePath) {
|
|
315
|
+
return null
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const normalized = path.normalize(absolutePath)
|
|
319
|
+
if (!normalized.startsWith(workspaceRoot)) {
|
|
320
|
+
return null
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return {
|
|
324
|
+
absolutePath: normalized,
|
|
325
|
+
relativePath: path.relative(workspaceRoot, normalized),
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
function isInsideLib(absolutePath, libRoots) {
|
|
330
|
+
return libRoots.some((libRoot) => {
|
|
331
|
+
const relative = path.relative(libRoot, absolutePath)
|
|
332
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function spawnWithLogs({ name, launcher, args, env, successMessage, failureMessage }) {
|
|
337
|
+
return new Promise((resolve, reject) => {
|
|
338
|
+
const stdoutBuffer = []
|
|
339
|
+
const stderrBuffer = []
|
|
340
|
+
|
|
341
|
+
const child = spawn(
|
|
342
|
+
launcher.command,
|
|
343
|
+
[...(launcher.args || []), ...args],
|
|
344
|
+
{
|
|
345
|
+
shell: false,
|
|
346
|
+
env,
|
|
347
|
+
},
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
child.stdout.on("data", (data) => {
|
|
351
|
+
if (!isAider) {
|
|
352
|
+
process.stdout.write(data)
|
|
353
|
+
}
|
|
354
|
+
stdoutBuffer.push(data.toString())
|
|
355
|
+
})
|
|
356
|
+
|
|
357
|
+
child.stderr.on("data", (data) => {
|
|
358
|
+
if (!isAider) {
|
|
359
|
+
process.stderr.write(data)
|
|
360
|
+
}
|
|
361
|
+
stderrBuffer.push(data.toString())
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
child.on("close", (code) => {
|
|
365
|
+
if (code === 0) {
|
|
366
|
+
if (successMessage) {
|
|
367
|
+
console.log(successMessage)
|
|
368
|
+
}
|
|
369
|
+
resolve()
|
|
370
|
+
} else {
|
|
371
|
+
console.error(failureMessage || `${name} failed:`)
|
|
372
|
+
|
|
373
|
+
if (isAider) {
|
|
374
|
+
if (stdoutBuffer.length > 0) {
|
|
375
|
+
console.error(stdoutBuffer.join(""))
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
if (stderrBuffer.length > 0) {
|
|
379
|
+
console.error(stderrBuffer.join(""))
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
reject(new Error(`${name} failed with exit code: ${code}`))
|
|
384
|
+
}
|
|
385
|
+
})
|
|
386
|
+
|
|
387
|
+
child.on("error", (error) => {
|
|
388
|
+
console.error(`Error spawning ${name}:`, error)
|
|
389
|
+
reject(error)
|
|
390
|
+
})
|
|
391
|
+
})
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function withRegisterShim(baseEnv) {
|
|
395
|
+
const nodeOptions = appendNodeRequire(baseEnv.NODE_OPTIONS, path.join(moduleDir, "register-tty.cjs"))
|
|
396
|
+
return {
|
|
397
|
+
...baseEnv,
|
|
398
|
+
NODE_OPTIONS: nodeOptions,
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
|
|
153
402
|
function ensureJsxRuntimeShim(projectRoot) {
|
|
154
403
|
const shimDir = path.join(projectRoot, "node_modules", "playwright")
|
|
155
404
|
fs.mkdirSync(shimDir, { recursive: true })
|
|
@@ -7,7 +7,7 @@ import { pathToFileURL } from "node:url"
|
|
|
7
7
|
import { build } from "esbuild"
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
const
|
|
10
|
+
const DEFAULT_COVERAGE_CANDIDATES = [
|
|
11
11
|
"spec/coverage.ts",
|
|
12
12
|
"spec/coverage.mts",
|
|
13
13
|
"spec/coverage.cts",
|
|
@@ -17,9 +17,9 @@ const COVERAGE_CANDIDATES = [
|
|
|
17
17
|
"spec/coverage.json",
|
|
18
18
|
]
|
|
19
19
|
|
|
20
|
-
export async function loadCoverageOptions({ optional = false } = {}) {
|
|
20
|
+
export async function loadCoverageOptions({ optional = false, candidates = DEFAULT_COVERAGE_CANDIDATES, defaultTestResultsDir } = {}) {
|
|
21
21
|
const projectRoot = process.cwd()
|
|
22
|
-
const resolved = await findCoverageFile(projectRoot)
|
|
22
|
+
const resolved = await findCoverageFile(projectRoot, candidates)
|
|
23
23
|
|
|
24
24
|
if (!resolved) {
|
|
25
25
|
if (optional) {
|
|
@@ -35,11 +35,11 @@ export async function loadCoverageOptions({ optional = false } = {}) {
|
|
|
35
35
|
throw new Error(`Coverage config at ${resolved} must export an object.`)
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
return normalizeOptions(raw, resolved)
|
|
38
|
+
return normalizeOptions(raw, resolved, defaultTestResultsDir)
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
async function findCoverageFile(root) {
|
|
42
|
-
for (const relative of
|
|
41
|
+
async function findCoverageFile(root, candidates) {
|
|
42
|
+
for (const relative of candidates) {
|
|
43
43
|
const candidate = path.resolve(root, relative)
|
|
44
44
|
try {
|
|
45
45
|
await fs.access(candidate)
|
|
@@ -101,7 +101,7 @@ async function loadModule(url) {
|
|
|
101
101
|
return imported
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
function normalizeOptions(rawOptions, filePath) {
|
|
104
|
+
function normalizeOptions(rawOptions, filePath, defaultTestResultsDir) {
|
|
105
105
|
const options = { ...rawOptions }
|
|
106
106
|
const configDir = path.dirname(filePath)
|
|
107
107
|
|
|
@@ -116,9 +116,11 @@ function normalizeOptions(rawOptions, filePath) {
|
|
|
116
116
|
return {
|
|
117
117
|
workspaceRoot,
|
|
118
118
|
libDirs,
|
|
119
|
-
testResultsDir: options.testResultsDir ?? "test-results",
|
|
119
|
+
testResultsDir: options.testResultsDir ?? defaultTestResultsDir ?? "test-results",
|
|
120
120
|
coverageReportSubdir: options.coverageReportSubdir ?? "coverage",
|
|
121
121
|
coverageFileName: options.coverageFileName ?? "v8-coverage.json",
|
|
122
122
|
thresholds: options.thresholds ?? {},
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
+
|
|
126
|
+
export { DEFAULT_COVERAGE_CANDIDATES }
|