@vitest-agent/ui 2.0.15 → 2.1.0

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.
@@ -4,9 +4,10 @@ import { renderAgentStringAsInk } from "../ink-helpers.js";
4
4
 
5
5
  //#region src/dispatcher/cells/single-project-pass.ts
6
6
  const renderAgent = (inputs) => {
7
- const modules = inputs.state.moduleOrder.length;
8
- const noun = modules === 1 ? "module" : "modules";
9
- return `${[formatTotals(inputs.state), `${modules} ${noun} all-passed.`].join("\n\n")}\n${buildFooter(inputs)}`;
7
+ const { passCount, failCount, skipCount, timeoutCount } = inputs.state.totals;
8
+ if (passCount + failCount + skipCount + timeoutCount === 0) return `${[formatTotals(inputs.state), "0 tests collected. A zero-test run usually means a wrong working directory, a filter that matched nothing, or a load-time error — verify before trusting it."].join("\n\n")}\n${buildFooter(inputs)}`;
9
+ const collected = inputs.state.collectedModules ?? inputs.state.moduleOrder.length;
10
+ return `${(collected === 0 ? [formatTotals(inputs.state)] : [formatTotals(inputs.state), `${collected} ${collected === 1 ? "module" : "modules"} all-passed.`]).join("\n\n")}\n${buildFooter(inputs)}`;
10
11
  };
11
12
  const renderSingleProjectPass = {
12
13
  agent: renderAgent,
@@ -18,7 +18,8 @@ const formatTotals = (state) => {
18
18
  const parts = [`${passCount}/${passCount + failCount + skipCount} passed`];
19
19
  if (failCount > 0) parts.push(`${failCount} failed`);
20
20
  if (skipCount > 0) parts.push(`${skipCount} skipped`);
21
- return `Tests: ${parts.join(", ")} (${formatDisplayDuration(durationMs)})`;
21
+ const suffix = state.collectedModules !== void 0 && state.collectedModules > 0 ? ` across ${state.collectedModules} files` : "";
22
+ return `Tests: ${parts.join(", ")} (${formatDisplayDuration(durationMs)})${suffix}`;
22
23
  };
23
24
  /**
24
25
  * Locate the sole {@link TestRecord} in a `single-test` shape state.
package/index.d.ts CHANGED
@@ -297,6 +297,16 @@ declare const RenderState: Schema.Struct<{
297
297
  readonly detail: Schema.String;
298
298
  readonly targetTool: Schema.optional<Schema.String>;
299
299
  }>>;
300
+ /**
301
+ * Count of every collected module, passing modules included. Folded
302
+ * from `RunFinished.collectedModules`. Renderers prefer this over
303
+ * `moduleOrder.length` for "N modules all-passed" copy — a report
304
+ * replay only queues failing modules, so `moduleOrder` alone
305
+ * undercounts a green run to zero (issue #204). Optional — a run
306
+ * that never carried the field (older replay data, hand-built
307
+ * fixtures) falls back to `moduleOrder.length`.
308
+ */
309
+ readonly collectedModules: Schema.optional<Schema.Number>;
300
310
  }>;
301
311
  /** @public */
302
312
  type RenderState = typeof RenderState.Type;
@@ -443,6 +453,14 @@ declare const AgentReport: Schema.Struct<{
443
453
  readonly failed: Schema.Number;
444
454
  readonly skipped: Schema.Number;
445
455
  readonly duration: Schema.Number;
456
+ /**
457
+ * Count of every collected module, passing modules included.
458
+ * `AgentReport.failed` only carries failing modules, so a green run
459
+ * has no other way to know how many files actually ran — without
460
+ * this a fully-passing replay reads as "0 modules all-passed"
461
+ * (issue #204). Optional to keep existing fixtures/reports valid.
462
+ */
463
+ readonly modules: Schema.optional<Schema.Number>;
446
464
  }>;
447
465
  readonly failed: Schema.$Array<Schema.Struct<{
448
466
  readonly file: Schema.String;
@@ -761,6 +779,14 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
761
779
  readonly skipCount: Schema.Number;
762
780
  readonly durationMs: Schema.Number;
763
781
  readonly timeoutCount: Schema.optional<Schema.Number>;
782
+ /**
783
+ * Count of every collected module, passing modules included.
784
+ * Carries `AgentReport.summary.modules` (or the live module
785
+ * count) into the event stream so the reducer can fold the
786
+ * true count into `RenderState` even when `moduleOrder` only
787
+ * tracks failing modules (report replay). See issue #204.
788
+ */
789
+ readonly collectedModules: Schema.optional<Schema.Number>;
764
790
  }>]>;
765
791
  /** @public */
766
792
  type RunEvent = typeof RunEvent.Type;
@@ -898,6 +924,12 @@ interface VitestTestSuite {
898
924
  line: number;
899
925
  column: number;
900
926
  };
927
+ /**
928
+ * Errors attached to the suite entity itself — a `beforeAll`/`afterAll`
929
+ * hook throw lands here, not on any individual `TestCase`. Optional
930
+ * because older duck-typed callers may not supply it.
931
+ */
932
+ errors?(): Array<VitestModuleError>;
901
933
  }
902
934
  /**
903
935
  * Duck-typed Vitest TestModule from the Reporter v2 API.
@@ -1236,6 +1268,7 @@ declare const RunEventChannel_base: Context.ServiceClass<RunEventChannel, "vites
1236
1268
  readonly skipCount: number;
1237
1269
  readonly durationMs: number;
1238
1270
  readonly timeoutCount?: number | undefined;
1271
+ readonly collectedModules?: number | undefined;
1239
1272
  }>>;
1240
1273
  /**
1241
1274
  * Service tag for the run-event channel. Consumers depend on this tag;
package/index.js CHANGED
@@ -33,7 +33,7 @@ import { ActionSeverity, CoverageFile, CoverageGap, CoverageMetric, CoverageRend
33
33
  *
34
34
  * @public
35
35
  */
36
- const CURRENT_UI_VERSION = "2.0.15";
36
+ const CURRENT_UI_VERSION = "2.1.0";
37
37
 
38
38
  //#endregion
39
39
  export { ActionSeverity, CURRENT_UI_VERSION, CountColumns, CoverageBlock, CoverageFile, CoverageGap, CoverageMetric, CoverageRenderState, DURATION_CELL_WIDTH, FailureRecord, FailureSection, FailuresSection, ModuleHeader, ModuleRecord, ProjectRow, RenderState, RenderTotals, RunEvent, RunEventChannel, RunEventChannelLive, SPINNER_FRAMES, SPINNER_FRAME_MS, SUITE_LOAD_FAILURE_LABEL, StatusIcon, StreamApp, SuggestedActionRecord, SuggestedActions, TagColumns, TestRecord, TestRow, TrendLine, accumulateUntilFinished, buildFooter, classifyOutcome, classifyRunShape, dispatch, dispatchInk, dispatcherTable, dominantClassification, forEachRenderState, formatDisplayDuration, initialRenderState, publish, publishAll, reduceRenderState, reduceRenderStateAll, renderAgent, renderStateStream, spinnerFrame, spinnerFrameForTime, subscribeRaw, synthesizeFromAgentReport, synthesizeRunEvents, tagUnion };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitest-agent/ui",
3
- "version": "2.0.15",
3
+ "version": "2.1.0",
4
4
  "private": false,
5
5
  "description": "Shared event-sourced renderer for vitest-agent. React Ink view for humans, plain-string view for agents, both fed by the same reducer over a RunEvent stream.",
6
6
  "keywords": [
@@ -39,7 +39,7 @@
39
39
  "./package.json": "./package.json"
40
40
  },
41
41
  "dependencies": {
42
- "@vitest-agent/sdk": "2.0.15",
42
+ "@vitest-agent/sdk": "2.1.0",
43
43
  "effect": "4.0.0-beta.107"
44
44
  },
45
45
  "peerDependencies": {
package/reducer.js CHANGED
@@ -218,7 +218,8 @@ const reduceRenderState = (state, event) => {
218
218
  skipCount: e.skipCount,
219
219
  timeoutCount: e.timeoutCount ?? state.totals.timeoutCount,
220
220
  durationMs: e.durationMs
221
- }
221
+ },
222
+ ...e.collectedModules !== void 0 && { collectedModules: e.collectedModules }
222
223
  }),
223
224
  RunTimedOut: () => ({
224
225
  ...state,
package/render-agent.js CHANGED
@@ -17,11 +17,14 @@ const formatHeader = (state) => {
17
17
  };
18
18
  const formatModulesSection = (state) => {
19
19
  const modules = state.moduleOrder.map((path) => state.modules[path]).filter((m) => m !== void 0);
20
- if (modules.length === 0) return null;
21
- if (modules.filter((m) => m.failCount > 0 || m.skipCount > 0).length === 0) {
22
- const noun = modules.length === 1 ? "module" : "modules";
23
- return `${modules.length} ${noun} all-passed.`;
20
+ const { passCount, failCount, skipCount, timeoutCount } = state.totals;
21
+ if (passCount + failCount + skipCount + timeoutCount === 0) return "0 tests collected. A zero-test run usually means a wrong working directory, a filter that matched nothing, or a load-time error — verify before trusting it.";
22
+ const collected = state.collectedModules ?? modules.length;
23
+ if (modules.length === 0) {
24
+ if (collected === 0) return null;
25
+ return `${collected} ${collected === 1 ? "module" : "modules"} all-passed.`;
24
26
  }
27
+ if (modules.filter((m) => m.failCount > 0 || m.skipCount > 0).length === 0) return `${collected} ${collected === 1 ? "module" : "modules"} all-passed.`;
25
28
  const lines = ["Modules:"];
26
29
  for (const m of modules) {
27
30
  const parts = [];
package/synthesize.js CHANGED
@@ -180,7 +180,8 @@ const synthesizeRunEvents = (modules, options = {}) => {
180
180
  failCount: totalFail,
181
181
  skipCount: totalSkip,
182
182
  durationMs: totalDuration,
183
- ...totalTimeout > 0 && { timeoutCount: totalTimeout }
183
+ ...totalTimeout > 0 && { timeoutCount: totalTimeout },
184
+ collectedModules: modules.length
184
185
  });
185
186
  return events;
186
187
  };
@@ -360,7 +361,8 @@ const synthesizeFromAgentReport = (report, options = {}) => {
360
361
  failCount: report.summary.failed + suiteFailureCount,
361
362
  skipCount: report.summary.skipped,
362
363
  durationMs: report.summary.duration,
363
- ...totalTimeoutCount > 0 && { timeoutCount: totalTimeoutCount }
364
+ ...totalTimeoutCount > 0 && { timeoutCount: totalTimeoutCount },
365
+ ...report.summary.modules !== void 0 && { collectedModules: report.summary.modules }
364
366
  });
365
367
  return events;
366
368
  };