@elench/testkit 0.1.80 → 0.1.81
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 +50 -35
- package/lib/cli/args.mjs +2 -14
- package/lib/cli/args.test.mjs +1 -17
- package/lib/cli/command-helpers.mjs +1 -20
- package/lib/cli/entrypoint.mjs +0 -4
- package/lib/cli/presentation/colors.mjs +1 -1
- package/lib/cli/presentation/failure-presentation.mjs +4 -4
- package/lib/cli/presentation/run-reporter.mjs +23 -9
- package/lib/cli/presentation/run-reporter.test.mjs +12 -6
- package/lib/cli/presentation/summary-box.test.mjs +4 -4
- package/lib/cli/viewer.mjs +18 -19
- package/lib/config/index.mjs +6 -6
- package/lib/config/runtime.mjs +8 -8
- package/lib/config-api/index.d.ts +4 -4
- package/lib/package.test.mjs +4 -4
- package/lib/{known-failures → regressions}/github.mjs +36 -78
- package/lib/regressions/github.test.mjs +324 -0
- package/lib/regressions/index.d.ts +189 -0
- package/lib/{known-failures → regressions}/index.mjs +90 -93
- package/lib/{known-failures → regressions}/index.test.mjs +37 -48
- package/lib/runner/formatting.mjs +49 -34
- package/lib/runner/formatting.test.mjs +16 -15
- package/lib/runner/metadata.mjs +1 -1
- package/lib/runner/orchestrator.mjs +7 -9
- package/lib/runner/regressions.mjs +304 -0
- package/lib/runner/{triage.test.mjs → regressions.test.mjs} +50 -36
- package/lib/runner/reporting.mjs +2 -2
- package/lib/runner/reporting.test.mjs +2 -2
- package/lib/runner/run-finalization.mjs +18 -30
- package/lib/runner/template-steps.mjs +2 -2
- package/node_modules/@elench/next-analysis/package.json +1 -1
- package/node_modules/@elench/testkit-bridge/package.json +2 -2
- package/node_modules/@elench/testkit-protocol/package.json +1 -1
- package/node_modules/@elench/ts-analysis/package.json +1 -1
- package/package.json +8 -8
- package/lib/cli/commands/known-failures/render.mjs +0 -19
- package/lib/cli/commands/known-failures/validate.mjs +0 -20
- package/lib/cli/known-failures.mjs +0 -164
- package/lib/known-failures/github.test.mjs +0 -512
- package/lib/known-failures/index.d.ts +0 -192
- package/lib/runner/triage.mjs +0 -221
- /package/lib/{known-failures → regressions}/github-cache.mjs +0 -0
- /package/lib/{known-failures → regressions}/github-transport.mjs +0 -0
|
@@ -3,10 +3,10 @@ import os from "os";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { afterEach, describe, expect, it } from "vitest";
|
|
5
5
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
findMatchingRegressionEntries,
|
|
7
|
+
normalizeRegressionCatalogDocument,
|
|
8
|
+
renderRegressionCatalogMarkdown,
|
|
9
|
+
validateRegressionCatalogDocument,
|
|
10
10
|
} from "./index.mjs";
|
|
11
11
|
|
|
12
12
|
const tempDirs = [];
|
|
@@ -17,25 +17,22 @@ afterEach(() => {
|
|
|
17
17
|
}
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
-
describe("
|
|
20
|
+
describe("regression catalog", () => {
|
|
21
21
|
it("matches entries by service, type, path, and failure key", () => {
|
|
22
|
-
const document =
|
|
22
|
+
const document = normalizeRegressionCatalogDocument({
|
|
23
23
|
schemaVersion: 1,
|
|
24
24
|
entries: [
|
|
25
25
|
{
|
|
26
26
|
id: "bad-message",
|
|
27
|
-
title: "Bad message bug",
|
|
28
27
|
classification: "product_bug",
|
|
29
|
-
state: "open",
|
|
30
28
|
issue: {
|
|
31
29
|
repo: "acme/repo",
|
|
32
30
|
number: 12,
|
|
33
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
34
31
|
},
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
lastReviewedAt: "2026-04
|
|
38
|
-
|
|
32
|
+
summary: "API returns the wrong message",
|
|
33
|
+
cause: "The payload is wrong.",
|
|
34
|
+
lastReviewedAt: "2026-05-04",
|
|
35
|
+
fingerprints: [
|
|
39
36
|
{
|
|
40
37
|
service: "api",
|
|
41
38
|
type: "int",
|
|
@@ -47,7 +44,7 @@ describe("known failures core", () => {
|
|
|
47
44
|
],
|
|
48
45
|
});
|
|
49
46
|
|
|
50
|
-
const matches =
|
|
47
|
+
const matches = findMatchingRegressionEntries(document, {
|
|
51
48
|
service: "api",
|
|
52
49
|
type: "int",
|
|
53
50
|
path: "src/api/routes/__testkit__/failing.int.testkit.ts",
|
|
@@ -59,24 +56,21 @@ describe("known failures core", () => {
|
|
|
59
56
|
expect(matches[0].id).toBe("bad-message");
|
|
60
57
|
});
|
|
61
58
|
|
|
62
|
-
it("matches failureKey against
|
|
63
|
-
const document =
|
|
59
|
+
it("matches fingerprint failureKey against detail title", () => {
|
|
60
|
+
const document = normalizeRegressionCatalogDocument({
|
|
64
61
|
schemaVersion: 1,
|
|
65
62
|
entries: [
|
|
66
63
|
{
|
|
67
64
|
id: "missing-route",
|
|
68
|
-
title: "Missing route bug",
|
|
69
65
|
classification: "product_bug",
|
|
70
|
-
state: "open",
|
|
71
66
|
issue: {
|
|
72
67
|
repo: "acme/repo",
|
|
73
68
|
number: 13,
|
|
74
|
-
url: "https://github.com/acme/repo/issues/13",
|
|
75
69
|
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
lastReviewedAt: "2026-04
|
|
79
|
-
|
|
70
|
+
summary: "Missing route returns the wrong status",
|
|
71
|
+
cause: "The route returns 404.",
|
|
72
|
+
lastReviewedAt: "2026-05-04",
|
|
73
|
+
fingerprints: [
|
|
80
74
|
{
|
|
81
75
|
service: "api",
|
|
82
76
|
type: "int",
|
|
@@ -88,7 +82,7 @@ describe("known failures core", () => {
|
|
|
88
82
|
],
|
|
89
83
|
});
|
|
90
84
|
|
|
91
|
-
const matches =
|
|
85
|
+
const matches = findMatchingRegressionEntries(document, {
|
|
92
86
|
service: "api",
|
|
93
87
|
type: "int",
|
|
94
88
|
path: "__testkit__/health/http-failure.int.testkit.ts",
|
|
@@ -105,31 +99,28 @@ describe("known failures core", () => {
|
|
|
105
99
|
expect(matches[0].id).toBe("missing-route");
|
|
106
100
|
});
|
|
107
101
|
|
|
108
|
-
it("validates status coverage and filesystem
|
|
109
|
-
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-
|
|
102
|
+
it("validates status coverage and filesystem fingerprints", () => {
|
|
103
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "testkit-regressions-"));
|
|
110
104
|
tempDirs.push(tempDir);
|
|
111
105
|
const testPath = path.join(tempDir, "src/api/routes/__testkit__/failing.int.testkit.ts");
|
|
112
106
|
fs.mkdirSync(path.dirname(testPath), { recursive: true });
|
|
113
107
|
fs.writeFileSync(testPath, "export default {};\n");
|
|
114
108
|
|
|
115
|
-
const document =
|
|
109
|
+
const document = normalizeRegressionCatalogDocument({
|
|
116
110
|
schemaVersion: 1,
|
|
117
111
|
issueRepo: "acme/repo",
|
|
118
112
|
entries: [
|
|
119
113
|
{
|
|
120
114
|
id: "bad-message",
|
|
121
|
-
title: "Bad message bug",
|
|
122
115
|
classification: "product_bug",
|
|
123
|
-
state: "open",
|
|
124
116
|
issue: {
|
|
125
117
|
repo: "acme/repo",
|
|
126
118
|
number: 12,
|
|
127
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
128
119
|
},
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
lastReviewedAt: "2026-04
|
|
132
|
-
|
|
120
|
+
summary: "API returns the wrong message",
|
|
121
|
+
cause: "The payload is wrong.",
|
|
122
|
+
lastReviewedAt: "2026-05-04",
|
|
123
|
+
fingerprints: [
|
|
133
124
|
{
|
|
134
125
|
service: "api",
|
|
135
126
|
type: "int",
|
|
@@ -140,7 +131,7 @@ describe("known failures core", () => {
|
|
|
140
131
|
],
|
|
141
132
|
});
|
|
142
133
|
|
|
143
|
-
const result =
|
|
134
|
+
const result = validateRegressionCatalogDocument(document, {
|
|
144
135
|
productDir: tempDir,
|
|
145
136
|
statusArtifact: {
|
|
146
137
|
tests: [
|
|
@@ -162,37 +153,35 @@ describe("known failures core", () => {
|
|
|
162
153
|
|
|
163
154
|
expect(result.errors).toEqual([]);
|
|
164
155
|
expect(result.stats.failedTests).toBe(2);
|
|
165
|
-
expect(result.stats.
|
|
156
|
+
expect(result.stats.diagnosedFailedTests).toBe(1);
|
|
166
157
|
expect(result.warnings).toContain(
|
|
167
|
-
"
|
|
158
|
+
"New failing test not yet in regression catalog: src/api/routes/__testkit__/other.int.testkit.ts"
|
|
168
159
|
);
|
|
169
160
|
});
|
|
170
161
|
|
|
171
162
|
it("renders markdown from the canonical document", () => {
|
|
172
|
-
const markdown =
|
|
173
|
-
|
|
163
|
+
const markdown = renderRegressionCatalogMarkdown(
|
|
164
|
+
normalizeRegressionCatalogDocument({
|
|
174
165
|
schemaVersion: 1,
|
|
175
166
|
entries: [
|
|
176
167
|
{
|
|
177
168
|
id: "bad-message",
|
|
178
|
-
title: "Bad message bug",
|
|
179
169
|
classification: "product_bug",
|
|
180
|
-
state: "open",
|
|
181
170
|
issue: {
|
|
182
171
|
repo: "acme/repo",
|
|
183
172
|
number: 12,
|
|
184
|
-
url: "https://github.com/acme/repo/issues/12",
|
|
185
173
|
},
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
lastReviewedAt: "2026-04
|
|
189
|
-
|
|
174
|
+
summary: "API returns the wrong message",
|
|
175
|
+
cause: "The payload is wrong.",
|
|
176
|
+
lastReviewedAt: "2026-05-04",
|
|
177
|
+
fingerprints: [{ path: "src/api/routes/__testkit__/failing.int.testkit.ts" }],
|
|
190
178
|
},
|
|
191
179
|
],
|
|
192
180
|
})
|
|
193
181
|
);
|
|
194
182
|
|
|
195
|
-
expect(markdown).toContain("#
|
|
196
|
-
expect(markdown).toContain("
|
|
183
|
+
expect(markdown).toContain("# Regression Catalog");
|
|
184
|
+
expect(markdown).toContain("API returns the wrong message");
|
|
185
|
+
expect(markdown).toContain("#12");
|
|
197
186
|
});
|
|
198
187
|
});
|
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
buildKnownFailureIssueValidationSummaryLines,
|
|
4
|
-
buildKnownFailureIssueValidationSummaryParts,
|
|
5
|
-
} from "../known-failures/github.mjs";
|
|
1
|
+
import { findMatchingRegressionEntries } from "../regressions/index.mjs";
|
|
2
|
+
import { buildRegressionSyncSummaryLines } from "../regressions/github.mjs";
|
|
6
3
|
|
|
7
4
|
export function formatDuration(durationMs) {
|
|
8
5
|
const totalSeconds = Math.max(0, Math.round(durationMs / 1000));
|
|
@@ -52,14 +49,14 @@ export function formatSuiteFramework(framework) {
|
|
|
52
49
|
return label ? ` [${label}]` : "";
|
|
53
50
|
}
|
|
54
51
|
|
|
55
|
-
export function buildRunSummaryLines(results, durationMs,
|
|
56
|
-
return buildCompactRunSummaryLines(results, durationMs,
|
|
52
|
+
export function buildRunSummaryLines(results, durationMs, regressionReport = null) {
|
|
53
|
+
return buildCompactRunSummaryLines(results, durationMs, regressionReport);
|
|
57
54
|
}
|
|
58
55
|
|
|
59
|
-
export function buildRunSummaryData(results, durationMs,
|
|
56
|
+
export function buildRunSummaryData(results, durationMs, regressionReport = null) {
|
|
60
57
|
const totals = summarizeResults(results);
|
|
61
58
|
const serviceErrors = collectServiceErrors(results);
|
|
62
|
-
const
|
|
59
|
+
const regressionSummary = regressionReport?.summary || null;
|
|
63
60
|
return {
|
|
64
61
|
result: totals.failedServices > 0 ? "FAILED" : "PASSED",
|
|
65
62
|
totalServices: totals.totalServices,
|
|
@@ -71,16 +68,21 @@ export function buildRunSummaryData(results, durationMs, knownFailureIssueValida
|
|
|
71
68
|
files: totals.totalFiles,
|
|
72
69
|
duration: formatDuration(durationMs),
|
|
73
70
|
serviceErrors: serviceErrors.length,
|
|
74
|
-
|
|
71
|
+
newRegressions: regressionSummary?.newRegressions || 0,
|
|
72
|
+
knownRegressions: regressionSummary?.knownRegressions || 0,
|
|
73
|
+
fixedKnownRegressions: regressionSummary?.fixedKnownRegressions || 0,
|
|
74
|
+
catalogStale: regressionSummary?.catalogStale || 0,
|
|
75
|
+
catalogSyncUnavailable: Boolean(regressionSummary?.catalogSyncUnavailable),
|
|
76
|
+
usedStaleCache: Boolean(regressionSummary?.usedStaleCache),
|
|
75
77
|
};
|
|
76
78
|
}
|
|
77
79
|
|
|
78
80
|
export function buildCompactRunSummaryLines(
|
|
79
81
|
results,
|
|
80
82
|
durationMs,
|
|
81
|
-
|
|
83
|
+
regressionReport = null
|
|
82
84
|
) {
|
|
83
|
-
const summary = buildRunSummaryData(results, durationMs,
|
|
85
|
+
const summary = buildRunSummaryData(results, durationMs, regressionReport);
|
|
84
86
|
const lines = [
|
|
85
87
|
"",
|
|
86
88
|
`Summary: ${summary.passed} passed, ${summary.failed} failed, ${summary.skipped} skipped, ${summary.notRun} not run across ${summary.files} ${pluralize(summary.files, "file", "files")} in ${summary.duration}`,
|
|
@@ -90,8 +92,20 @@ export function buildCompactRunSummaryLines(
|
|
|
90
92
|
lines.push(`Runtime errors: ${summary.serviceErrors}`);
|
|
91
93
|
}
|
|
92
94
|
|
|
93
|
-
if (summary.
|
|
94
|
-
lines.push(`
|
|
95
|
+
if (summary.newRegressions > 0) {
|
|
96
|
+
lines.push(`New regressions: ${summary.newRegressions}`);
|
|
97
|
+
}
|
|
98
|
+
if (summary.knownRegressions > 0) {
|
|
99
|
+
lines.push(`Known regressions: ${summary.knownRegressions}`);
|
|
100
|
+
}
|
|
101
|
+
if (summary.fixedKnownRegressions > 0) {
|
|
102
|
+
lines.push(`Fixed known regressions: ${summary.fixedKnownRegressions}`);
|
|
103
|
+
}
|
|
104
|
+
if (summary.catalogStale > 0) {
|
|
105
|
+
lines.push(`Catalog stale: ${summary.catalogStale}`);
|
|
106
|
+
}
|
|
107
|
+
if (summary.catalogSyncUnavailable) {
|
|
108
|
+
lines.push("Catalog sync unavailable");
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
lines.push("");
|
|
@@ -103,7 +117,7 @@ export function buildCompactRunSummaryLines(
|
|
|
103
117
|
return lines;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
|
-
export function buildDebugRunSummaryLines(results, durationMs,
|
|
120
|
+
export function buildDebugRunSummaryLines(results, durationMs, regressionReport = null) {
|
|
107
121
|
const totalServices = results.length;
|
|
108
122
|
const executedServices = results.filter((result) => !result.skipped);
|
|
109
123
|
const skippedServices = results.filter((result) => result.skipped);
|
|
@@ -172,11 +186,19 @@ export function buildDebugRunSummaryLines(results, durationMs, knownFailureIssue
|
|
|
172
186
|
}
|
|
173
187
|
}
|
|
174
188
|
|
|
175
|
-
const
|
|
176
|
-
|
|
189
|
+
const regressionSyncLines = buildRegressionSyncSummaryLines(
|
|
190
|
+
regressionReport?.catalog?.configured ? {
|
|
191
|
+
summary: {
|
|
192
|
+
byCode: {
|
|
193
|
+
closed_but_failing: regressionReport.summary.catalogStale,
|
|
194
|
+
validation_unavailable: regressionReport.summary.catalogSyncUnavailable ? 1 : 0,
|
|
195
|
+
used_stale_cache: regressionReport.summary.usedStaleCache ? 1 : 0,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
} : null
|
|
177
199
|
);
|
|
178
|
-
if (
|
|
179
|
-
lines.push(...
|
|
200
|
+
if (regressionSyncLines.length > 0) {
|
|
201
|
+
lines.push(...regressionSyncLines);
|
|
180
202
|
}
|
|
181
203
|
|
|
182
204
|
if (failedServices.length > 0) {
|
|
@@ -188,7 +210,7 @@ export function buildDebugRunSummaryLines(results, durationMs, knownFailureIssue
|
|
|
188
210
|
return lines;
|
|
189
211
|
}
|
|
190
212
|
|
|
191
|
-
export function buildFailurePresentation(fileSummary,
|
|
213
|
+
export function buildFailurePresentation(fileSummary, regressionCatalog = null) {
|
|
192
214
|
const rankedDetails = rankFailureDetails(fileSummary.failureDetails || []);
|
|
193
215
|
const primaryDetail = rankedDetails[0] || null;
|
|
194
216
|
const fallbackMessages = rankedDetails
|
|
@@ -200,8 +222,8 @@ export function buildFailurePresentation(fileSummary, knownFailures = null) {
|
|
|
200
222
|
const responseLine = formatFailureResponsePreview(primaryDetail);
|
|
201
223
|
if (responseLine) details.push(responseLine);
|
|
202
224
|
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
225
|
+
const regressionLine = formatInlineRegressionLine(fileSummary, regressionCatalog);
|
|
226
|
+
if (regressionLine) details.push(regressionLine);
|
|
205
227
|
|
|
206
228
|
const requestLine = formatFailureRequestHint(primaryDetail);
|
|
207
229
|
if (requestLine) details.push(requestLine);
|
|
@@ -297,20 +319,13 @@ function formatFailureRequestHint(detail) {
|
|
|
297
319
|
return `request: ${method} ${path}`;
|
|
298
320
|
}
|
|
299
321
|
|
|
300
|
-
function
|
|
301
|
-
if (!
|
|
302
|
-
const matches =
|
|
303
|
-
if (matches.length === 0) return "
|
|
322
|
+
function formatInlineRegressionLine(fileSummary, regressionCatalog) {
|
|
323
|
+
if (!regressionCatalog) return "regression: new";
|
|
324
|
+
const matches = findMatchingRegressionEntries(regressionCatalog, fileSummary);
|
|
325
|
+
if (matches.length === 0) return "regression: new";
|
|
304
326
|
|
|
305
327
|
const entry = matches[0];
|
|
306
|
-
|
|
307
|
-
if (entry.state === "open") {
|
|
308
|
-
return `triage: known issue ${issueLabel} open`;
|
|
309
|
-
}
|
|
310
|
-
if (entry.state === "closed") {
|
|
311
|
-
return `triage: known issue ${issueLabel} closed`;
|
|
312
|
-
}
|
|
313
|
-
return `triage: known issue ${issueLabel}`;
|
|
328
|
+
return `regression: known #${entry.issue.number} ${entry.classification}`;
|
|
314
329
|
}
|
|
315
330
|
|
|
316
331
|
function isThresholdWrapperMessage(message) {
|
|
@@ -87,10 +87,12 @@ describe("runner formatting", () => {
|
|
|
87
87
|
20_000,
|
|
88
88
|
{
|
|
89
89
|
summary: {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
newRegressions: 1,
|
|
91
|
+
knownRegressions: 2,
|
|
92
|
+
fixedKnownRegressions: 3,
|
|
93
|
+
catalogStale: 4,
|
|
94
|
+
catalogSyncUnavailable: true,
|
|
95
|
+
usedStaleCache: false,
|
|
94
96
|
},
|
|
95
97
|
}
|
|
96
98
|
);
|
|
@@ -105,10 +107,11 @@ describe("runner formatting", () => {
|
|
|
105
107
|
duration: "20s",
|
|
106
108
|
serviceErrors: 1,
|
|
107
109
|
});
|
|
108
|
-
expect(summary.
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
expect(summary.newRegressions).toBe(1);
|
|
111
|
+
expect(summary.knownRegressions).toBe(2);
|
|
112
|
+
expect(summary.fixedKnownRegressions).toBe(3);
|
|
113
|
+
expect(summary.catalogStale).toBe(4);
|
|
114
|
+
expect(summary.catalogSyncUnavailable).toBe(true);
|
|
112
115
|
});
|
|
113
116
|
|
|
114
117
|
it("builds compact summary lines without failure and runtime dump sections", () => {
|
|
@@ -137,6 +140,7 @@ describe("runner formatting", () => {
|
|
|
137
140
|
|
|
138
141
|
expect(lines.join("\n")).toContain("Summary: 0 passed, 1 failed, 0 skipped, 0 not run across 1 file");
|
|
139
142
|
expect(lines.join("\n")).toContain("Runtime errors: 1");
|
|
143
|
+
expect(lines.join("\n")).not.toContain("Catalog issues:");
|
|
140
144
|
expect(lines.join("\n")).not.toContain("Failures:");
|
|
141
145
|
expect(lines.join("\n")).not.toContain("Runtime Errors:");
|
|
142
146
|
});
|
|
@@ -172,18 +176,15 @@ describe("runner formatting", () => {
|
|
|
172
176
|
entries: [
|
|
173
177
|
{
|
|
174
178
|
id: "health-is-bad",
|
|
175
|
-
title: "Health is bad",
|
|
176
179
|
classification: "product_bug",
|
|
177
|
-
state: "open",
|
|
178
180
|
issue: {
|
|
179
181
|
repo: "acme/example",
|
|
180
182
|
number: 42,
|
|
181
|
-
url: "https://github.com/acme/example/issues/42",
|
|
182
183
|
},
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
summary: "Health is bad",
|
|
185
|
+
cause: "because",
|
|
185
186
|
lastReviewedAt: "2026-01-01",
|
|
186
|
-
|
|
187
|
+
fingerprints: [
|
|
187
188
|
{
|
|
188
189
|
service: "api",
|
|
189
190
|
type: "int",
|
|
@@ -198,7 +199,7 @@ describe("runner formatting", () => {
|
|
|
198
199
|
|
|
199
200
|
expect(presentation.primary).toBe("GET /health expected 200, got 404");
|
|
200
201
|
expect(presentation.details).toContain('response: {"error":"nope"}');
|
|
201
|
-
expect(presentation.details).toContain("
|
|
202
|
+
expect(presentation.details).toContain("regression: known #42 product_bug");
|
|
202
203
|
expect(presentation.details).toContain("logs: requestId=req-1");
|
|
203
204
|
});
|
|
204
205
|
});
|
package/lib/runner/metadata.mjs
CHANGED
|
@@ -3,7 +3,7 @@ import os from "os";
|
|
|
3
3
|
import path from "path";
|
|
4
4
|
import { execFileSync } from "child_process";
|
|
5
5
|
import { fileURLToPath } from "url";
|
|
6
|
-
import { parseGitHubRepoSlug } from "../
|
|
6
|
+
import { parseGitHubRepoSlug } from "../regressions/github.mjs";
|
|
7
7
|
|
|
8
8
|
export function collectGitMetadata(productDir) {
|
|
9
9
|
const read = (args) => {
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
recordGraphError,
|
|
14
14
|
recordTaskOutcome,
|
|
15
15
|
} from "./results.mjs";
|
|
16
|
-
import {
|
|
16
|
+
import { loadRegressionCatalogConfig } from "./regressions.mjs";
|
|
17
17
|
import { formatError } from "./formatting.mjs";
|
|
18
18
|
import {
|
|
19
19
|
loadTimings,
|
|
@@ -56,12 +56,12 @@ export async function runAll(configs, typeValues, suiteSelectors, opts, allConfi
|
|
|
56
56
|
},
|
|
57
57
|
testkitVersion: readPackageMetadata().version,
|
|
58
58
|
};
|
|
59
|
-
const
|
|
59
|
+
const regressionCatalog = loadRegressionCatalogConfig(
|
|
60
60
|
productDir,
|
|
61
|
-
configs[0]?.testkit?.
|
|
61
|
+
configs[0]?.testkit?.regressions || null
|
|
62
62
|
);
|
|
63
63
|
const reporter = opts.reporter || null;
|
|
64
|
-
reporter?.
|
|
64
|
+
reporter?.setRegressionCatalog?.(regressionCatalog);
|
|
65
65
|
const logRegistry = createRunLogRegistry(productDir);
|
|
66
66
|
const workerState = {
|
|
67
67
|
workerCount: 0,
|
|
@@ -114,7 +114,6 @@ export async function runAll(configs, typeValues, suiteSelectors, opts, allConfi
|
|
|
114
114
|
lifecycle.installSignalHandlers();
|
|
115
115
|
let results = [];
|
|
116
116
|
let finishedAt = Date.now();
|
|
117
|
-
let knownFailureIssueValidation = null;
|
|
118
117
|
writeLiveSnapshot();
|
|
119
118
|
|
|
120
119
|
try {
|
|
@@ -209,15 +208,14 @@ export async function runAll(configs, typeValues, suiteSelectors, opts, allConfi
|
|
|
209
208
|
metadata,
|
|
210
209
|
logRegistry,
|
|
211
210
|
setupRegistry,
|
|
212
|
-
|
|
213
|
-
|
|
211
|
+
regressionCatalog,
|
|
212
|
+
regressionSyncConfig: configs[0]?.testkit?.regressions?.sync || null,
|
|
214
213
|
telemetry,
|
|
215
214
|
reporter,
|
|
216
215
|
writeStatus: opts.writeStatus,
|
|
217
216
|
});
|
|
218
|
-
knownFailureIssueValidation = finalized.knownFailureIssueValidation;
|
|
219
217
|
if (results.some((result) => result.failed)) exitCode = 1;
|
|
220
|
-
if (finalized.
|
|
218
|
+
if (finalized.shouldFailRegressionSync) {
|
|
221
219
|
exitCode = 1;
|
|
222
220
|
}
|
|
223
221
|
if (lifecycle.isStopRequested()) exitCode = Math.max(exitCode, 130);
|