@elench/testkit 0.1.86 → 0.1.88
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 +19 -12
- package/lib/cli/agents/providers/claude.mjs +1 -1
- package/lib/cli/agents/providers/codex.mjs +1 -1
- package/lib/cli/assistant/prompt-builder.mjs +78 -0
- package/lib/cli/assistant/protocol.mjs +67 -0
- package/lib/cli/assistant/session.mjs +92 -0
- package/lib/cli/assistant/slash-commands.mjs +160 -0
- package/lib/cli/assistant/state.mjs +279 -0
- package/lib/cli/assistant/tool-registry.mjs +236 -0
- package/lib/cli/assistant/tool-run-reporter.mjs +80 -0
- package/lib/cli/command-helpers.mjs +40 -24
- package/lib/cli/commands/assistant.mjs +84 -0
- package/lib/cli/entrypoint.mjs +37 -11
- package/lib/cli/presentation/tree-reporter.mjs +34 -28
- package/lib/cli/tui/assistant-app.mjs +131 -0
- package/lib/cli/tui/detail-pane.mjs +161 -0
- package/lib/cli/tui/filter-bar.mjs +12 -0
- package/lib/cli/tui/fuzzy-match.mjs +106 -0
- package/lib/cli/tui/inspect-app.mjs +306 -0
- package/lib/cli/tui/inspect-artifact-adapter.mjs +3 -0
- package/lib/cli/tui/inspect-live-adapter.mjs +15 -0
- package/lib/cli/tui/inspect-model.mjs +817 -0
- package/lib/cli/tui/inspect-state.mjs +321 -0
- 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 +6 -6
- package/lib/cli/commands/artifacts.mjs +0 -45
- package/lib/cli/commands/investigate.mjs +0 -87
- package/lib/cli/commands/logs.mjs +0 -47
- package/lib/cli/commands/show.mjs +0 -47
- package/lib/cli/commands/watch.mjs +0 -23
- package/lib/cli/tui/run-app.mjs +0 -1
- package/lib/cli/tui/run-session-app.mjs +0 -432
- package/lib/cli/tui/run-session-state.mjs +0 -505
- package/lib/cli/tui/run-tree-state.mjs +0 -1
- package/lib/cli/tui/watch-app.mjs +0 -220
|
@@ -1,505 +0,0 @@
|
|
|
1
|
-
import { fileDisplayName } from "../../discovery/index.mjs";
|
|
2
|
-
import { suiteSelectionType } from "../../runner/suite-selection.mjs";
|
|
3
|
-
import { buildRunSummaryData } from "../../runner/formatting.mjs";
|
|
4
|
-
|
|
5
|
-
export function buildFailureKey(serviceName, filePath) {
|
|
6
|
-
return `${serviceName}::${filePath}`;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function parseFailureKey(failureKey) {
|
|
10
|
-
const separator = String(failureKey || "").indexOf("::");
|
|
11
|
-
if (separator <= 0) return { serviceName: null, filePath: null };
|
|
12
|
-
return {
|
|
13
|
-
serviceName: failureKey.slice(0, separator),
|
|
14
|
-
filePath: failureKey.slice(separator + 2),
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function createRunSessionState() {
|
|
19
|
-
const services = new Map();
|
|
20
|
-
let totalCount = 0;
|
|
21
|
-
let completedCount = 0;
|
|
22
|
-
let phase = null;
|
|
23
|
-
let finished = false;
|
|
24
|
-
let summaryData = null;
|
|
25
|
-
let regressionCatalog = null;
|
|
26
|
-
let mode = "running";
|
|
27
|
-
let notice = null;
|
|
28
|
-
let selectedFailureKey = null;
|
|
29
|
-
let agentSession = null;
|
|
30
|
-
const listeners = new Set();
|
|
31
|
-
|
|
32
|
-
function notify() {
|
|
33
|
-
for (const callback of listeners) callback();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function getOrCreateService(serviceName) {
|
|
37
|
-
if (!services.has(serviceName)) {
|
|
38
|
-
services.set(serviceName, { name: serviceName, types: new Map(), skipped: false, skipReason: null });
|
|
39
|
-
}
|
|
40
|
-
return services.get(serviceName);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function getOrCreateType(service, displayType) {
|
|
44
|
-
if (!service.types.has(displayType)) {
|
|
45
|
-
service.types.set(displayType, { type: displayType, suites: new Map() });
|
|
46
|
-
}
|
|
47
|
-
return service.types.get(displayType);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function getOrCreateSuite(typeNode, suiteKey, suiteMeta) {
|
|
51
|
-
if (!typeNode.suites.has(suiteKey)) {
|
|
52
|
-
typeNode.suites.set(suiteKey, {
|
|
53
|
-
name: suiteMeta.name,
|
|
54
|
-
groupLabel: suiteMeta.groupLabel,
|
|
55
|
-
framework: suiteMeta.framework,
|
|
56
|
-
files: new Map(),
|
|
57
|
-
});
|
|
58
|
-
}
|
|
59
|
-
return typeNode.suites.get(suiteKey);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function findFile(serviceName, suiteKey, filePath) {
|
|
63
|
-
const service = services.get(serviceName);
|
|
64
|
-
if (!service) return null;
|
|
65
|
-
for (const typeNode of service.types.values()) {
|
|
66
|
-
const suite = typeNode.suites.get(suiteKey);
|
|
67
|
-
if (!suite) continue;
|
|
68
|
-
const file = suite.files.get(filePath);
|
|
69
|
-
if (file) return file;
|
|
70
|
-
}
|
|
71
|
-
return null;
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function findFileByServiceAndPath(serviceName, filePath) {
|
|
75
|
-
const service = services.get(serviceName);
|
|
76
|
-
if (!service) return null;
|
|
77
|
-
for (const typeNode of service.types.values()) {
|
|
78
|
-
for (const suite of typeNode.suites.values()) {
|
|
79
|
-
const file = suite.files.get(filePath);
|
|
80
|
-
if (file) return file;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function collectFailedFiles() {
|
|
87
|
-
const failures = [];
|
|
88
|
-
for (const service of services.values()) {
|
|
89
|
-
for (const typeNode of service.types.values()) {
|
|
90
|
-
for (const [suiteKey, suite] of typeNode.suites) {
|
|
91
|
-
for (const file of suite.files.values()) {
|
|
92
|
-
if (file.status !== "failed") continue;
|
|
93
|
-
failures.push({
|
|
94
|
-
key: buildFailureKey(service.name, file.path),
|
|
95
|
-
serviceName: service.name,
|
|
96
|
-
suiteKey,
|
|
97
|
-
suiteName: suite.name,
|
|
98
|
-
displayType: typeNode.type,
|
|
99
|
-
framework: suite.framework,
|
|
100
|
-
filePath: file.path,
|
|
101
|
-
displayName: file.displayName,
|
|
102
|
-
error: file.error,
|
|
103
|
-
failureDetails: file.failureDetails,
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
return failures.sort(
|
|
110
|
-
(left, right) =>
|
|
111
|
-
left.serviceName.localeCompare(right.serviceName) || left.filePath.localeCompare(right.filePath)
|
|
112
|
-
);
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function ensureSelectedFailure() {
|
|
116
|
-
const failures = collectFailedFiles();
|
|
117
|
-
if (failures.length === 0) {
|
|
118
|
-
selectedFailureKey = null;
|
|
119
|
-
return;
|
|
120
|
-
}
|
|
121
|
-
if (selectedFailureKey && failures.some((failure) => failure.key === selectedFailureKey)) return;
|
|
122
|
-
selectedFailureKey = failures[0].key;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
function appendTranscriptEntry(kind, text) {
|
|
126
|
-
if (!agentSession || !text) return;
|
|
127
|
-
const normalizedText = String(text);
|
|
128
|
-
const entries = agentSession.transcriptEntries || [];
|
|
129
|
-
const lastEntry = entries.at(-1);
|
|
130
|
-
if (kind === "assistant" && lastEntry?.kind === "assistant") {
|
|
131
|
-
lastEntry.text += normalizedText;
|
|
132
|
-
agentSession.updatedAt = Date.now();
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
entries.push({ kind, text: normalizedText });
|
|
136
|
-
agentSession.transcriptEntries = entries;
|
|
137
|
-
agentSession.updatedAt = Date.now();
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function finishAgentSession(status, extra = {}) {
|
|
141
|
-
if (!agentSession) return;
|
|
142
|
-
agentSession = {
|
|
143
|
-
...agentSession,
|
|
144
|
-
...extra,
|
|
145
|
-
status,
|
|
146
|
-
endedAt: Date.now(),
|
|
147
|
-
};
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
initFromPlans(servicePlans) {
|
|
152
|
-
for (const plan of servicePlans) {
|
|
153
|
-
const serviceName = plan.config.name;
|
|
154
|
-
const service = getOrCreateService(serviceName);
|
|
155
|
-
|
|
156
|
-
if (plan.skipped) {
|
|
157
|
-
service.skipped = true;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
for (const suite of plan.suites) {
|
|
162
|
-
const displayType = suite.displayType || suiteSelectionType(suite.type, suite.framework);
|
|
163
|
-
const typeNode = getOrCreateType(service, displayType);
|
|
164
|
-
const suiteKey = `${displayType}:${suite.name}`;
|
|
165
|
-
const suiteNode = getOrCreateSuite(typeNode, suiteKey, {
|
|
166
|
-
name: suite.name,
|
|
167
|
-
groupLabel: fileDisplayName(suite.name),
|
|
168
|
-
framework: suite.framework,
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
for (const filePath of suite.files) {
|
|
172
|
-
if (suiteNode.files.has(filePath)) continue;
|
|
173
|
-
suiteNode.files.set(filePath, {
|
|
174
|
-
path: filePath,
|
|
175
|
-
displayName: fileDisplayName(filePath),
|
|
176
|
-
status: "pending",
|
|
177
|
-
durationMs: null,
|
|
178
|
-
error: null,
|
|
179
|
-
failureDetails: null,
|
|
180
|
-
skipReason: null,
|
|
181
|
-
});
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
ensureSelectedFailure();
|
|
186
|
-
notify();
|
|
187
|
-
},
|
|
188
|
-
|
|
189
|
-
setRegressionCatalog(document) {
|
|
190
|
-
regressionCatalog = document;
|
|
191
|
-
notify();
|
|
192
|
-
},
|
|
193
|
-
|
|
194
|
-
markFileRunning(serviceName, suiteKey, filePath) {
|
|
195
|
-
const file = findFile(serviceName, suiteKey, filePath) || findFileByServiceAndPath(serviceName, filePath);
|
|
196
|
-
if (!file) return;
|
|
197
|
-
file.status = "running";
|
|
198
|
-
notify();
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
markFileFinished(task, outcome) {
|
|
202
|
-
const suiteKey = `${task.displayType || suiteSelectionType(task.type, task.framework)}:${task.suiteName}`;
|
|
203
|
-
const file = findFile(task.serviceName, suiteKey, task.file) || findFileByServiceAndPath(task.serviceName, task.file);
|
|
204
|
-
if (!file) return;
|
|
205
|
-
if (outcome.status === "skipped") {
|
|
206
|
-
file.status = "skipped";
|
|
207
|
-
file.skipReason = outcome.reason || null;
|
|
208
|
-
} else if (outcome.status === "not_run") {
|
|
209
|
-
file.status = "not_run";
|
|
210
|
-
file.skipReason = outcome.reason || null;
|
|
211
|
-
} else if (outcome.failed) {
|
|
212
|
-
file.status = "failed";
|
|
213
|
-
file.error = outcome.error || null;
|
|
214
|
-
file.failureDetails = outcome.failureDetails || null;
|
|
215
|
-
} else {
|
|
216
|
-
file.status = "passed";
|
|
217
|
-
}
|
|
218
|
-
file.durationMs = outcome.durationMs || null;
|
|
219
|
-
completedCount += 1;
|
|
220
|
-
ensureSelectedFailure();
|
|
221
|
-
notify();
|
|
222
|
-
},
|
|
223
|
-
|
|
224
|
-
markServiceSkipped(serviceName, reason) {
|
|
225
|
-
const service = getOrCreateService(serviceName);
|
|
226
|
-
service.skipped = true;
|
|
227
|
-
service.skipReason = reason || null;
|
|
228
|
-
notify();
|
|
229
|
-
},
|
|
230
|
-
|
|
231
|
-
markPlannedSkip(entry) {
|
|
232
|
-
const file = findFileByServiceAndPath(entry.serviceName, entry.file);
|
|
233
|
-
if (!file) return;
|
|
234
|
-
file.status = "skipped";
|
|
235
|
-
file.skipReason = entry.reason || null;
|
|
236
|
-
completedCount += 1;
|
|
237
|
-
notify();
|
|
238
|
-
},
|
|
239
|
-
|
|
240
|
-
markRuntimeError(task, message) {
|
|
241
|
-
const file = findFileByServiceAndPath(task.serviceName, task.file);
|
|
242
|
-
if (!file) return;
|
|
243
|
-
file.status = "failed";
|
|
244
|
-
file.error = message || "runtime error";
|
|
245
|
-
completedCount += 1;
|
|
246
|
-
ensureSelectedFailure();
|
|
247
|
-
notify();
|
|
248
|
-
},
|
|
249
|
-
|
|
250
|
-
setTotalFileCount(count) {
|
|
251
|
-
totalCount = count;
|
|
252
|
-
notify();
|
|
253
|
-
},
|
|
254
|
-
|
|
255
|
-
setPhase(label) {
|
|
256
|
-
phase = label;
|
|
257
|
-
notify();
|
|
258
|
-
},
|
|
259
|
-
|
|
260
|
-
finish(results, durationMs, regressionReport) {
|
|
261
|
-
finished = true;
|
|
262
|
-
mode = "complete";
|
|
263
|
-
const summary = buildRunSummaryData(results, durationMs, regressionReport);
|
|
264
|
-
const rows = [
|
|
265
|
-
["Result", summary.result],
|
|
266
|
-
["Passed", String(summary.passed)],
|
|
267
|
-
["Failed", String(summary.failed)],
|
|
268
|
-
["Skipped", String(summary.skipped)],
|
|
269
|
-
["Not run", String(summary.notRun)],
|
|
270
|
-
["Files", String(summary.files)],
|
|
271
|
-
["Duration", summary.duration],
|
|
272
|
-
];
|
|
273
|
-
if (summary.serviceErrors > 0) rows.push(["Runtime errors", String(summary.serviceErrors)]);
|
|
274
|
-
if (summary.newRegressions > 0) rows.push(["New regressions", String(summary.newRegressions)]);
|
|
275
|
-
if (summary.knownRegressions > 0) rows.push(["Known regressions", String(summary.knownRegressions)]);
|
|
276
|
-
if (summary.fixedKnownRegressions > 0) rows.push(["Fixed known", String(summary.fixedKnownRegressions)]);
|
|
277
|
-
if (summary.catalogStale > 0) rows.push(["Catalog stale", String(summary.catalogStale)]);
|
|
278
|
-
summaryData = { rows, result: summary.result };
|
|
279
|
-
ensureSelectedFailure();
|
|
280
|
-
notify();
|
|
281
|
-
},
|
|
282
|
-
|
|
283
|
-
setNotice(message) {
|
|
284
|
-
notice = message ? String(message) : null;
|
|
285
|
-
notify();
|
|
286
|
-
},
|
|
287
|
-
|
|
288
|
-
clearNotice() {
|
|
289
|
-
if (!notice) return;
|
|
290
|
-
notice = null;
|
|
291
|
-
notify();
|
|
292
|
-
},
|
|
293
|
-
|
|
294
|
-
selectNextFailure() {
|
|
295
|
-
const failures = collectFailedFiles();
|
|
296
|
-
if (failures.length === 0) return;
|
|
297
|
-
const currentIndex = failures.findIndex((failure) => failure.key === selectedFailureKey);
|
|
298
|
-
const nextIndex = currentIndex < 0 ? 0 : (currentIndex + 1) % failures.length;
|
|
299
|
-
selectedFailureKey = failures[nextIndex].key;
|
|
300
|
-
notify();
|
|
301
|
-
},
|
|
302
|
-
|
|
303
|
-
selectPreviousFailure() {
|
|
304
|
-
const failures = collectFailedFiles();
|
|
305
|
-
if (failures.length === 0) return;
|
|
306
|
-
const currentIndex = failures.findIndex((failure) => failure.key === selectedFailureKey);
|
|
307
|
-
const nextIndex = currentIndex < 0 ? failures.length - 1 : (currentIndex - 1 + failures.length) % failures.length;
|
|
308
|
-
selectedFailureKey = failures[nextIndex].key;
|
|
309
|
-
notify();
|
|
310
|
-
},
|
|
311
|
-
|
|
312
|
-
selectFailure(failureKey) {
|
|
313
|
-
selectedFailureKey = failureKey || null;
|
|
314
|
-
ensureSelectedFailure();
|
|
315
|
-
notify();
|
|
316
|
-
},
|
|
317
|
-
|
|
318
|
-
beginInvestigation({ provider, userMessage } = {}) {
|
|
319
|
-
mode = "investigating";
|
|
320
|
-
notice = null;
|
|
321
|
-
agentSession = {
|
|
322
|
-
provider: provider || "auto",
|
|
323
|
-
userMessage: userMessage || "",
|
|
324
|
-
status: "starting",
|
|
325
|
-
startedAt: Date.now(),
|
|
326
|
-
updatedAt: Date.now(),
|
|
327
|
-
rawEvents: [],
|
|
328
|
-
transcriptEntries: [],
|
|
329
|
-
timeline: [],
|
|
330
|
-
summary: null,
|
|
331
|
-
activePhase: "planning",
|
|
332
|
-
activeStep: null,
|
|
333
|
-
viewMode: "summary",
|
|
334
|
-
};
|
|
335
|
-
notify();
|
|
336
|
-
},
|
|
337
|
-
|
|
338
|
-
recordInvestigationProgress(event, presentation = null) {
|
|
339
|
-
if (!agentSession || !event) return;
|
|
340
|
-
agentSession.rawEvents.push({ ...event });
|
|
341
|
-
if (event.type === "start") {
|
|
342
|
-
agentSession.status = "running";
|
|
343
|
-
} else if (event.type === "delta") {
|
|
344
|
-
appendTranscriptEntry("assistant", event.text || "");
|
|
345
|
-
} else if (event.type === "final") {
|
|
346
|
-
if (event.text && !(agentSession.transcriptEntries || []).some((entry) => entry.kind === "assistant")) {
|
|
347
|
-
appendTranscriptEntry("assistant", event.text);
|
|
348
|
-
}
|
|
349
|
-
agentSession.finalText = event.text || agentSession.finalText || "";
|
|
350
|
-
} else if (event.type === "tool") {
|
|
351
|
-
appendTranscriptEntry("tool", event.detail ? `${event.name}: ${event.detail}` : event.name);
|
|
352
|
-
} else if (event.type === "status") {
|
|
353
|
-
appendTranscriptEntry("status", event.message || "");
|
|
354
|
-
} else if (event.type === "error") {
|
|
355
|
-
appendTranscriptEntry("error", event.message || "Agent error");
|
|
356
|
-
} else if (event.type === "exit") {
|
|
357
|
-
agentSession.exitCode = event.code;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
if (presentation) {
|
|
361
|
-
agentSession.activePhase = presentation.phase || agentSession.activePhase || null;
|
|
362
|
-
agentSession.activeStep = presentation.activeStep || null;
|
|
363
|
-
agentSession.timeline = presentation.timeline || [];
|
|
364
|
-
agentSession.summary = presentation.summary || null;
|
|
365
|
-
}
|
|
366
|
-
notify();
|
|
367
|
-
},
|
|
368
|
-
|
|
369
|
-
appendAgentEvent(event) {
|
|
370
|
-
this.recordInvestigationProgress(event);
|
|
371
|
-
},
|
|
372
|
-
|
|
373
|
-
toggleInvestigationViewMode() {
|
|
374
|
-
if (!agentSession) return;
|
|
375
|
-
agentSession.viewMode = agentSession.viewMode === "summary" ? "transcript" : "summary";
|
|
376
|
-
notify();
|
|
377
|
-
},
|
|
378
|
-
|
|
379
|
-
completeAgentSession(result = {}) {
|
|
380
|
-
finishAgentSession("complete", {
|
|
381
|
-
finalText: result.finalText || agentSession?.finalText || "",
|
|
382
|
-
exitCode: result.exitCode ?? agentSession?.exitCode ?? 0,
|
|
383
|
-
});
|
|
384
|
-
notify();
|
|
385
|
-
},
|
|
386
|
-
|
|
387
|
-
failAgentSession(error) {
|
|
388
|
-
finishAgentSession("error", {
|
|
389
|
-
error: error instanceof Error ? error.message : String(error || "Agent error"),
|
|
390
|
-
});
|
|
391
|
-
notify();
|
|
392
|
-
},
|
|
393
|
-
|
|
394
|
-
cancelAgentSession(message = "Cancelled investigation.") {
|
|
395
|
-
finishAgentSession("cancelled");
|
|
396
|
-
mode = "complete";
|
|
397
|
-
notice = message;
|
|
398
|
-
notify();
|
|
399
|
-
},
|
|
400
|
-
|
|
401
|
-
returnToSummary() {
|
|
402
|
-
mode = "complete";
|
|
403
|
-
notify();
|
|
404
|
-
},
|
|
405
|
-
|
|
406
|
-
subscribe(callback) {
|
|
407
|
-
listeners.add(callback);
|
|
408
|
-
return () => listeners.delete(callback);
|
|
409
|
-
},
|
|
410
|
-
|
|
411
|
-
getSnapshot() {
|
|
412
|
-
const serviceSnapshots = [];
|
|
413
|
-
for (const service of services.values()) {
|
|
414
|
-
if (service.skipped) {
|
|
415
|
-
serviceSnapshots.push({
|
|
416
|
-
name: service.name,
|
|
417
|
-
skipped: true,
|
|
418
|
-
skipReason: service.skipReason,
|
|
419
|
-
types: [],
|
|
420
|
-
});
|
|
421
|
-
continue;
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
const typeSnapshots = [];
|
|
425
|
-
for (const typeNode of service.types.values()) {
|
|
426
|
-
const suiteSnapshots = [];
|
|
427
|
-
let typeAllCollapsed = true;
|
|
428
|
-
|
|
429
|
-
for (const [suiteKey, suite] of typeNode.suites) {
|
|
430
|
-
const files = [...suite.files.values()];
|
|
431
|
-
const allPassed = files.length > 0 && files.every((file) => file.status === "passed");
|
|
432
|
-
const allSkipped = files.length > 0 && files.every((file) => file.status === "skipped");
|
|
433
|
-
const anyFailed = files.some((file) => file.status === "failed");
|
|
434
|
-
const anyRunning = files.some((file) => file.status === "running");
|
|
435
|
-
const anyPending = files.some((file) => file.status === "pending");
|
|
436
|
-
|
|
437
|
-
let collapsed = false;
|
|
438
|
-
let collapseStatus = null;
|
|
439
|
-
let visibleFiles = files;
|
|
440
|
-
|
|
441
|
-
if (allPassed) {
|
|
442
|
-
collapsed = true;
|
|
443
|
-
collapseStatus = "all_passed";
|
|
444
|
-
visibleFiles = [];
|
|
445
|
-
} else if (allSkipped) {
|
|
446
|
-
collapsed = true;
|
|
447
|
-
collapseStatus = "all_skipped";
|
|
448
|
-
visibleFiles = [];
|
|
449
|
-
} else if (anyFailed && !anyRunning && !anyPending) {
|
|
450
|
-
visibleFiles = files.filter((file) => file.status === "failed" || file.status === "not_run");
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
if (!collapsed) {
|
|
454
|
-
typeAllCollapsed = false;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
suiteSnapshots.push({
|
|
458
|
-
key: suiteKey,
|
|
459
|
-
name: suite.name,
|
|
460
|
-
groupLabel: suite.groupLabel,
|
|
461
|
-
framework: suite.framework,
|
|
462
|
-
collapsed,
|
|
463
|
-
collapseStatus,
|
|
464
|
-
fileCount: files.length,
|
|
465
|
-
passedCount: files.filter((file) => file.status === "passed").length,
|
|
466
|
-
totalDurationMs: files.reduce((sum, file) => sum + (file.durationMs || 0), 0),
|
|
467
|
-
visibleFiles,
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
typeSnapshots.push({
|
|
472
|
-
type: typeNode.type,
|
|
473
|
-
collapsed: typeAllCollapsed,
|
|
474
|
-
suites: suiteSnapshots,
|
|
475
|
-
});
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
serviceSnapshots.push({
|
|
479
|
-
name: service.name,
|
|
480
|
-
skipped: false,
|
|
481
|
-
skipReason: null,
|
|
482
|
-
types: typeSnapshots,
|
|
483
|
-
});
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
const failures = collectFailedFiles();
|
|
487
|
-
const selectedFailure = failures.find((failure) => failure.key === selectedFailureKey) || null;
|
|
488
|
-
return {
|
|
489
|
-
services: serviceSnapshots,
|
|
490
|
-
completedCount,
|
|
491
|
-
totalCount,
|
|
492
|
-
phase,
|
|
493
|
-
finished,
|
|
494
|
-
summaryData,
|
|
495
|
-
regressionCatalog,
|
|
496
|
-
mode,
|
|
497
|
-
notice,
|
|
498
|
-
failures,
|
|
499
|
-
selectedFailureKey,
|
|
500
|
-
selectedFailure,
|
|
501
|
-
agentSession,
|
|
502
|
-
};
|
|
503
|
-
},
|
|
504
|
-
};
|
|
505
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { createRunSessionState as createRunTreeState } from "./run-session-state.mjs";
|