canary-test-cli 6.7.0 → 6.7.1
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.
|
@@ -73,7 +73,24 @@ export declare function runStatus(totals: {
|
|
|
73
73
|
failed: number;
|
|
74
74
|
flaky: number;
|
|
75
75
|
}, fullResultStatus?: string): "passed" | "failed" | "flaky" | "cancelled";
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Collapse results that share a `full_title` into one row.
|
|
78
|
+
*
|
|
79
|
+
* TestTracker's ingest holds a unique index on `(run_id, full_title)` and
|
|
80
|
+
* rejects the WHOLE run on a collision, so a duplicate title is not a cosmetic
|
|
81
|
+
* problem — it takes the suite dark. The reporter keys its in-flight map by
|
|
82
|
+
* `test.id`, which is genuinely unique, but a title is not: a Playwright
|
|
83
|
+
* `dependencies:` setup project runs in full in EVERY shard (dependencies are
|
|
84
|
+
* not sharded), so a `merge-reports` payload over a sharded matrix legitimately
|
|
85
|
+
* carries the same setup title once per shard. Capwell's `capwell-web` suite
|
|
86
|
+
* had every nightly rejected this way and never ingested a single run.
|
|
87
|
+
*
|
|
88
|
+
* Collapse rule keeps the merged row honest: worst status wins, the first real
|
|
89
|
+
* error is preserved (so the SDET still sees why it failed), and duration and
|
|
90
|
+
* retries take the max rather than the last-seen value.
|
|
91
|
+
*/
|
|
92
|
+
export declare function dedupeByFullTitle(results: ResultEntry[]): ResultEntry[];
|
|
93
|
+
export declare function buildPayload(rawResults: ResultEntry[], cfg: ResolvedConfig, timing: {
|
|
77
94
|
startedAt: string;
|
|
78
95
|
finishedAt: string;
|
|
79
96
|
}, env?: NodeJS.ProcessEnv, fullResultStatus?: string): IngestPayload;
|
|
@@ -8,6 +8,7 @@ exports.resolveTestStatus = resolveTestStatus;
|
|
|
8
8
|
exports.resolveConfig = resolveConfig;
|
|
9
9
|
exports.shouldPush = shouldPush;
|
|
10
10
|
exports.runStatus = runStatus;
|
|
11
|
+
exports.dedupeByFullTitle = dedupeByFullTitle;
|
|
11
12
|
exports.buildPayload = buildPayload;
|
|
12
13
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
13
14
|
// Optional .env load — MUST NOT crash the suite if dotenv is absent.
|
|
@@ -92,7 +93,64 @@ function runStatus(totals, fullResultStatus) {
|
|
|
92
93
|
return "flaky";
|
|
93
94
|
return "passed";
|
|
94
95
|
}
|
|
95
|
-
|
|
96
|
+
/** Worst-first, so a collapsed row can never look healthier than its parts. */
|
|
97
|
+
const STATUS_SEVERITY = {
|
|
98
|
+
failed: 3,
|
|
99
|
+
flaky: 2,
|
|
100
|
+
passed: 1,
|
|
101
|
+
skipped: 0,
|
|
102
|
+
};
|
|
103
|
+
/**
|
|
104
|
+
* Collapse results that share a `full_title` into one row.
|
|
105
|
+
*
|
|
106
|
+
* TestTracker's ingest holds a unique index on `(run_id, full_title)` and
|
|
107
|
+
* rejects the WHOLE run on a collision, so a duplicate title is not a cosmetic
|
|
108
|
+
* problem — it takes the suite dark. The reporter keys its in-flight map by
|
|
109
|
+
* `test.id`, which is genuinely unique, but a title is not: a Playwright
|
|
110
|
+
* `dependencies:` setup project runs in full in EVERY shard (dependencies are
|
|
111
|
+
* not sharded), so a `merge-reports` payload over a sharded matrix legitimately
|
|
112
|
+
* carries the same setup title once per shard. Capwell's `capwell-web` suite
|
|
113
|
+
* had every nightly rejected this way and never ingested a single run.
|
|
114
|
+
*
|
|
115
|
+
* Collapse rule keeps the merged row honest: worst status wins, the first real
|
|
116
|
+
* error is preserved (so the SDET still sees why it failed), and duration and
|
|
117
|
+
* retries take the max rather than the last-seen value.
|
|
118
|
+
*/
|
|
119
|
+
function dedupeByFullTitle(results) {
|
|
120
|
+
const merged = new Map();
|
|
121
|
+
for (const r of results) {
|
|
122
|
+
const prior = merged.get(r.full_title);
|
|
123
|
+
merged.set(r.full_title, prior ? mergeEntries(prior, r) : { ...r, tags: [...r.tags] });
|
|
124
|
+
}
|
|
125
|
+
return [...merged.values()];
|
|
126
|
+
}
|
|
127
|
+
function worstOf(a, b) {
|
|
128
|
+
return (STATUS_SEVERITY[b] ?? 0) > (STATUS_SEVERITY[a] ?? 0) ? b : a;
|
|
129
|
+
}
|
|
130
|
+
/** `Math.max` treats a missing duration as 0; absent-on-both must stay absent. */
|
|
131
|
+
function longerOf(a, b) {
|
|
132
|
+
if (a === undefined)
|
|
133
|
+
return b;
|
|
134
|
+
if (b === undefined)
|
|
135
|
+
return a;
|
|
136
|
+
return Math.max(a, b);
|
|
137
|
+
}
|
|
138
|
+
function mergeEntries(prior, next) {
|
|
139
|
+
return {
|
|
140
|
+
...prior,
|
|
141
|
+
status: worstOf(prior.status, next.status),
|
|
142
|
+
error_message: prior.error_message ?? next.error_message,
|
|
143
|
+
error_stack: prior.error_stack ?? next.error_stack,
|
|
144
|
+
duration_ms: longerOf(prior.duration_ms, next.duration_ms),
|
|
145
|
+
retries: Math.max(prior.retries, next.retries),
|
|
146
|
+
tags: [...new Set([...prior.tags, ...next.tags])],
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function buildPayload(rawResults, cfg, timing, env = process.env, fullResultStatus) {
|
|
150
|
+
// Deduped before the totals are counted, so `totals` always describes the
|
|
151
|
+
// rows actually sent — a payload whose totals disagree with `results.length`
|
|
152
|
+
// would misreport the run on the dashboard.
|
|
153
|
+
const results = dedupeByFullTitle(rawResults);
|
|
96
154
|
const count = (s) => results.filter((r) => r.status === s).length;
|
|
97
155
|
const passed = count("passed");
|
|
98
156
|
const failed = count("failed");
|