@elench/testkit 0.1.55 → 0.1.57
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 +81 -0
- package/lib/bundler/index.mjs +1 -1
- package/lib/bundler/index.test.mjs +29 -0
- package/lib/cli/args.mjs +2 -2
- package/lib/cli/args.test.mjs +8 -2
- package/lib/cli/command-helpers.mjs +5 -1
- package/lib/cli/commands/discover.mjs +80 -0
- package/lib/cli/commands/run.mjs +2 -2
- package/lib/cli/entrypoint.mjs +3 -1
- package/lib/cli/presentation/colors.mjs +32 -0
- package/lib/cli/presentation/discovery-reporter.mjs +166 -0
- package/lib/cli/viewer.mjs +30 -0
- package/lib/config/discovery.mjs +107 -45
- package/lib/config/discovery.test.mjs +83 -1
- package/lib/config/index.mjs +21 -3
- package/lib/discovery/index.d.ts +121 -0
- package/lib/discovery/index.mjs +540 -0
- package/lib/discovery/index.test.mjs +182 -0
- package/lib/history/index.d.ts +46 -0
- package/lib/history/index.mjs +166 -0
- package/lib/history/index.test.mjs +115 -0
- package/lib/index.d.ts +58 -0
- package/lib/index.mjs +3 -0
- package/lib/package.test.mjs +5 -0
- package/lib/runner/default-runtime-runner.mjs +4 -1
- package/lib/runner/orchestrator.mjs +21 -8
- package/lib/runner/planning.mjs +1 -1
- package/lib/runner/reporting.mjs +6 -0
- package/lib/runner/reporting.test.mjs +5 -0
- package/lib/runner/suite-selection.mjs +4 -4
- package/lib/runner/suite-selection.test.mjs +9 -2
- package/lib/runner/worker-loop.mjs +1 -1
- package/lib/runtime-src/k6/checks.js +9 -0
- package/lib/runtime-src/k6/scenario-runtime.js +234 -0
- package/lib/runtime-src/k6/scenario-suite.js +179 -0
- package/package.json +5 -1
package/lib/config/discovery.mjs
CHANGED
|
@@ -5,6 +5,7 @@ const TESTKIT_DIRNAME = "__testkit__";
|
|
|
5
5
|
const DISCOVERY_RULES = [
|
|
6
6
|
{ suffix: ".int.testkit.ts", type: "integration", framework: "k6" },
|
|
7
7
|
{ suffix: ".e2e.testkit.ts", type: "e2e", framework: "k6" },
|
|
8
|
+
{ suffix: ".scenario.testkit.ts", type: "scenario", framework: "k6" },
|
|
8
9
|
{ suffix: ".dal.testkit.ts", type: "dal", framework: "k6" },
|
|
9
10
|
{ suffix: ".load.testkit.ts", type: "load", framework: "k6" },
|
|
10
11
|
{ suffix: ".pw.testkit.ts", type: "e2e", framework: "playwright" },
|
|
@@ -28,13 +29,13 @@ const IGNORED_DIRS = new Set([
|
|
|
28
29
|
"test-results",
|
|
29
30
|
]);
|
|
30
31
|
|
|
31
|
-
export function discoverProject(productDir, explicitServices = {}) {
|
|
32
|
+
export function discoverProject(productDir, explicitServices = {}, options = {}) {
|
|
33
|
+
const strict = options.strict !== false;
|
|
32
34
|
const { suiteFiles, legacyFiles } = discoverFiles(productDir);
|
|
33
35
|
const groupedByService = {};
|
|
34
36
|
const services = {};
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
const discoveredSuites = [];
|
|
37
|
+
const diagnostics = buildLegacyFileDiagnostics(legacyFiles);
|
|
38
|
+
const discoveredFiles = [];
|
|
38
39
|
|
|
39
40
|
for (const filePath of suiteFiles) {
|
|
40
41
|
const rule = inferRule(filePath);
|
|
@@ -42,12 +43,20 @@ export function discoverProject(productDir, explicitServices = {}) {
|
|
|
42
43
|
|
|
43
44
|
const owners = inferOwners(filePath, explicitServices);
|
|
44
45
|
if (owners.length === 0) {
|
|
45
|
-
|
|
46
|
+
diagnostics.push({
|
|
47
|
+
code: "unowned_test",
|
|
48
|
+
severity: "error",
|
|
49
|
+
message: `Unowned test file: ${filePath}`,
|
|
50
|
+
path: filePath,
|
|
51
|
+
});
|
|
46
52
|
continue;
|
|
47
53
|
}
|
|
48
54
|
if (owners.length > 1) {
|
|
49
|
-
|
|
50
|
-
|
|
55
|
+
diagnostics.push({
|
|
56
|
+
code: "ambiguous_test",
|
|
57
|
+
severity: "error",
|
|
58
|
+
message: `Ambiguous test file: ${filePath} -> ${owners.map((owner) => owner.name).sort((left, right) => left.localeCompare(right)).join(", ")}`,
|
|
59
|
+
path: filePath,
|
|
51
60
|
serviceNames: owners.map((owner) => owner.name).sort((left, right) => left.localeCompare(right)),
|
|
52
61
|
});
|
|
53
62
|
continue;
|
|
@@ -57,7 +66,7 @@ export function discoverProject(productDir, explicitServices = {}) {
|
|
|
57
66
|
services[owner.name] = mergeServiceDiscovery(services[owner.name], owner);
|
|
58
67
|
const relativeToService = relativeToServiceRoot(owner, filePath);
|
|
59
68
|
const suiteRef = deriveSuiteRef(relativeToService, rule.suffix);
|
|
60
|
-
|
|
69
|
+
discoveredFiles.push({
|
|
61
70
|
serviceName: owner.name,
|
|
62
71
|
type: rule.type,
|
|
63
72
|
framework: rule.framework,
|
|
@@ -66,11 +75,11 @@ export function discoverProject(productDir, explicitServices = {}) {
|
|
|
66
75
|
});
|
|
67
76
|
}
|
|
68
77
|
|
|
69
|
-
if (
|
|
70
|
-
throw
|
|
78
|
+
if (strict && hasDiscoveryErrors(diagnostics)) {
|
|
79
|
+
throw buildDiscoveryErrorFromDiagnostics(diagnostics);
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
for (const entry of
|
|
82
|
+
for (const entry of discoveredFiles) {
|
|
74
83
|
const grouped = groupedByService[entry.serviceName] || {};
|
|
75
84
|
const suitesForType = grouped[entry.type] || [];
|
|
76
85
|
const suiteKey = entry.suitePath.join("/");
|
|
@@ -94,12 +103,23 @@ export function discoverProject(productDir, explicitServices = {}) {
|
|
|
94
103
|
suite.files.push(entry.filePath);
|
|
95
104
|
}
|
|
96
105
|
|
|
106
|
+
const fileEntries = [];
|
|
97
107
|
for (const grouped of Object.values(groupedByService)) {
|
|
98
108
|
for (const suites of Object.values(grouped)) {
|
|
99
109
|
const suiteNames = disambiguateSuiteNames(suites);
|
|
100
110
|
for (const suite of suites) {
|
|
101
111
|
suite.name = suiteNames.get(suite._suiteKey);
|
|
102
112
|
suite.files.sort((left, right) => left.localeCompare(right));
|
|
113
|
+
for (const filePath of suite.files) {
|
|
114
|
+
fileEntries.push({
|
|
115
|
+
serviceName: findSuiteServiceName(groupedByService, grouped, suite),
|
|
116
|
+
type: findSuiteType(grouped, suites),
|
|
117
|
+
framework: suite.framework,
|
|
118
|
+
suiteName: suite.name,
|
|
119
|
+
suitePath: [...suite._suitePath],
|
|
120
|
+
filePath,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
103
123
|
delete suite._suiteKey;
|
|
104
124
|
delete suite._suitePath;
|
|
105
125
|
}
|
|
@@ -110,11 +130,59 @@ export function discoverProject(productDir, explicitServices = {}) {
|
|
|
110
130
|
return {
|
|
111
131
|
services,
|
|
112
132
|
suitesByService: groupedByService,
|
|
133
|
+
files: fileEntries.sort(
|
|
134
|
+
(left, right) =>
|
|
135
|
+
left.serviceName.localeCompare(right.serviceName) ||
|
|
136
|
+
left.type.localeCompare(right.type) ||
|
|
137
|
+
left.suiteName.localeCompare(right.suiteName) ||
|
|
138
|
+
left.filePath.localeCompare(right.filePath)
|
|
139
|
+
),
|
|
140
|
+
diagnostics,
|
|
113
141
|
};
|
|
114
142
|
}
|
|
115
143
|
|
|
116
|
-
export function discoverSuites(productDir, explicitServices = {}) {
|
|
117
|
-
return discoverProject(productDir, explicitServices).suitesByService;
|
|
144
|
+
export function discoverSuites(productDir, explicitServices = {}, options = {}) {
|
|
145
|
+
return discoverProject(productDir, explicitServices, options).suitesByService;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function hasDiscoveryErrors(diagnostics = []) {
|
|
149
|
+
return diagnostics.some((entry) => entry?.severity === "error");
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export function buildDiscoveryErrorFromDiagnostics(diagnostics = []) {
|
|
153
|
+
const lines = ["Filesystem discovery failed for one or more .testkit.ts files."];
|
|
154
|
+
|
|
155
|
+
const legacyFiles = diagnostics.filter((entry) => entry.code === "legacy_path").map((entry) => entry.path);
|
|
156
|
+
const unownedFiles = diagnostics.filter((entry) => entry.code === "unowned_test").map((entry) => entry.path);
|
|
157
|
+
const ambiguousFiles = diagnostics.filter((entry) => entry.code === "ambiguous_test");
|
|
158
|
+
|
|
159
|
+
if (legacyFiles.length > 0) {
|
|
160
|
+
lines.push("");
|
|
161
|
+
lines.push("Legacy test files outside __testkit__:");
|
|
162
|
+
for (const filePath of legacyFiles) {
|
|
163
|
+
lines.push(`- ${filePath}`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (unownedFiles.length > 0) {
|
|
168
|
+
lines.push("");
|
|
169
|
+
lines.push("Unowned test files:");
|
|
170
|
+
for (const filePath of unownedFiles) {
|
|
171
|
+
lines.push(`- ${filePath}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (ambiguousFiles.length > 0) {
|
|
176
|
+
lines.push("");
|
|
177
|
+
lines.push("Ambiguous test files:");
|
|
178
|
+
for (const entry of ambiguousFiles) {
|
|
179
|
+
lines.push(`- ${entry.path} -> ${entry.serviceNames.join(", ")}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
lines.push("");
|
|
184
|
+
lines.push('Expected test files to live under a "__testkit__" directory within a service root.');
|
|
185
|
+
return new Error(lines.join("\n"));
|
|
118
186
|
}
|
|
119
187
|
|
|
120
188
|
function discoverFiles(productDir) {
|
|
@@ -305,38 +373,6 @@ function mergeServiceDiscovery(existing, owner) {
|
|
|
305
373
|
};
|
|
306
374
|
}
|
|
307
375
|
|
|
308
|
-
function buildDiscoveryError(legacyFiles, unowned, ambiguous) {
|
|
309
|
-
const lines = ["Filesystem discovery failed for one or more .testkit.ts files."];
|
|
310
|
-
|
|
311
|
-
if (legacyFiles.length > 0) {
|
|
312
|
-
lines.push("");
|
|
313
|
-
lines.push("Legacy test files outside __testkit__:");
|
|
314
|
-
for (const filePath of legacyFiles) {
|
|
315
|
-
lines.push(`- ${filePath}`);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
if (unowned.length > 0) {
|
|
320
|
-
lines.push("");
|
|
321
|
-
lines.push("Unowned test files:");
|
|
322
|
-
for (const filePath of unowned) {
|
|
323
|
-
lines.push(`- ${filePath}`);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
if (ambiguous.length > 0) {
|
|
328
|
-
lines.push("");
|
|
329
|
-
lines.push("Ambiguous test files:");
|
|
330
|
-
for (const entry of ambiguous) {
|
|
331
|
-
lines.push(`- ${entry.filePath} -> ${entry.serviceNames.join(", ")}`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
lines.push("");
|
|
336
|
-
lines.push('Expected test files to live under a "__testkit__" directory within a service root.');
|
|
337
|
-
return new Error(lines.join("\n"));
|
|
338
|
-
}
|
|
339
|
-
|
|
340
376
|
function inferRule(filePath) {
|
|
341
377
|
return DISCOVERY_RULES.find((rule) => filePath.endsWith(rule.suffix)) || null;
|
|
342
378
|
}
|
|
@@ -350,3 +386,29 @@ function normalizePath(value) {
|
|
|
350
386
|
if (normalized === "." || normalized === "./") return ".";
|
|
351
387
|
return normalized.replace(/^\.\/+/, "").replace(/\/+$/, "") || ".";
|
|
352
388
|
}
|
|
389
|
+
|
|
390
|
+
function buildLegacyFileDiagnostics(legacyFiles) {
|
|
391
|
+
return legacyFiles.map((filePath) => ({
|
|
392
|
+
code: "legacy_path",
|
|
393
|
+
severity: "error",
|
|
394
|
+
message: `Legacy test file outside __testkit__: ${filePath}`,
|
|
395
|
+
path: filePath,
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function findSuiteServiceName(groupedByService, grouped, suite) {
|
|
400
|
+
for (const [serviceName, candidate] of Object.entries(groupedByService)) {
|
|
401
|
+
if (candidate !== grouped) continue;
|
|
402
|
+
for (const suites of Object.values(candidate)) {
|
|
403
|
+
if (suites.includes(suite)) return serviceName;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
return "app";
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function findSuiteType(grouped, targetSuites) {
|
|
410
|
+
for (const [type, suites] of Object.entries(grouped)) {
|
|
411
|
+
if (suites === targetSuites) return type;
|
|
412
|
+
}
|
|
413
|
+
return "integration";
|
|
414
|
+
}
|
|
@@ -19,9 +19,10 @@ describe("filesystem-discovery", () => {
|
|
|
19
19
|
|
|
20
20
|
writeFile(productDir, "src/api/routes/__testkit__/auth/me.int.testkit.ts");
|
|
21
21
|
writeFile(productDir, "src/api/routes/__testkit__/health/ready.int.testkit.ts");
|
|
22
|
+
writeFile(productDir, "src/api/routes/__testkit__/journeys/smoke.scenario.testkit.ts");
|
|
22
23
|
writeFile(productDir, "frontend/app/__testkit__/homepage/homepage.pw.testkit.ts");
|
|
23
24
|
|
|
24
|
-
const
|
|
25
|
+
const project = discoverProject(productDir, {
|
|
25
26
|
api: {
|
|
26
27
|
local: {
|
|
27
28
|
cwd: ".",
|
|
@@ -34,6 +35,7 @@ describe("filesystem-discovery", () => {
|
|
|
34
35
|
},
|
|
35
36
|
});
|
|
36
37
|
|
|
38
|
+
const suites = project.suitesByService;
|
|
37
39
|
expect(suites.api.integration).toEqual([
|
|
38
40
|
{
|
|
39
41
|
name: "auth",
|
|
@@ -46,6 +48,13 @@ describe("filesystem-discovery", () => {
|
|
|
46
48
|
framework: "k6",
|
|
47
49
|
},
|
|
48
50
|
]);
|
|
51
|
+
expect(suites.api.scenario).toEqual([
|
|
52
|
+
{
|
|
53
|
+
name: "journeys",
|
|
54
|
+
files: ["src/api/routes/__testkit__/journeys/smoke.scenario.testkit.ts"],
|
|
55
|
+
framework: "k6",
|
|
56
|
+
},
|
|
57
|
+
]);
|
|
49
58
|
expect(suites.frontend.e2e).toEqual([
|
|
50
59
|
{
|
|
51
60
|
name: "homepage",
|
|
@@ -53,6 +62,41 @@ describe("filesystem-discovery", () => {
|
|
|
53
62
|
framework: "playwright",
|
|
54
63
|
},
|
|
55
64
|
]);
|
|
65
|
+
expect(project.files).toEqual([
|
|
66
|
+
{
|
|
67
|
+
serviceName: "api",
|
|
68
|
+
type: "integration",
|
|
69
|
+
framework: "k6",
|
|
70
|
+
suiteName: "auth",
|
|
71
|
+
suitePath: ["src", "api", "routes", "auth"],
|
|
72
|
+
filePath: "src/api/routes/__testkit__/auth/me.int.testkit.ts",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
serviceName: "api",
|
|
76
|
+
type: "integration",
|
|
77
|
+
framework: "k6",
|
|
78
|
+
suiteName: "health",
|
|
79
|
+
suitePath: ["src", "api", "routes", "health"],
|
|
80
|
+
filePath: "src/api/routes/__testkit__/health/ready.int.testkit.ts",
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
serviceName: "api",
|
|
84
|
+
type: "scenario",
|
|
85
|
+
framework: "k6",
|
|
86
|
+
suiteName: "journeys",
|
|
87
|
+
suitePath: ["src", "api", "routes", "journeys"],
|
|
88
|
+
filePath: "src/api/routes/__testkit__/journeys/smoke.scenario.testkit.ts",
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
serviceName: "frontend",
|
|
92
|
+
type: "e2e",
|
|
93
|
+
framework: "playwright",
|
|
94
|
+
suiteName: "homepage",
|
|
95
|
+
suitePath: ["app", "homepage"],
|
|
96
|
+
filePath: "frontend/app/__testkit__/homepage/homepage.pw.testkit.ts",
|
|
97
|
+
},
|
|
98
|
+
]);
|
|
99
|
+
expect(project.diagnostics).toEqual([]);
|
|
56
100
|
});
|
|
57
101
|
|
|
58
102
|
it("infers the suite from the directory that owns __testkit__", () => {
|
|
@@ -122,6 +166,44 @@ describe("filesystem-discovery", () => {
|
|
|
122
166
|
})
|
|
123
167
|
).toThrow("Legacy test files outside __testkit__");
|
|
124
168
|
});
|
|
169
|
+
|
|
170
|
+
it("reports legacy files in non-strict mode", () => {
|
|
171
|
+
const productDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-discovery-"));
|
|
172
|
+
cleanups.push(() => fs.rmSync(productDir, { recursive: true, force: true }));
|
|
173
|
+
|
|
174
|
+
writeFile(productDir, "src/api/routes/__testkit__/health/ready.int.testkit.ts");
|
|
175
|
+
writeFile(productDir, "tests/api/integration/health.int.testkit.ts");
|
|
176
|
+
|
|
177
|
+
const project = discoverProject(
|
|
178
|
+
productDir,
|
|
179
|
+
{
|
|
180
|
+
api: {
|
|
181
|
+
local: {
|
|
182
|
+
cwd: ".",
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
strict: false,
|
|
188
|
+
}
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(project.suitesByService.api.integration).toEqual([
|
|
192
|
+
{
|
|
193
|
+
name: "health",
|
|
194
|
+
files: ["src/api/routes/__testkit__/health/ready.int.testkit.ts"],
|
|
195
|
+
framework: "k6",
|
|
196
|
+
},
|
|
197
|
+
]);
|
|
198
|
+
expect(project.diagnostics).toEqual([
|
|
199
|
+
{
|
|
200
|
+
code: "legacy_path",
|
|
201
|
+
severity: "error",
|
|
202
|
+
message: "Legacy test file outside __testkit__: tests/api/integration/health.int.testkit.ts",
|
|
203
|
+
path: "tests/api/integration/health.int.testkit.ts",
|
|
204
|
+
},
|
|
205
|
+
]);
|
|
206
|
+
});
|
|
125
207
|
});
|
|
126
208
|
|
|
127
209
|
function writeFile(productDir, relativePath) {
|
package/lib/config/index.mjs
CHANGED
|
@@ -31,14 +31,15 @@ export function parseDotenv(filePath) {
|
|
|
31
31
|
return parseDotenvString(fs.readFileSync(filePath, "utf8"));
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
export async function
|
|
34
|
+
export async function loadConfigContext(opts = {}) {
|
|
35
35
|
const productDir = resolveProductDir(process.cwd(), opts.dir);
|
|
36
|
-
const
|
|
36
|
+
const setupContext = opts.setupContext || (await loadTestkitSetup(productDir));
|
|
37
|
+
const { setup, setupFile } = setupContext;
|
|
37
38
|
const execution = normalizeRepoExecution(setup.execution);
|
|
38
39
|
const reporting = normalizeReportingConfig(setup.reporting);
|
|
39
40
|
const toolchains = normalizeToolchainRegistry(setup.toolchains);
|
|
40
41
|
const explicitServices = setup.services || {};
|
|
41
|
-
const discovery = discoverProject(productDir, explicitServices);
|
|
42
|
+
const discovery = discoverProject(productDir, explicitServices, opts.discoveryOptions || {});
|
|
42
43
|
const serviceNames = new Set([
|
|
43
44
|
...Object.keys(explicitServices),
|
|
44
45
|
...Object.keys(discovery.suitesByService),
|
|
@@ -63,6 +64,23 @@ export async function loadConfigs(opts = {}) {
|
|
|
63
64
|
|
|
64
65
|
validateConfigCoverage(configs);
|
|
65
66
|
|
|
67
|
+
return {
|
|
68
|
+
productDir,
|
|
69
|
+
setup,
|
|
70
|
+
setupFile,
|
|
71
|
+
execution,
|
|
72
|
+
reporting,
|
|
73
|
+
toolchains,
|
|
74
|
+
explicitServices,
|
|
75
|
+
discovery,
|
|
76
|
+
configs,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function loadConfigs(opts = {}) {
|
|
81
|
+
const context = await loadConfigContext(opts);
|
|
82
|
+
const { configs } = context;
|
|
83
|
+
|
|
66
84
|
const filtered = opts.service
|
|
67
85
|
? configs.filter((config) => config.name === opts.service)
|
|
68
86
|
: configs;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
export type DiscoverySelectionType = "int" | "e2e" | "scenario" | "dal" | "load" | "pw";
|
|
2
|
+
export type DiscoveryInternalType = "integration" | "e2e" | "scenario" | "dal" | "load";
|
|
3
|
+
export type DiscoveryFramework = "k6" | "playwright";
|
|
4
|
+
|
|
5
|
+
export interface DiscoveryDiagnostic {
|
|
6
|
+
code: string;
|
|
7
|
+
severity: "error" | "warning";
|
|
8
|
+
message: string;
|
|
9
|
+
path?: string;
|
|
10
|
+
serviceNames?: string[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface DiscoveryService {
|
|
14
|
+
name: string;
|
|
15
|
+
discovered: boolean;
|
|
16
|
+
localCwd: string;
|
|
17
|
+
dependsOn: string[];
|
|
18
|
+
suiteCount: number;
|
|
19
|
+
fileCount: number;
|
|
20
|
+
activeFileCount: number;
|
|
21
|
+
skippedFileCount: number;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface DiscoverySuite {
|
|
25
|
+
id: string;
|
|
26
|
+
service: string;
|
|
27
|
+
name: string;
|
|
28
|
+
displayName: string;
|
|
29
|
+
selectionType: DiscoverySelectionType;
|
|
30
|
+
internalType: DiscoveryInternalType;
|
|
31
|
+
framework: DiscoveryFramework;
|
|
32
|
+
groupLabel: string;
|
|
33
|
+
fileCount: number;
|
|
34
|
+
activeFileCount: number;
|
|
35
|
+
skippedFileCount: number;
|
|
36
|
+
dependsOn: string[];
|
|
37
|
+
locks: string[];
|
|
38
|
+
filePaths: string[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface DiscoveryHistorySummary {
|
|
42
|
+
firstSeenAt?: string | null;
|
|
43
|
+
lastSeenAt?: string | null;
|
|
44
|
+
lastRunAt?: string | null;
|
|
45
|
+
runCount?: number;
|
|
46
|
+
passCount?: number;
|
|
47
|
+
failCount?: number;
|
|
48
|
+
skipCount?: number;
|
|
49
|
+
avgDurationMs?: number;
|
|
50
|
+
lastStatus?: "passed" | "failed" | "skipped" | "not_run" | null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface DiscoveryFile {
|
|
54
|
+
id: string;
|
|
55
|
+
path: string;
|
|
56
|
+
displayName: string;
|
|
57
|
+
service: string;
|
|
58
|
+
suiteName: string;
|
|
59
|
+
groupLabel: string;
|
|
60
|
+
selectionType: DiscoverySelectionType;
|
|
61
|
+
internalType: DiscoveryInternalType;
|
|
62
|
+
framework: DiscoveryFramework;
|
|
63
|
+
skipped: boolean;
|
|
64
|
+
skipReason: string | null;
|
|
65
|
+
locks: string[];
|
|
66
|
+
dependsOn: string[];
|
|
67
|
+
history?: DiscoveryHistorySummary;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface DiscoveryResult {
|
|
71
|
+
schemaVersion: number;
|
|
72
|
+
source: "testkit-discovery";
|
|
73
|
+
product: {
|
|
74
|
+
name: string;
|
|
75
|
+
directory: string;
|
|
76
|
+
};
|
|
77
|
+
setupFile: string | null;
|
|
78
|
+
filters: {
|
|
79
|
+
service: string | null;
|
|
80
|
+
types: DiscoverySelectionType[] | ["all"];
|
|
81
|
+
suiteSelectors: string[];
|
|
82
|
+
fileNames: string[];
|
|
83
|
+
runnableOnly: boolean;
|
|
84
|
+
diagnostics: "error" | "report";
|
|
85
|
+
};
|
|
86
|
+
services: DiscoveryService[];
|
|
87
|
+
suites: DiscoverySuite[];
|
|
88
|
+
files: DiscoveryFile[];
|
|
89
|
+
diagnostics: DiscoveryDiagnostic[];
|
|
90
|
+
summary: {
|
|
91
|
+
services: number;
|
|
92
|
+
suites: number;
|
|
93
|
+
files: number;
|
|
94
|
+
activeFiles: number;
|
|
95
|
+
skippedFiles: number;
|
|
96
|
+
diagnostics: {
|
|
97
|
+
errors: number;
|
|
98
|
+
warnings: number;
|
|
99
|
+
};
|
|
100
|
+
byService: Record<string, number>;
|
|
101
|
+
byType: Record<string, number>;
|
|
102
|
+
};
|
|
103
|
+
history: {
|
|
104
|
+
available: boolean;
|
|
105
|
+
path?: string;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface DiscoverTestsOptions {
|
|
110
|
+
dir?: string;
|
|
111
|
+
service?: string;
|
|
112
|
+
type?: string | string[];
|
|
113
|
+
suite?: string | string[];
|
|
114
|
+
file?: string | string[];
|
|
115
|
+
runnableOnly?: boolean;
|
|
116
|
+
diagnostics?: "error" | "report";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export declare function discoverTests(options?: DiscoverTestsOptions): Promise<DiscoveryResult>;
|
|
120
|
+
export declare function formatSelectionTypeLabel(type: DiscoverySelectionType): string;
|
|
121
|
+
export declare function formatDisplayName(value: string): string;
|