@elench/testkit 0.1.108 → 0.1.109
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 -9
- package/lib/app/doctor.mjs +5 -5
- package/lib/app/typecheck.mjs +6 -5
- package/lib/bundler/index.mjs +134 -7
- package/lib/cli/args.mjs +3 -2
- package/lib/cli/assistant/command-observer.mjs +2 -1
- package/lib/cli/assistant/command-results.mjs +2 -1
- package/lib/cli/assistant/context-pack.mjs +2 -2
- package/lib/cli/assistant/prompt-builder.mjs +2 -2
- package/lib/cli/command-flags.mjs +2 -1
- package/lib/cli/commands/cleanup.mjs +13 -2
- package/lib/cli/commands/discover.mjs +2 -1
- package/lib/cli/commands/run.mjs +3 -2
- package/lib/cli/entrypoint.mjs +3 -1
- package/lib/cli/operations/cleanup/operation.mjs +6 -1
- package/lib/cli/operations/status/operation.mjs +2 -2
- package/lib/cli/renderers/discover/report.mjs +6 -8
- package/lib/cli/renderers/run/failure.mjs +1 -1
- package/lib/cli/renderers/run/text-reporter.mjs +1 -1
- package/lib/cli/renderers/status/text.mjs +101 -1
- package/lib/config/discovery.mjs +10 -1
- package/lib/config-api/index.mjs +2 -2
- package/lib/config-api/next-runtime-tsconfig.mjs +2 -1
- package/lib/coverage/graph-builder.mjs +2 -4
- package/lib/coverage/routing.mjs +1 -1
- package/lib/coverage/shared.mjs +1 -2
- package/lib/discovery/index.d.ts +5 -8
- package/lib/discovery/index.mjs +15 -24
- package/lib/domain/test-types.mjs +44 -0
- package/lib/history/index.d.ts +3 -4
- package/lib/history/index.mjs +6 -14
- package/lib/runner/formatting.mjs +2 -3
- package/lib/runner/maintenance.mjs +136 -35
- package/lib/runner/planning.mjs +1 -1
- package/lib/runner/results.mjs +0 -6
- package/lib/runner/status-model.mjs +520 -0
- package/lib/runner/suite-selection.mjs +20 -11
- package/lib/runner/template-steps.mjs +2 -2
- package/lib/runner/template.mjs +4 -0
- package/lib/ui/index.d.ts +1 -0
- package/lib/ui/index.mjs +1 -0
- package/lib/vitest/index.mjs +2 -1
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/dist/index.js +9 -11
- package/node_modules/@elench/testkit-bridge/dist/index.js.map +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/dist/index.d.ts +1 -3
- package/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +1 -1
- package/node_modules/@elench/testkit-protocol/dist/index.js +3 -6
- package/node_modules/@elench/testkit-protocol/dist/index.js.map +1 -1
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/dist/requests.js +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +9 -9
|
@@ -1,7 +1,107 @@
|
|
|
1
1
|
export function renderStatusResult(result) {
|
|
2
|
-
return normalizeLines(result
|
|
2
|
+
if (Array.isArray(result?.lines)) return normalizeLines(result.lines);
|
|
3
|
+
if (!result) return [];
|
|
4
|
+
|
|
5
|
+
const lines = [
|
|
6
|
+
`Testkit Status: ${result.name || result.product?.selectedService || "service"}`,
|
|
7
|
+
"",
|
|
8
|
+
"Product",
|
|
9
|
+
` dir: ${result.product?.dir || "unknown"}`,
|
|
10
|
+
` config: ${result.product?.configFile || "none"}`,
|
|
11
|
+
` services: ${formatList(result.product?.services)}`,
|
|
12
|
+
` dependencies: ${formatList(result.product?.dependencies)}`,
|
|
13
|
+
"",
|
|
14
|
+
"Runs",
|
|
15
|
+
` active: ${result.runs?.active || 0}`,
|
|
16
|
+
` stale: ${result.runs?.stale || 0}`,
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const run of (result.runs?.runs || []).slice(0, 5)) {
|
|
20
|
+
const ports = run.ports?.length ? ` ports=${run.ports.join(",")}` : "";
|
|
21
|
+
lines.push(` ${run.runId}: ${run.status} pid=${run.pid}${ports}`);
|
|
22
|
+
}
|
|
23
|
+
if ((result.runs?.runs || []).length > 5) {
|
|
24
|
+
lines.push(` ... ${(result.runs.runs.length - 5)} more run${result.runs.runs.length - 5 === 1 ? "" : "s"}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
lines.push("", "Runtime Graphs");
|
|
28
|
+
if ((result.runtimeGraphs || []).length === 0) {
|
|
29
|
+
lines.push(" none");
|
|
30
|
+
} else {
|
|
31
|
+
for (const graph of result.runtimeGraphs || []) {
|
|
32
|
+
const marker = graph.orphan ? " orphan" : "";
|
|
33
|
+
lines.push(` ${graph.name}${marker}`);
|
|
34
|
+
lines.push(` desired instances: ${graph.desired?.instanceCount ?? "none"}`);
|
|
35
|
+
lines.push(` runtime dirs: ${graph.runtimeDirCount || 0}`);
|
|
36
|
+
lines.push(` current: ${formatRuntimeRange(graph.currentRuntimeDirs?.map((runtime) => runtime.id))}`);
|
|
37
|
+
lines.push(` stale: ${formatRuntimeRange(graph.staleRuntimeDirs?.map((runtime) => runtime.id))}`);
|
|
38
|
+
lines.push(` runtime services: ${formatList(graph.desired?.runtimeServices || graph.actual?.runtimeServices)}`);
|
|
39
|
+
lines.push(` target services: ${formatList(graph.desired?.targetServices || graph.actual?.targetServices)}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
lines.push(
|
|
44
|
+
"",
|
|
45
|
+
"Caches",
|
|
46
|
+
" bundles",
|
|
47
|
+
` size: ${formatBytes(result.caches?.bundles?.sizeBytes || 0)}`,
|
|
48
|
+
` files: ${result.caches?.bundles?.fileCount || 0}`,
|
|
49
|
+
` source files: ${result.caches?.bundles?.sourceFileCount || 0}`,
|
|
50
|
+
` duplicated sources: ${result.caches?.bundles?.duplicatedSourceCount || 0}`,
|
|
51
|
+
` inline sourcemaps: ${formatSourcemaps(result.caches?.bundles)}`,
|
|
52
|
+
` manifest entries: ${result.caches?.bundles?.manifest?.entryCount || 0}`,
|
|
53
|
+
` unmanaged files: ${result.caches?.bundles?.unmanagedFileCount || 0}`,
|
|
54
|
+
" assistant command results",
|
|
55
|
+
` size: ${formatBytes(result.caches?.assistant?.sizeBytes || 0)}`,
|
|
56
|
+
` files: ${result.caches?.assistant?.fileCount || 0}`,
|
|
57
|
+
` large files: ${result.caches?.assistant?.largeFileCount || 0}`
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
if (result.warnings?.length) {
|
|
61
|
+
lines.push("", "Warnings");
|
|
62
|
+
for (const warning of result.warnings) lines.push(` - ${warning}`);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!result.hasState) {
|
|
66
|
+
lines.push("", "No state — run tests first.");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return lines;
|
|
3
70
|
}
|
|
4
71
|
|
|
5
72
|
function normalizeLines(lines) {
|
|
6
73
|
return Array.isArray(lines) ? lines : [];
|
|
7
74
|
}
|
|
75
|
+
|
|
76
|
+
function formatList(values) {
|
|
77
|
+
const list = Array.isArray(values) ? values.filter(Boolean) : [];
|
|
78
|
+
return list.length > 0 ? list.join(", ") : "none";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function formatRuntimeRange(ids) {
|
|
82
|
+
const values = Array.isArray(ids) ? ids : [];
|
|
83
|
+
if (values.length === 0) return "none";
|
|
84
|
+
if (values.length === 1) return values[0];
|
|
85
|
+
return `${values[0]}..${values.at(-1)}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function formatBytes(value) {
|
|
89
|
+
const bytes = Number(value || 0);
|
|
90
|
+
if (bytes < 1024) return `${bytes}B`;
|
|
91
|
+
const units = ["KB", "MB", "GB", "TB"];
|
|
92
|
+
let amount = bytes / 1024;
|
|
93
|
+
let unitIndex = 0;
|
|
94
|
+
while (amount >= 1024 && unitIndex < units.length - 1) {
|
|
95
|
+
amount /= 1024;
|
|
96
|
+
unitIndex += 1;
|
|
97
|
+
}
|
|
98
|
+
return `${amount >= 10 ? amount.toFixed(0) : amount.toFixed(1)}${units[unitIndex]}`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function formatSourcemaps(bundles = {}) {
|
|
102
|
+
if (bundles.sourcemapFileCount == null) return bundles.sourcemapStatus || "unknown";
|
|
103
|
+
if (bundles.sourcemapStatus && bundles.sourcemapStatus !== "present" && bundles.sourcemapStatus !== "none") {
|
|
104
|
+
return `${bundles.sourcemapFileCount} (${bundles.sourcemapStatus})`;
|
|
105
|
+
}
|
|
106
|
+
return String(bundles.sourcemapFileCount || 0);
|
|
107
|
+
}
|
package/lib/config/discovery.mjs
CHANGED
|
@@ -15,7 +15,8 @@ const DISCOVERY_RULES = [
|
|
|
15
15
|
{ suffix: ".scenario.testkit.ts", type: "scenario", framework: "k6" },
|
|
16
16
|
{ suffix: ".dal.testkit.ts", type: "dal", framework: "k6" },
|
|
17
17
|
{ suffix: ".load.testkit.ts", type: "load", framework: "k6" },
|
|
18
|
-
{ suffix: ".
|
|
18
|
+
{ suffix: ".ui.testkit.ts", type: "ui", framework: "playwright" },
|
|
19
|
+
{ suffix: ".pw.testkit.ts", type: "ui", framework: "playwright", legacySuffix: true },
|
|
19
20
|
];
|
|
20
21
|
|
|
21
22
|
export function discoverProject(productDir, explicitServices = {}, options = {}) {
|
|
@@ -30,6 +31,14 @@ export function discoverProject(productDir, explicitServices = {}, options = {})
|
|
|
30
31
|
for (const filePath of suiteFiles) {
|
|
31
32
|
const rule = inferRule(filePath);
|
|
32
33
|
if (!rule) continue;
|
|
34
|
+
if (rule.legacySuffix) {
|
|
35
|
+
diagnostics.push({
|
|
36
|
+
code: "legacy_ui_suffix",
|
|
37
|
+
severity: "warning",
|
|
38
|
+
message: `Legacy UI test suffix ".pw.testkit.ts" is deprecated. Rename to ".ui.testkit.ts": ${filePath}`,
|
|
39
|
+
path: filePath,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
33
42
|
|
|
34
43
|
const owners = inferOwners(filePath, explicitServices, repoDiscovery);
|
|
35
44
|
if (owners === null) continue;
|
package/lib/config-api/index.mjs
CHANGED
|
@@ -225,9 +225,9 @@ function nextApp(options = {}) {
|
|
|
225
225
|
readyTimeoutMs,
|
|
226
226
|
env: mode === "start"
|
|
227
227
|
? {
|
|
228
|
-
NEXT_DIST_DIR: normalizedEnv.NEXT_DIST_DIR || "
|
|
228
|
+
NEXT_DIST_DIR: normalizedEnv.NEXT_DIST_DIR || "{productDir}/.next-testkit/{runtimeId}/dist",
|
|
229
229
|
NEXT_TSCONFIG_PATH:
|
|
230
|
-
normalizedEnv.NEXT_TSCONFIG_PATH || "
|
|
230
|
+
normalizedEnv.NEXT_TSCONFIG_PATH || "{productDir}/.next-testkit/{runtimeId}/tsconfig.json",
|
|
231
231
|
...normalizedEnv,
|
|
232
232
|
}
|
|
233
233
|
: normalizedEnv,
|
|
@@ -3,12 +3,13 @@ import path from "path";
|
|
|
3
3
|
|
|
4
4
|
export async function writeNextRuntimeTsconfig(context = {}) {
|
|
5
5
|
const serviceDir = context.cwd || context.productDir;
|
|
6
|
+
const productDir = context.productDir || serviceDir;
|
|
6
7
|
const runtimeId = context.runtimeId;
|
|
7
8
|
if (!serviceDir || !runtimeId) {
|
|
8
9
|
throw new Error("writeNextRuntimeTsconfig requires cwd and runtimeId");
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
const runtimeRoot = path.join(
|
|
12
|
+
const runtimeRoot = path.join(productDir, ".next-testkit", String(runtimeId));
|
|
12
13
|
const outputPath = path.join(runtimeRoot, "tsconfig.json");
|
|
13
14
|
const outputDir = path.dirname(outputPath);
|
|
14
15
|
const relative = (target) => path.relative(outputDir, target).replaceAll(path.sep, "/");
|
|
@@ -70,8 +70,7 @@ export function buildCoverageGraph({ productDir, repoDiscovery = {}, services =
|
|
|
70
70
|
confidence: "high",
|
|
71
71
|
service: entry.serviceName,
|
|
72
72
|
suiteName: entry.suiteName,
|
|
73
|
-
|
|
74
|
-
framework: entry.framework,
|
|
73
|
+
type: toSelectionType(entry.type, entry.framework),
|
|
75
74
|
testFilePath: entry.filePath,
|
|
76
75
|
coveredNodeIds,
|
|
77
76
|
details: buildEvidenceDetails(coveredNodeIds, graph, entry, context),
|
|
@@ -173,8 +172,7 @@ function createFallbackEvidence(entry, testNodeId) {
|
|
|
173
172
|
confidence: "medium",
|
|
174
173
|
service: entry.serviceName,
|
|
175
174
|
suiteName: entry.suiteName,
|
|
176
|
-
|
|
177
|
-
framework: entry.framework,
|
|
175
|
+
type: toSelectionType(entry.type, entry.framework),
|
|
178
176
|
testFilePath: entry.filePath,
|
|
179
177
|
coveredNodeIds: [testNodeId],
|
|
180
178
|
};
|
package/lib/coverage/routing.mjs
CHANGED
|
@@ -53,7 +53,7 @@ export function apiRouteLookupKey(method, route) {
|
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export function toSelectionType(type, framework) {
|
|
56
|
-
if (framework === "playwright") return "
|
|
56
|
+
if (framework === "playwright" || type === "ui") return "ui";
|
|
57
57
|
if (type === "integration") return "int";
|
|
58
58
|
return type;
|
|
59
59
|
}
|
package/lib/coverage/shared.mjs
CHANGED
|
@@ -67,7 +67,6 @@ export function createTestFileNode(graph, entry) {
|
|
|
67
67
|
filePath: entry.filePath,
|
|
68
68
|
metadata: {
|
|
69
69
|
suiteName: entry.suiteName,
|
|
70
|
-
framework: entry.framework,
|
|
71
70
|
type: toSelectionType(entry.type, entry.framework),
|
|
72
71
|
},
|
|
73
72
|
});
|
|
@@ -80,7 +79,7 @@ export function apiRouteLookupKey(method, route) {
|
|
|
80
79
|
}
|
|
81
80
|
|
|
82
81
|
export function toSelectionType(type, framework) {
|
|
83
|
-
if (framework === "playwright") return "
|
|
82
|
+
if (framework === "playwright" || type === "ui") return "ui";
|
|
84
83
|
if (type === "integration") return "int";
|
|
85
84
|
return type;
|
|
86
85
|
}
|
package/lib/discovery/index.d.ts
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import type { CoverageGraph } from "@elench/testkit-protocol";
|
|
2
2
|
|
|
3
|
-
export type
|
|
4
|
-
export type DiscoveryInternalType = "integration" | "e2e" | "scenario" | "dal" | "load";
|
|
5
|
-
export type DiscoveryFramework = "k6" | "playwright";
|
|
3
|
+
export type DiscoveryTestType = "ui" | "e2e" | "scenario" | "int" | "dal" | "load";
|
|
4
|
+
export type DiscoveryInternalType = "ui" | "integration" | "e2e" | "scenario" | "dal" | "load";
|
|
6
5
|
|
|
7
6
|
export interface DiscoveryDiagnostic {
|
|
8
7
|
code: string;
|
|
@@ -28,9 +27,8 @@ export interface DiscoverySuite {
|
|
|
28
27
|
service: string;
|
|
29
28
|
name: string;
|
|
30
29
|
displayName: string;
|
|
31
|
-
|
|
30
|
+
type: DiscoveryTestType;
|
|
32
31
|
internalType: DiscoveryInternalType;
|
|
33
|
-
framework: DiscoveryFramework;
|
|
34
32
|
groupLabel: string;
|
|
35
33
|
fileCount: number;
|
|
36
34
|
activeFileCount: number;
|
|
@@ -59,9 +57,8 @@ export interface DiscoveryFile {
|
|
|
59
57
|
service: string;
|
|
60
58
|
suiteName: string;
|
|
61
59
|
groupLabel: string;
|
|
62
|
-
|
|
60
|
+
type: DiscoveryTestType;
|
|
63
61
|
internalType: DiscoveryInternalType;
|
|
64
|
-
framework: DiscoveryFramework;
|
|
65
62
|
skipped: boolean;
|
|
66
63
|
skipReason: string | null;
|
|
67
64
|
locks: string[];
|
|
@@ -79,7 +76,7 @@ export interface DiscoveryResult {
|
|
|
79
76
|
configFile: string | null;
|
|
80
77
|
filters: {
|
|
81
78
|
service: string | null;
|
|
82
|
-
types:
|
|
79
|
+
types: DiscoveryTestType[] | ["all"];
|
|
83
80
|
suiteSelectors: string[];
|
|
84
81
|
fileNames: string[];
|
|
85
82
|
runnableOnly: boolean;
|
package/lib/discovery/index.mjs
CHANGED
|
@@ -3,6 +3,7 @@ import { loadConfigContext, resolveProductDir } from "../config/index.mjs";
|
|
|
3
3
|
import { discoverProject } from "../config/discovery.mjs";
|
|
4
4
|
import { loadTestkitConfig } from "../config/config-loader.mjs";
|
|
5
5
|
import { buildCoverageGraph } from "../coverage/index.mjs";
|
|
6
|
+
import { formatPublicTestType } from "../domain/test-types.mjs";
|
|
6
7
|
import { historyFilePath, loadHistory, summarizeHistoryForFiles } from "../history/index.mjs";
|
|
7
8
|
import {
|
|
8
9
|
matchesSelectedTypes,
|
|
@@ -124,12 +125,7 @@ export async function discoverTests(options = {}) {
|
|
|
124
125
|
|
|
125
126
|
export function formatSelectionTypeLabel(type) {
|
|
126
127
|
if (type === "int") return "Integration";
|
|
127
|
-
|
|
128
|
-
if (type === "scenario") return "Scenario";
|
|
129
|
-
if (type === "dal") return "DAL";
|
|
130
|
-
if (type === "load") return "Load";
|
|
131
|
-
if (type === "pw") return "Playwright";
|
|
132
|
-
return type;
|
|
128
|
+
return formatPublicTestType(type);
|
|
133
129
|
}
|
|
134
130
|
|
|
135
131
|
export function formatDisplayName(value) {
|
|
@@ -226,9 +222,8 @@ function buildResolvedSuiteEntries(config, suite, internalType, filters) {
|
|
|
226
222
|
service: config.name,
|
|
227
223
|
suiteName: suite.name,
|
|
228
224
|
groupLabel: formatDisplayName(suite.name),
|
|
229
|
-
selectionType,
|
|
225
|
+
type: selectionType,
|
|
230
226
|
internalType,
|
|
231
|
-
framework,
|
|
232
227
|
skipped,
|
|
233
228
|
skipReason,
|
|
234
229
|
locks: [...new Set([...suiteLocks, ...fileLocks])].sort(),
|
|
@@ -239,13 +234,12 @@ function buildResolvedSuiteEntries(config, suite, internalType, filters) {
|
|
|
239
234
|
if (visibleFiles.length === 0) return null;
|
|
240
235
|
const skippedFileCount = visibleFiles.filter((entry) => entry.skipped).length;
|
|
241
236
|
const suiteEntry = {
|
|
242
|
-
id: buildSuiteId(config.name, selectionType, suite.name
|
|
237
|
+
id: buildSuiteId(config.name, selectionType, suite.name),
|
|
243
238
|
service: config.name,
|
|
244
239
|
name: suite.name,
|
|
245
240
|
displayName: formatDisplayName(suite.name),
|
|
246
|
-
selectionType,
|
|
241
|
+
type: selectionType,
|
|
247
242
|
internalType,
|
|
248
|
-
framework,
|
|
249
243
|
groupLabel: formatDisplayName(suite.name),
|
|
250
244
|
fileCount: visibleFiles.length,
|
|
251
245
|
activeFileCount: visibleFiles.length - skippedFileCount,
|
|
@@ -308,9 +302,8 @@ function buildRawDiscovery({ rawDiscovery, explicitServices, filters }) {
|
|
|
308
302
|
service: entry.serviceName,
|
|
309
303
|
suiteName: entry.suiteName,
|
|
310
304
|
groupLabel: formatDisplayName(entry.suiteName),
|
|
311
|
-
selectionType,
|
|
305
|
+
type: selectionType,
|
|
312
306
|
internalType: entry.type,
|
|
313
|
-
framework: entry.framework,
|
|
314
307
|
skipped: false,
|
|
315
308
|
skipReason: null,
|
|
316
309
|
locks: [],
|
|
@@ -318,7 +311,7 @@ function buildRawDiscovery({ rawDiscovery, explicitServices, filters }) {
|
|
|
318
311
|
};
|
|
319
312
|
files.push(fileEntry);
|
|
320
313
|
|
|
321
|
-
const suiteId = buildSuiteId(entry.serviceName, selectionType, entry.suiteName
|
|
314
|
+
const suiteId = buildSuiteId(entry.serviceName, selectionType, entry.suiteName);
|
|
322
315
|
const existingSuite = suitesById.get(suiteId);
|
|
323
316
|
if (existingSuite) {
|
|
324
317
|
existingSuite.filePaths.push(entry.filePath);
|
|
@@ -330,9 +323,8 @@ function buildRawDiscovery({ rawDiscovery, explicitServices, filters }) {
|
|
|
330
323
|
service: entry.serviceName,
|
|
331
324
|
name: entry.suiteName,
|
|
332
325
|
displayName: formatDisplayName(entry.suiteName),
|
|
333
|
-
selectionType,
|
|
326
|
+
type: selectionType,
|
|
334
327
|
internalType: entry.type,
|
|
335
|
-
framework: entry.framework,
|
|
336
328
|
groupLabel: formatDisplayName(entry.suiteName),
|
|
337
329
|
fileCount: 1,
|
|
338
330
|
activeFileCount: 1,
|
|
@@ -412,7 +404,7 @@ function buildSummary(services, suites, files, diagnostics) {
|
|
|
412
404
|
|
|
413
405
|
for (const file of files) {
|
|
414
406
|
byService[file.service] = (byService[file.service] || 0) + 1;
|
|
415
|
-
byType[file.
|
|
407
|
+
byType[file.type] = (byType[file.type] || 0) + 1;
|
|
416
408
|
}
|
|
417
409
|
|
|
418
410
|
return {
|
|
@@ -497,12 +489,12 @@ function normalizePath(filePath) {
|
|
|
497
489
|
export function fileDisplayName(filePath) {
|
|
498
490
|
const base = path.posix
|
|
499
491
|
.basename(filePath)
|
|
500
|
-
.replace(/(\.int|\.e2e|\.scenario|\.dal|\.load|\.pw)\.testkit\.ts$/, "");
|
|
492
|
+
.replace(/(\.int|\.e2e|\.scenario|\.dal|\.load|\.ui|\.pw)\.testkit\.ts$/, "");
|
|
501
493
|
return formatDisplayName(base);
|
|
502
494
|
}
|
|
503
495
|
|
|
504
|
-
function buildSuiteId(serviceName, selectionType, suiteName
|
|
505
|
-
return [serviceName, selectionType,
|
|
496
|
+
function buildSuiteId(serviceName, selectionType, suiteName) {
|
|
497
|
+
return [serviceName, selectionType, suiteName].join("|");
|
|
506
498
|
}
|
|
507
499
|
|
|
508
500
|
function buildFileId(serviceName, selectionType, filePath) {
|
|
@@ -512,16 +504,15 @@ function buildFileId(serviceName, selectionType, filePath) {
|
|
|
512
504
|
function compareSuiteEntries(left, right) {
|
|
513
505
|
return (
|
|
514
506
|
left.service.localeCompare(right.service) ||
|
|
515
|
-
left.
|
|
516
|
-
left.name.localeCompare(right.name)
|
|
517
|
-
left.framework.localeCompare(right.framework)
|
|
507
|
+
left.type.localeCompare(right.type) ||
|
|
508
|
+
left.name.localeCompare(right.name)
|
|
518
509
|
);
|
|
519
510
|
}
|
|
520
511
|
|
|
521
512
|
function compareFileEntries(left, right) {
|
|
522
513
|
return (
|
|
523
514
|
left.service.localeCompare(right.service) ||
|
|
524
|
-
left.
|
|
515
|
+
left.type.localeCompare(right.type) ||
|
|
525
516
|
left.suiteName.localeCompare(right.suiteName) ||
|
|
526
517
|
left.path.localeCompare(right.path)
|
|
527
518
|
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export const TEST_TYPE_ORDER = ["ui", "e2e", "scenario", "int", "dal", "load"];
|
|
2
|
+
export const TEST_TYPES = new Set(TEST_TYPE_ORDER);
|
|
3
|
+
export const RUN_TYPE_ORDER = [...TEST_TYPE_ORDER, "all"];
|
|
4
|
+
export const RUN_TYPES = new Set(RUN_TYPE_ORDER);
|
|
5
|
+
export const LEGACY_TEST_TYPE_ALIASES = new Map([
|
|
6
|
+
["pw", "ui"],
|
|
7
|
+
]);
|
|
8
|
+
|
|
9
|
+
const TEST_TYPE_LABELS = {
|
|
10
|
+
ui: "UI",
|
|
11
|
+
e2e: "E2E",
|
|
12
|
+
scenario: "Scenario",
|
|
13
|
+
int: "INT",
|
|
14
|
+
dal: "DAL",
|
|
15
|
+
load: "Load",
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
export function normalizePublicTestType(value) {
|
|
19
|
+
const normalized = String(value || "").trim();
|
|
20
|
+
return LEGACY_TEST_TYPE_ALIASES.get(normalized) || normalized;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function isPublicTestType(value) {
|
|
24
|
+
return TEST_TYPES.has(normalizePublicTestType(value));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function isRunType(value) {
|
|
28
|
+
const normalized = normalizePublicTestType(value);
|
|
29
|
+
return normalized === "all" || TEST_TYPES.has(normalized);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function formatPublicTestType(value) {
|
|
33
|
+
const normalized = normalizePublicTestType(value);
|
|
34
|
+
return TEST_TYPE_LABELS[normalized] || String(value || "");
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function publicTestTypeList({ includeAll = false, includeLegacy = false } = {}) {
|
|
38
|
+
const values = includeAll ? RUN_TYPE_ORDER : TEST_TYPE_ORDER;
|
|
39
|
+
return includeLegacy ? [...values, "pw"] : [...values];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function publicTestTypeListText(options = {}) {
|
|
43
|
+
return publicTestTypeList(options).join(", ");
|
|
44
|
+
}
|
package/lib/history/index.d.ts
CHANGED
|
@@ -15,8 +15,7 @@ export interface TestHistoryEntry extends TestHistorySummary {
|
|
|
15
15
|
path: string;
|
|
16
16
|
service: string;
|
|
17
17
|
suiteName: string;
|
|
18
|
-
|
|
19
|
-
framework: string;
|
|
18
|
+
type: string;
|
|
20
19
|
notRunCount: number;
|
|
21
20
|
durationCount: number;
|
|
22
21
|
}
|
|
@@ -37,10 +36,10 @@ export declare function updateHistoryFromRunArtifact(
|
|
|
37
36
|
): TestHistoryDocument;
|
|
38
37
|
export declare function summarizeHistoryForFiles(
|
|
39
38
|
history: TestHistoryDocument,
|
|
40
|
-
files?: Array<{ id: string; service: string;
|
|
39
|
+
files?: Array<{ id: string; service: string; type: string; path: string }>
|
|
41
40
|
): Map<string, TestHistorySummary>;
|
|
42
41
|
export declare function buildHistoryTestId(
|
|
43
42
|
serviceName: string,
|
|
44
|
-
|
|
43
|
+
type: string,
|
|
45
44
|
filePath: string
|
|
46
45
|
): string;
|
package/lib/history/index.mjs
CHANGED
|
@@ -52,8 +52,7 @@ export function updateHistoryFromRunArtifact(history, runArtifact, recordedAt =
|
|
|
52
52
|
path: file.path,
|
|
53
53
|
service: service.name,
|
|
54
54
|
suiteName: suite.name,
|
|
55
|
-
|
|
56
|
-
framework: normalizeArtifactFramework(suite.framework),
|
|
55
|
+
type: suite.type,
|
|
57
56
|
firstSeenAt: seenAt,
|
|
58
57
|
lastSeenAt: seenAt,
|
|
59
58
|
lastRunAt: seenAt,
|
|
@@ -70,8 +69,7 @@ export function updateHistoryFromRunArtifact(history, runArtifact, recordedAt =
|
|
|
70
69
|
next.path = file.path;
|
|
71
70
|
next.service = service.name;
|
|
72
71
|
next.suiteName = suite.name;
|
|
73
|
-
next.
|
|
74
|
-
next.framework = normalizeArtifactFramework(suite.framework);
|
|
72
|
+
next.type = suite.type;
|
|
75
73
|
next.lastSeenAt = seenAt;
|
|
76
74
|
next.lastRunAt = seenAt;
|
|
77
75
|
next.runCount += 1;
|
|
@@ -106,7 +104,7 @@ export function summarizeHistoryForFiles(history, files = []) {
|
|
|
106
104
|
const normalized = normalizeHistory(history);
|
|
107
105
|
const byId = new Map();
|
|
108
106
|
for (const file of files) {
|
|
109
|
-
const entry = normalized.tests[file.id] || normalized.tests[buildHistoryTestId(file.service, file.
|
|
107
|
+
const entry = normalized.tests[file.id] || normalized.tests[buildHistoryTestId(file.service, file.type, file.path)];
|
|
110
108
|
if (!entry) continue;
|
|
111
109
|
byId.set(file.id, {
|
|
112
110
|
firstSeenAt: entry.firstSeenAt || null,
|
|
@@ -123,8 +121,8 @@ export function summarizeHistoryForFiles(history, files = []) {
|
|
|
123
121
|
return byId;
|
|
124
122
|
}
|
|
125
123
|
|
|
126
|
-
export function buildHistoryTestId(serviceName,
|
|
127
|
-
return [serviceName,
|
|
124
|
+
export function buildHistoryTestId(serviceName, type, filePath) {
|
|
125
|
+
return [serviceName, type, normalizePath(filePath)].join("|");
|
|
128
126
|
}
|
|
129
127
|
|
|
130
128
|
function normalizeHistory(parsed) {
|
|
@@ -135,8 +133,7 @@ function normalizeHistory(parsed) {
|
|
|
135
133
|
path: normalizePath(entry.path || ""),
|
|
136
134
|
service: String(entry.service || ""),
|
|
137
135
|
suiteName: String(entry.suiteName || ""),
|
|
138
|
-
|
|
139
|
-
framework: normalizeArtifactFramework(entry.framework || "k6"),
|
|
136
|
+
type: String(entry.type || entry.selectionType || ""),
|
|
140
137
|
firstSeenAt: entry.firstSeenAt || null,
|
|
141
138
|
lastSeenAt: entry.lastSeenAt || null,
|
|
142
139
|
lastRunAt: entry.lastRunAt || null,
|
|
@@ -156,11 +153,6 @@ function normalizeHistory(parsed) {
|
|
|
156
153
|
};
|
|
157
154
|
}
|
|
158
155
|
|
|
159
|
-
function normalizeArtifactFramework(value) {
|
|
160
|
-
if (value === "default") return "k6";
|
|
161
|
-
return value || "k6";
|
|
162
|
-
}
|
|
163
|
-
|
|
164
156
|
function normalizePath(filePath) {
|
|
165
157
|
return String(filePath).split(path.sep).join("/").replace(/^\.\/+/, "");
|
|
166
158
|
}
|
|
@@ -40,8 +40,7 @@ export function longestServiceName(results) {
|
|
|
40
40
|
}
|
|
41
41
|
|
|
42
42
|
export function formatFrameworkLabel(framework) {
|
|
43
|
-
|
|
44
|
-
return framework;
|
|
43
|
+
return "";
|
|
45
44
|
}
|
|
46
45
|
|
|
47
46
|
export function formatSuiteFramework(framework) {
|
|
@@ -175,7 +174,7 @@ export function buildDebugRunSummaryLines(results, durationMs, regressionReport
|
|
|
175
174
|
const fileDetail =
|
|
176
175
|
suite.failedFiles.length > 0 ? ` (${suite.failedFiles.join(", ")})` : "";
|
|
177
176
|
lines.push(
|
|
178
|
-
` - ${suite.type}:${suite.name}${
|
|
177
|
+
` - ${suite.type}:${suite.name}${fileDetail} · ${formatDuration(suite.durationMs)}`
|
|
179
178
|
);
|
|
180
179
|
if (suite.error) {
|
|
181
180
|
lines.push(` ${suite.error}`);
|