@rpcbase/test 0.308.0 → 0.310.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/dist/clearDatabase.d.ts +2 -0
- package/dist/clearDatabase.d.ts.map +1 -0
- package/dist/clearDatabase.js +12 -0
- package/dist/clearDatabase.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +785 -0
- package/dist/cli.js.map +1 -0
- package/dist/coverage/collect.d.ts +5 -0
- package/dist/coverage/collect.d.ts.map +1 -0
- package/dist/coverage/collect.js +113 -0
- package/dist/coverage/collect.js.map +1 -0
- package/dist/coverage/config-loader.d.ts +11 -0
- package/dist/coverage/config-loader.d.ts.map +1 -0
- package/dist/coverage/config-loader.js +292 -0
- package/dist/coverage/config-loader.js.map +1 -0
- package/dist/coverage/config.d.ts +3 -0
- package/dist/coverage/config.d.ts.map +1 -0
- package/dist/coverage/config.js +110 -0
- package/dist/coverage/config.js.map +1 -0
- package/dist/coverage/files.d.ts +4 -0
- package/dist/coverage/files.d.ts.map +1 -0
- package/dist/coverage/files.js +52 -0
- package/dist/coverage/files.js.map +1 -0
- package/dist/coverage/fixtures.d.ts +5 -0
- package/dist/coverage/fixtures.d.ts.map +1 -0
- package/dist/coverage/fixtures.js +42 -0
- package/dist/coverage/fixtures.js.map +1 -0
- package/dist/coverage/global-setup.d.ts +3 -0
- package/dist/coverage/global-setup.d.ts.map +1 -0
- package/dist/coverage/global-setup.js +18 -0
- package/dist/coverage/global-setup.js.map +1 -0
- package/dist/coverage/index.d.ts +10 -0
- package/dist/coverage/index.d.ts.map +1 -0
- package/dist/coverage/index.js +62 -0
- package/dist/coverage/index.js.map +1 -0
- package/dist/coverage/report.d.ts +7 -0
- package/dist/coverage/report.d.ts.map +1 -0
- package/{src → dist}/coverage/report.js +262 -362
- package/dist/coverage/report.js.map +1 -0
- package/dist/coverage/reporter.d.ts +16 -0
- package/dist/coverage/reporter.d.ts.map +1 -0
- package/dist/coverage/reporter.js +57 -0
- package/dist/coverage/reporter.js.map +1 -0
- package/dist/coverage/types.d.ts +47 -0
- package/dist/coverage/types.d.ts.map +1 -0
- package/dist/coverage/v8-tracker.d.ts +6 -0
- package/dist/coverage/v8-tracker.d.ts.map +1 -0
- package/dist/coverage/v8-tracker.js +166 -0
- package/dist/coverage/v8-tracker.js.map +1 -0
- package/dist/defineConfig.d.ts +3 -0
- package/dist/defineConfig.d.ts.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/serverCoverage.d.ts +8 -0
- package/dist/serverCoverage.d.ts.map +1 -0
- package/dist/serverCoverage.js +42 -0
- package/dist/serverCoverage.js.map +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +83 -0
- package/dist/vitest.config.js.map +1 -0
- package/package.json +25 -14
- package/index.d.ts +0 -64
- package/src/clearDatabase.js +0 -19
- package/src/cli.js +0 -948
- package/src/coverage/collect.js +0 -134
- package/src/coverage/config-loader.js +0 -202
- package/src/coverage/config.js +0 -134
- package/src/coverage/files.js +0 -55
- package/src/coverage/fixtures.js +0 -37
- package/src/coverage/global-setup.js +0 -19
- package/src/coverage/index.js +0 -30
- package/src/coverage/reporter.js +0 -65
- package/src/coverage/v8-tracker.js +0 -205
- package/src/defineConfig.js +0 -129
- package/src/index.js +0 -49
- package/src/vitest.config.mjs +0 -107
- /package/{src → dist}/register-tty.cjs +0 -0
package/src/coverage/collect.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
|
|
3
|
-
import picomatch from "picomatch"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export function createCollectCoverageMatcher(patterns, rootDir) {
|
|
7
|
-
const normalizedRoot = path.resolve(String(rootDir ?? ""))
|
|
8
|
-
|
|
9
|
-
const includeMatchers = []
|
|
10
|
-
const excludeMatchers = []
|
|
11
|
-
|
|
12
|
-
if (Array.isArray(patterns)) {
|
|
13
|
-
for (const pattern of patterns) {
|
|
14
|
-
const raw = String(pattern ?? "").trim()
|
|
15
|
-
if (!raw) {
|
|
16
|
-
continue
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const isExclude = raw.startsWith("!")
|
|
20
|
-
const body = toPosix(isExclude ? raw.slice(1) : raw)
|
|
21
|
-
if (!body) {
|
|
22
|
-
continue
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const matcher = picomatch(body, { dot: true })
|
|
26
|
-
if (isExclude) {
|
|
27
|
-
excludeMatchers.push(matcher)
|
|
28
|
-
} else {
|
|
29
|
-
includeMatchers.push(matcher)
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (includeMatchers.length === 0) {
|
|
35
|
-
return () => false
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return (absolutePath) => {
|
|
39
|
-
const normalizedAbsolute = path.resolve(String(absolutePath ?? ""))
|
|
40
|
-
if (!normalizedAbsolute) {
|
|
41
|
-
return false
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const relativePosix = toPosix(path.relative(normalizedRoot, normalizedAbsolute))
|
|
45
|
-
const absolutePosix = toPosix(normalizedAbsolute)
|
|
46
|
-
const candidates = new Set([absolutePosix, relativePosix])
|
|
47
|
-
|
|
48
|
-
if (relativePosix) {
|
|
49
|
-
candidates.add(`./${relativePosix}`)
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
const candidateList = Array.from(candidates)
|
|
53
|
-
const included = includeMatchers.some((matcher) => candidateList.some((candidate) => matcher(candidate)))
|
|
54
|
-
if (!included) {
|
|
55
|
-
return false
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return !excludeMatchers.some((matcher) => candidateList.some((candidate) => matcher(candidate)))
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export function toPosix(input) {
|
|
63
|
-
return String(input ?? "").split(path.sep).join("/")
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export function isInsideAnyRoot(absolutePath, roots) {
|
|
67
|
-
const normalizedAbsolute = path.resolve(String(absolutePath ?? ""))
|
|
68
|
-
return (Array.isArray(roots) ? roots : []).some((root) => {
|
|
69
|
-
const normalizedRoot = path.resolve(String(root ?? ""))
|
|
70
|
-
const relative = path.relative(normalizedRoot, normalizedAbsolute)
|
|
71
|
-
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative))
|
|
72
|
-
})
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function resolveCollectCoverageRoots(patterns, rootDir) {
|
|
76
|
-
const resolvedRootDir = path.resolve(String(rootDir ?? ""))
|
|
77
|
-
const roots = new Set([resolvedRootDir])
|
|
78
|
-
|
|
79
|
-
if (!Array.isArray(patterns)) {
|
|
80
|
-
return Array.from(roots)
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
for (const pattern of patterns) {
|
|
84
|
-
const raw = String(pattern ?? "").trim()
|
|
85
|
-
if (!raw) {
|
|
86
|
-
continue
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const cleaned = raw.startsWith("!") ? raw.slice(1) : raw
|
|
90
|
-
if (!cleaned) {
|
|
91
|
-
continue
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const prefix = staticGlobPrefix(cleaned)
|
|
95
|
-
if (!prefix) {
|
|
96
|
-
continue
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
roots.add(path.resolve(resolvedRootDir, prefix))
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return Array.from(roots)
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
function staticGlobPrefix(pattern) {
|
|
106
|
-
const normalized = String(pattern ?? "").trim()
|
|
107
|
-
if (!normalized) {
|
|
108
|
-
return null
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
const firstGlobIndex = findFirstGlobIndex(normalized)
|
|
112
|
-
const prefix = firstGlobIndex === -1 ? normalized : normalized.slice(0, firstGlobIndex)
|
|
113
|
-
|
|
114
|
-
if (!prefix) {
|
|
115
|
-
return null
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
if (prefix.endsWith("/") || prefix.endsWith(path.sep)) {
|
|
119
|
-
return prefix
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const posix = prefix.split(path.sep).join("/")
|
|
123
|
-
return path.posix.dirname(posix)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function findFirstGlobIndex(value) {
|
|
127
|
-
for (let i = 0; i < value.length; i += 1) {
|
|
128
|
-
const char = value[i]
|
|
129
|
-
if (char === "*" || char === "?" || char === "[" || char === "{") {
|
|
130
|
-
return i
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return -1
|
|
134
|
-
}
|
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises"
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
import os from "node:os"
|
|
4
|
-
import crypto from "node:crypto"
|
|
5
|
-
import { pathToFileURL } from "node:url"
|
|
6
|
-
|
|
7
|
-
import { build } from "esbuild"
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
const DEFAULT_COVERAGE_CANDIDATES = [
|
|
11
|
-
"spec/coverage.config.ts",
|
|
12
|
-
"spec/coverage.config.js",
|
|
13
|
-
"spec/coverage.config.mjs",
|
|
14
|
-
"spec/coverage.config.cjs",
|
|
15
|
-
"spec/coverage.config.json",
|
|
16
|
-
]
|
|
17
|
-
|
|
18
|
-
const LEGACY_COVERAGE_CANDIDATES = [
|
|
19
|
-
"spec/coverage.ts",
|
|
20
|
-
"spec/coverage.js",
|
|
21
|
-
"spec/coverage.mjs",
|
|
22
|
-
"spec/coverage.cjs",
|
|
23
|
-
"spec/coverage.json",
|
|
24
|
-
]
|
|
25
|
-
|
|
26
|
-
export async function loadCoverageOptions({ optional = false, candidates = DEFAULT_COVERAGE_CANDIDATES, defaultTestResultsDir } = {}) {
|
|
27
|
-
const projectRoot = process.cwd()
|
|
28
|
-
const legacy = await findCoverageFile(projectRoot, LEGACY_COVERAGE_CANDIDATES)
|
|
29
|
-
if (legacy) {
|
|
30
|
-
const ext = path.extname(legacy)
|
|
31
|
-
const suggested = path.join("spec", `coverage.config${ext}`)
|
|
32
|
-
throw new Error(
|
|
33
|
-
`Legacy coverage config detected (${path.relative(projectRoot, legacy)}). Rename it to ${suggested}.`,
|
|
34
|
-
)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const resolved = await findCoverageFile(projectRoot, candidates)
|
|
38
|
-
|
|
39
|
-
if (!resolved) {
|
|
40
|
-
if (optional) {
|
|
41
|
-
return null
|
|
42
|
-
}
|
|
43
|
-
throw new Error(
|
|
44
|
-
"Coverage config not found. Create `spec/coverage.config.{ts,js,json}` with your coverage settings.",
|
|
45
|
-
)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const raw = await importCoverageModule(resolved)
|
|
49
|
-
if (!raw || typeof raw !== "object") {
|
|
50
|
-
throw new Error(`Coverage config at ${resolved} must export an object.`)
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return await normalizeOptions(raw, resolved, defaultTestResultsDir)
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
async function findCoverageFile(root, candidates) {
|
|
57
|
-
for (const relative of candidates) {
|
|
58
|
-
const candidate = path.resolve(root, relative)
|
|
59
|
-
try {
|
|
60
|
-
await fs.access(candidate)
|
|
61
|
-
return candidate
|
|
62
|
-
} catch {
|
|
63
|
-
// continue
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
return null
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
async function importCoverageModule(filePath) {
|
|
70
|
-
const ext = path.extname(filePath)
|
|
71
|
-
|
|
72
|
-
if (ext === ".json") {
|
|
73
|
-
const raw = await fs.readFile(filePath, "utf8")
|
|
74
|
-
return JSON.parse(raw)
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (ext === ".ts") {
|
|
78
|
-
const compiledUrl = await compileTsModule(filePath)
|
|
79
|
-
return loadModule(compiledUrl)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
const moduleUrl = pathToFileURL(filePath).href
|
|
83
|
-
return loadModule(moduleUrl)
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async function compileTsModule(filePath) {
|
|
87
|
-
const stat = await fs.stat(filePath)
|
|
88
|
-
const hash = crypto
|
|
89
|
-
.createHash("sha1")
|
|
90
|
-
.update(filePath)
|
|
91
|
-
.update(String(stat.mtimeMs))
|
|
92
|
-
.digest("hex")
|
|
93
|
-
|
|
94
|
-
const outDir = path.join(os.tmpdir(), "rpcbase-test")
|
|
95
|
-
await fs.mkdir(outDir, { recursive: true })
|
|
96
|
-
const outfile = path.join(outDir, `coverage-${hash}.mjs`)
|
|
97
|
-
|
|
98
|
-
await build({
|
|
99
|
-
entryPoints: [filePath],
|
|
100
|
-
outfile,
|
|
101
|
-
platform: "node",
|
|
102
|
-
format: "esm",
|
|
103
|
-
bundle: false,
|
|
104
|
-
sourcemap: "inline",
|
|
105
|
-
logLevel: "silent",
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
return pathToFileURL(outfile).href
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async function loadModule(url) {
|
|
112
|
-
const imported = await import(url)
|
|
113
|
-
if (imported && typeof imported.default === "object") {
|
|
114
|
-
return imported.default
|
|
115
|
-
}
|
|
116
|
-
return imported
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const DEFAULT_COLLECT_COVERAGE_EXTENSIONS = "ts,tsx,js,jsx,mjs,cjs"
|
|
120
|
-
const DEFAULT_COLLECT_COVERAGE_TEST_EXCLUDE = `!**/*.test.{${DEFAULT_COLLECT_COVERAGE_EXTENSIONS}}`
|
|
121
|
-
|
|
122
|
-
async function resolveCollectCoverageFrom(rawPatterns, rootDir) {
|
|
123
|
-
const resolvedRootDir = path.resolve(String(rootDir ?? ""))
|
|
124
|
-
|
|
125
|
-
const normalized = Array.isArray(rawPatterns)
|
|
126
|
-
? rawPatterns
|
|
127
|
-
.map((pattern) => String(pattern ?? "").trim())
|
|
128
|
-
.filter((pattern) => pattern.length > 0)
|
|
129
|
-
: []
|
|
130
|
-
|
|
131
|
-
const excludes = normalized.filter((pattern) => pattern.startsWith("!"))
|
|
132
|
-
const hasIncludes = normalized.some((pattern) => !pattern.startsWith("!"))
|
|
133
|
-
if (hasIncludes) {
|
|
134
|
-
return withDefaultExcludes(normalized)
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const defaults = await inferDefaultCollectCoverageFrom(resolvedRootDir)
|
|
138
|
-
if (defaults.length === 0) {
|
|
139
|
-
throw new Error(
|
|
140
|
-
"Coverage config: couldn't infer a default `collectCoverageFrom` (src/ directory missing). Provide `collectCoverageFrom` with at least one positive glob pattern.",
|
|
141
|
-
)
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
return withDefaultExcludes([...defaults, ...excludes])
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
async function inferDefaultCollectCoverageFrom(rootDir) {
|
|
148
|
-
const srcDir = path.join(rootDir, "src")
|
|
149
|
-
if (await isDirectory(srcDir)) {
|
|
150
|
-
return [`src/**/*.{${DEFAULT_COLLECT_COVERAGE_EXTENSIONS}}`]
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return []
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function withDefaultExcludes(patterns) {
|
|
157
|
-
if (!Array.isArray(patterns) || patterns.length === 0) {
|
|
158
|
-
return patterns
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const hasTestExclude = patterns.some((pattern) => {
|
|
162
|
-
const raw = String(pattern ?? "")
|
|
163
|
-
if (!raw.startsWith("!")) {
|
|
164
|
-
return false
|
|
165
|
-
}
|
|
166
|
-
return /\.test(?:\.|\{|$)/.test(raw.slice(1))
|
|
167
|
-
})
|
|
168
|
-
if (hasTestExclude) {
|
|
169
|
-
return patterns
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
return [...patterns, DEFAULT_COLLECT_COVERAGE_TEST_EXCLUDE]
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
async function isDirectory(filePath) {
|
|
176
|
-
try {
|
|
177
|
-
const stat = await fs.stat(filePath)
|
|
178
|
-
return stat.isDirectory()
|
|
179
|
-
} catch {
|
|
180
|
-
return false
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
async function normalizeOptions(rawOptions, filePath, defaultTestResultsDir) {
|
|
185
|
-
const options = { ...rawOptions }
|
|
186
|
-
const configDir = path.dirname(filePath)
|
|
187
|
-
const rootDir = path.resolve(configDir, "..")
|
|
188
|
-
|
|
189
|
-
const collectCoverageFrom = await resolveCollectCoverageFrom(options.collectCoverageFrom, rootDir)
|
|
190
|
-
|
|
191
|
-
return {
|
|
192
|
-
rootDir,
|
|
193
|
-
collectCoverageFrom,
|
|
194
|
-
testResultsDir: options.testResultsDir ?? defaultTestResultsDir ?? "test-results",
|
|
195
|
-
coverageReportSubdir: options.coverageReportSubdir ?? "coverage",
|
|
196
|
-
coverageFileName: options.coverageFileName ?? "v8-coverage.json",
|
|
197
|
-
includeAllFiles: options.includeAllFiles,
|
|
198
|
-
thresholds: options.thresholds ?? {},
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export { DEFAULT_COVERAGE_CANDIDATES }
|
package/src/coverage/config.js
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import path from "node:path"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const DEFAULT_THRESHOLDS = {
|
|
5
|
-
branches: 60,
|
|
6
|
-
functions: 75,
|
|
7
|
-
lines: 75,
|
|
8
|
-
statements: 75,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const THRESHOLD_KEYS = Object.keys(DEFAULT_THRESHOLDS)
|
|
12
|
-
|
|
13
|
-
function resolveDir(root, target, fallback) {
|
|
14
|
-
if (!target) {
|
|
15
|
-
return path.resolve(root, fallback)
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (path.isAbsolute(target)) {
|
|
19
|
-
return target
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return path.resolve(root, target)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function createCoverageConfig(options = {}) {
|
|
26
|
-
const { rootDir } = options
|
|
27
|
-
if (!rootDir) {
|
|
28
|
-
throw new Error("createCoverageConfig requires a rootDir")
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const resolvedRootDir = path.resolve(rootDir)
|
|
32
|
-
const includeAllFiles = options.includeAllFiles !== false
|
|
33
|
-
|
|
34
|
-
const collectCoverageFrom = Array.isArray(options.collectCoverageFrom)
|
|
35
|
-
? options.collectCoverageFrom
|
|
36
|
-
.map((pattern) => String(pattern ?? "").trim())
|
|
37
|
-
.filter((pattern) => pattern.length > 0)
|
|
38
|
-
: []
|
|
39
|
-
|
|
40
|
-
if (collectCoverageFrom.length === 0) {
|
|
41
|
-
throw new Error("createCoverageConfig requires a collectCoverageFrom option")
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const testResultsRoot = resolveDir(resolvedRootDir, options.testResultsDir, "test-results")
|
|
45
|
-
const coverageReportDir = resolveDir(testResultsRoot, options.coverageReportSubdir, "coverage")
|
|
46
|
-
const coverageFileName = options.coverageFileName ?? "v8-coverage.json"
|
|
47
|
-
const disabledEnvVar = options.disabledEnvVar ?? "RB_DISABLE_COVERAGE"
|
|
48
|
-
const coverageEnabled = process.env[disabledEnvVar] !== "1"
|
|
49
|
-
|
|
50
|
-
const { global: thresholds, targets: thresholdTargets } = normalizeThresholdOptions(options.thresholds)
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
rootDir: resolvedRootDir,
|
|
54
|
-
collectCoverageFrom,
|
|
55
|
-
testResultsRoot,
|
|
56
|
-
coverageReportDir,
|
|
57
|
-
coverageFileName,
|
|
58
|
-
thresholds,
|
|
59
|
-
thresholdTargets,
|
|
60
|
-
coverageEnabled,
|
|
61
|
-
disabledEnvVar,
|
|
62
|
-
includeAllFiles,
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function normalizeThresholdOptions(rawThresholds) {
|
|
67
|
-
const globalThresholds = { ...DEFAULT_THRESHOLDS }
|
|
68
|
-
const targets = []
|
|
69
|
-
|
|
70
|
-
if (!isPlainObject(rawThresholds)) {
|
|
71
|
-
return { global: globalThresholds, targets }
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
for (const key of THRESHOLD_KEYS) {
|
|
75
|
-
const value = rawThresholds[key]
|
|
76
|
-
if (isThresholdValue(value)) {
|
|
77
|
-
globalThresholds[key] = value
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (Object.prototype.hasOwnProperty.call(rawThresholds, "global")) {
|
|
82
|
-
if (!isPlainObject(rawThresholds.global)) {
|
|
83
|
-
throw new Error("coverage thresholds: the `global` override must be an object of metric values")
|
|
84
|
-
}
|
|
85
|
-
Object.assign(globalThresholds, pickThresholdOverrides(rawThresholds.global))
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
for (const [pattern, overrides] of Object.entries(rawThresholds)) {
|
|
89
|
-
if (pattern === "global" || THRESHOLD_KEYS.includes(pattern)) {
|
|
90
|
-
continue
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (!isPlainObject(overrides)) {
|
|
94
|
-
throw new Error(
|
|
95
|
-
`coverage thresholds: override for "${pattern}" must be an object containing coverage metrics`,
|
|
96
|
-
)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
targets.push({
|
|
100
|
-
id: pattern,
|
|
101
|
-
pattern,
|
|
102
|
-
thresholds: {
|
|
103
|
-
...globalThresholds,
|
|
104
|
-
...pickThresholdOverrides(overrides),
|
|
105
|
-
},
|
|
106
|
-
})
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
return { global: globalThresholds, targets }
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function pickThresholdOverrides(source) {
|
|
113
|
-
const overrides = {}
|
|
114
|
-
if (!isPlainObject(source)) {
|
|
115
|
-
return overrides
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
for (const key of THRESHOLD_KEYS) {
|
|
119
|
-
const value = source[key]
|
|
120
|
-
if (isThresholdValue(value)) {
|
|
121
|
-
overrides[key] = value
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
return overrides
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function isPlainObject(value) {
|
|
129
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function isThresholdValue(value) {
|
|
133
|
-
return typeof value === "number" && Number.isFinite(value)
|
|
134
|
-
}
|
package/src/coverage/files.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises"
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export async function findCoverageFiles(config, root = config.testResultsRoot) {
|
|
6
|
-
const files = []
|
|
7
|
-
|
|
8
|
-
async function walk(current) {
|
|
9
|
-
const entries = await fs.readdir(current, { withFileTypes: true })
|
|
10
|
-
await Promise.all(
|
|
11
|
-
entries.map(async (entry) => {
|
|
12
|
-
const entryPath = path.join(current, entry.name)
|
|
13
|
-
if (entry.isDirectory()) {
|
|
14
|
-
await walk(entryPath)
|
|
15
|
-
} else if (entry.isFile() && entry.name === config.coverageFileName) {
|
|
16
|
-
files.push(entryPath)
|
|
17
|
-
}
|
|
18
|
-
}),
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
const stats = await fs.stat(root)
|
|
24
|
-
if (!stats.isDirectory()) {
|
|
25
|
-
return []
|
|
26
|
-
}
|
|
27
|
-
} catch {
|
|
28
|
-
return []
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
await walk(root)
|
|
32
|
-
return files.sort()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export async function removeCoverageFiles(config, root = config.testResultsRoot) {
|
|
36
|
-
async function walk(current) {
|
|
37
|
-
const entries = await fs.readdir(current, { withFileTypes: true })
|
|
38
|
-
await Promise.all(
|
|
39
|
-
entries.map(async (entry) => {
|
|
40
|
-
const entryPath = path.join(current, entry.name)
|
|
41
|
-
if (entry.isDirectory()) {
|
|
42
|
-
await walk(entryPath)
|
|
43
|
-
} else if (entry.isFile() && entry.name === config.coverageFileName) {
|
|
44
|
-
await fs.rm(entryPath, { force: true })
|
|
45
|
-
}
|
|
46
|
-
}),
|
|
47
|
-
)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
await walk(root)
|
|
52
|
-
} catch {
|
|
53
|
-
// ignore cleanup errors
|
|
54
|
-
}
|
|
55
|
-
}
|
package/src/coverage/fixtures.js
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
import { createCoverageTracker } from "./v8-tracker.js"
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function noopTracker() {
|
|
5
|
-
return {
|
|
6
|
-
async stop() {
|
|
7
|
-
// no-op
|
|
8
|
-
},
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function createCoverageFixtures(baseTest, config) {
|
|
13
|
-
return baseTest.extend({
|
|
14
|
-
page: async ({ page }, use, testInfo) => {
|
|
15
|
-
let tracker = noopTracker()
|
|
16
|
-
|
|
17
|
-
if (config.coverageEnabled) {
|
|
18
|
-
try {
|
|
19
|
-
tracker = await createCoverageTracker(page, config)
|
|
20
|
-
} catch (error) {
|
|
21
|
-
console.warn("[coverage] failed to initialize V8 coverage:", error)
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
27
|
-
await use(page)
|
|
28
|
-
} finally {
|
|
29
|
-
try {
|
|
30
|
-
await tracker.stop(testInfo)
|
|
31
|
-
} catch (error) {
|
|
32
|
-
console.warn("[coverage] failed to record V8 coverage:", error)
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
},
|
|
36
|
-
})
|
|
37
|
-
}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises"
|
|
2
|
-
|
|
3
|
-
import { removeCoverageFiles } from "./files.js"
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
export function createCoverageGlobalSetup(config) {
|
|
7
|
-
return async function globalSetup() {
|
|
8
|
-
if (process.env.RB_TEST_COMBINED_COVERAGE === "1") {
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
if (!config.coverageEnabled) {
|
|
13
|
-
return
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
await removeCoverageFiles(config)
|
|
17
|
-
await fs.rm(config.coverageReportDir, { recursive: true, force: true })
|
|
18
|
-
}
|
|
19
|
-
}
|
package/src/coverage/index.js
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { fileURLToPath } from "node:url"
|
|
2
|
-
|
|
3
|
-
import { createCoverageConfig } from "./config.js"
|
|
4
|
-
import { createCoverageGlobalSetup } from "./global-setup.js"
|
|
5
|
-
import { createCoverageFixtures } from "./fixtures.js"
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
export function createCoverageHarness(options = {}) {
|
|
9
|
-
const config = createCoverageConfig(options)
|
|
10
|
-
const globalSetup = createCoverageGlobalSetup(config)
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
config,
|
|
14
|
-
globalSetup,
|
|
15
|
-
extendTest(baseTest) {
|
|
16
|
-
return createCoverageFixtures(baseTest, config)
|
|
17
|
-
},
|
|
18
|
-
reporterEntry() {
|
|
19
|
-
const reporterPath = fileURLToPath(new URL("./reporter.js", import.meta.url))
|
|
20
|
-
return [reporterPath, { coverageConfig: config }]
|
|
21
|
-
},
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export { CoverageReporter } from "./reporter.js"
|
|
26
|
-
export { createCoverageConfig } from "./config.js"
|
|
27
|
-
export { generateCoverageReport } from "./report.js"
|
|
28
|
-
export { createCoverageFixtures } from "./fixtures.js"
|
|
29
|
-
export { createCoverageGlobalSetup } from "./global-setup.js"
|
|
30
|
-
export { findCoverageFiles, removeCoverageFiles } from "./files.js"
|
package/src/coverage/reporter.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs/promises"
|
|
2
|
-
|
|
3
|
-
import { CoverageThresholdError, generateCoverageReport } from "./report.js"
|
|
4
|
-
import { removeCoverageFiles } from "./files.js"
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class CoverageReporter {
|
|
8
|
-
constructor(options = {}) {
|
|
9
|
-
this.config = options.coverageConfig
|
|
10
|
-
if (!this.config) {
|
|
11
|
-
throw new Error("CoverageReporter requires a coverageConfig option")
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
async onBegin() {
|
|
16
|
-
if (process.env.RB_TEST_COMBINED_COVERAGE === "1") {
|
|
17
|
-
return
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!this.config.coverageEnabled) {
|
|
21
|
-
return
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
await removeCoverageFiles(this.config)
|
|
25
|
-
await fs.rm(this.config.coverageReportDir, { recursive: true, force: true })
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
async onEnd(result) {
|
|
29
|
-
if (process.env.RB_TEST_COMBINED_COVERAGE === "1") {
|
|
30
|
-
return
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
if (!this.config.coverageEnabled) {
|
|
34
|
-
return
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
try {
|
|
38
|
-
await generateCoverageReport(this.config)
|
|
39
|
-
} catch (error) {
|
|
40
|
-
if (error instanceof CoverageThresholdError) {
|
|
41
|
-
console.error(error.message)
|
|
42
|
-
setFailureExitCode(result)
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
throw error
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export { CoverageReporter }
|
|
52
|
-
export default CoverageReporter
|
|
53
|
-
|
|
54
|
-
function setFailureExitCode(result) {
|
|
55
|
-
if (result && typeof result === "object") {
|
|
56
|
-
result.status = "failed"
|
|
57
|
-
if (typeof result.exitCode !== "number" || result.exitCode === 0) {
|
|
58
|
-
result.exitCode = 1
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (!process.exitCode || process.exitCode === 0) {
|
|
63
|
-
process.exitCode = 1
|
|
64
|
-
}
|
|
65
|
-
}
|