@vitest-agent/reporter 2.2.4 → 3.0.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.
@@ -1,6 +1,6 @@
1
1
  import { renderGithubLog, toDisplayPath } from "./githubLog.js";
2
2
  import { createLiveInk } from "./LiveInkRenderer.js";
3
- import { countSuiteFailures, isTimeoutError } from "@vitest-agent/sdk";
3
+ import { RUN_REPORT_FILE_SCHEMA_URL, countSuiteFailures, isTimeoutError } from "@vitest-agent/sdk";
4
4
  import { classifyOutcome, classifyRunShape, dispatch, dispatcherTable, reduceRenderStateAll, synthesizeFromAgentReport } from "@vitest-agent/ui";
5
5
  import { Effect, PubSub } from "effect";
6
6
 
@@ -199,28 +199,99 @@ const renderTrendSection = (trendSummary) => {
199
199
  }
200
200
  return lines.join("\n");
201
201
  };
202
+ const formatSummaryDuration = (ms) => ms >= 1e3 ? `${(ms / 1e3).toFixed(1)}s` : `${Math.round(ms)}ms`;
202
203
  /**
203
- * Builds the vitest-agent GitHub step-summary payload — ONE `RenderedOutput`
204
- * for the whole run, carrying only data Vitest's own built-in
205
- * `github-actions` reporter cannot know: test classifications (new-failure /
206
- * persistent / flaky / recovered), coverage-target shortfalls, and the
207
- * coverage trend. Vitest's reporter already writes pass/fail/skip counts
208
- * and a flaky-tests section to the same `$GITHUB_STEP_SUMMARY` file, so
209
- * duplicating that per-project breakdown here would be redundant noise
210
- * multiplied across every project in a workspace. Returns an empty array
211
- * when all three sections would be empty — a clean run should not leave a
212
- * bare heading in the job summary.
204
+ * Per-project pass/fail/timeout/skip/duration table — the unconditional
205
+ * half of the summary body.
206
+ *
207
+ * Vitest's own `github-actions` reporter no longer writes these counts
208
+ * to the step summary (its job-summary half is disabled), so this table
209
+ * is the only place a CI reader sees the totals. A multi-project run
210
+ * gets a trailing `Total` row; a single-project run does not, because
211
+ * the row would just repeat the one above it.
212
+ *
213
+ * Every count comes from `summarizeProject`, the same projection the
214
+ * console surfaces render from — so the table cannot disagree with what
215
+ * the terminal printed. That matters most for timeouts:
216
+ * `summarizeProject` folds suite-level (collection/load) failures INTO
217
+ * `failCount` and pulls timed-out tests back OUT of it, reporting them
218
+ * in their own column. Recomputing from `report.summary.failed` here
219
+ * would show a timed-out test as `Failed: 1` while every console surface
220
+ * says `0 failed, 1 timed out`.
221
+ *
222
+ * @internal
213
223
  */
214
- const renderGithubSummary = (input) => {
224
+ const renderTotalsSection = (reports) => {
225
+ const rows = reports.map((report) => summarizeProject(report));
226
+ const cells = (name, passed, failed, timedOut, skipped, duration) => `| ${name} | ${passed} | ${failed} | ${timedOut} | ${skipped} | ${formatSummaryDuration(duration)} |`;
227
+ const lines = [
228
+ "### Totals",
229
+ "",
230
+ "| Project | Passed | Failed | Timed out | Skipped | Duration |",
231
+ "| --- | --- | --- | --- | --- | --- |",
232
+ ...rows.map((r) => cells(r.name, r.passCount, r.failCount, r.timeoutCount ?? 0, r.skipCount, r.durationMs))
233
+ ];
234
+ if (rows.length > 1) {
235
+ const total = rows.reduce((acc, r) => ({
236
+ passed: acc.passed + r.passCount,
237
+ failed: acc.failed + r.failCount,
238
+ timedOut: acc.timedOut + (r.timeoutCount ?? 0),
239
+ skipped: acc.skipped + r.skipCount,
240
+ duration: acc.duration + r.durationMs
241
+ }), {
242
+ passed: 0,
243
+ failed: 0,
244
+ timedOut: 0,
245
+ skipped: 0,
246
+ duration: 0
247
+ });
248
+ lines.push(cells("**Total**", total.passed, total.failed, total.timedOut, total.skipped, total.duration));
249
+ }
250
+ return lines.join("\n");
251
+ };
252
+ /**
253
+ * Assemble the vitest-agent markdown body shared by the GitHub step
254
+ * summary and the `summary.md` report file: a `## vitest-agent` heading,
255
+ * the always-present per-project totals table, then whichever of the
256
+ * classification, coverage, and trend sections have content.
257
+ *
258
+ * Always returns markdown. An all-green run still gets a body — with
259
+ * Vitest's own job summary disabled, returning `null` here left the step
260
+ * summary blank on every passing CI run and wrote no `summary.md` at
261
+ * all.
262
+ *
263
+ * @internal
264
+ */
265
+ const buildSummaryMarkdown = (input) => {
215
266
  const sections = [
216
267
  renderClassificationsSection(input.classifications),
217
268
  renderCoverageSection(input.reports),
218
269
  renderTrendSection(input.trendSummary)
219
270
  ].filter((section) => section !== null);
220
- if (sections.length === 0) return [];
271
+ return [
272
+ "## vitest-agent",
273
+ renderTotalsSection(input.reports),
274
+ ...sections
275
+ ].join("\n\n");
276
+ };
277
+ /**
278
+ * Builds the vitest-agent GitHub step-summary payload — ONE `RenderedOutput`
279
+ * for the whole run: the per-project totals table, plus test
280
+ * classifications (new-failure / persistent / flaky / recovered),
281
+ * coverage-target shortfalls, and the coverage trend when those have
282
+ * content.
283
+ *
284
+ * The totals used to be omitted on the theory that Vitest's own
285
+ * `github-actions` reporter already wrote pass/fail/skip counts to the
286
+ * same `$GITHUB_STEP_SUMMARY` file. That is no longer true — its
287
+ * job-summary half is disabled — so this block is the only summary a CI
288
+ * reader gets, and it is always emitted. A green run gets a totals table
289
+ * rather than nothing at all.
290
+ */
291
+ const renderGithubSummary = (input) => {
221
292
  return [{
222
293
  target: "github-summary",
223
- content: `\n${["## vitest-agent", ...sections].join("\n\n")}\n`,
294
+ content: `\n${buildSummaryMarkdown(input)}\n`,
224
295
  contentType: "text/markdown"
225
296
  }];
226
297
  };
@@ -287,6 +358,23 @@ const DefaultVitestAgentReporter = (kit) => {
287
358
  out.push(...renderGithubSummary(input));
288
359
  out.push(renderGithubLog(input, renderKit));
289
360
  }
361
+ out.push({
362
+ target: "report",
363
+ filename: "run.json",
364
+ contentType: "application/json",
365
+ content: `${JSON.stringify({
366
+ $schema: RUN_REPORT_FILE_SCHEMA_URL,
367
+ schemaVersion: 1,
368
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
369
+ reports: input.reports
370
+ }, null, 2)}\n`
371
+ });
372
+ out.push({
373
+ target: "report",
374
+ filename: "summary.md",
375
+ contentType: "text/markdown",
376
+ content: `${buildSummaryMarkdown(input)}\n`
377
+ });
290
378
  return out;
291
379
  } };
292
380
  };
package/index.d.ts CHANGED
@@ -365,6 +365,12 @@ declare const AgentReport: Schema.Struct<{
365
365
  readonly functions: Schema.optional<Schema.Number>;
366
366
  readonly branches: Schema.optional<Schema.Number>;
367
367
  readonly statements: Schema.optional<Schema.Number>;
368
+ readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
369
+ readonly lines: Schema.optional<Schema.Number>;
370
+ readonly functions: Schema.optional<Schema.Number>;
371
+ readonly branches: Schema.optional<Schema.Number>;
372
+ readonly statements: Schema.optional<Schema.Number>;
373
+ }>]>>;
368
374
  }>]>>, never>;
369
375
  }>;
370
376
  readonly targets: Schema.optional<Schema.Struct<{
@@ -379,6 +385,12 @@ declare const AgentReport: Schema.Struct<{
379
385
  readonly functions: Schema.optional<Schema.Number>;
380
386
  readonly branches: Schema.optional<Schema.Number>;
381
387
  readonly statements: Schema.optional<Schema.Number>;
388
+ readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
389
+ readonly lines: Schema.optional<Schema.Number>;
390
+ readonly functions: Schema.optional<Schema.Number>;
391
+ readonly branches: Schema.optional<Schema.Number>;
392
+ readonly statements: Schema.optional<Schema.Number>;
393
+ }>]>>;
382
394
  }>]>>, never>;
383
395
  }>>;
384
396
  readonly baselines: Schema.optional<Schema.Struct<{
@@ -393,6 +405,12 @@ declare const AgentReport: Schema.Struct<{
393
405
  readonly functions: Schema.optional<Schema.Number>;
394
406
  readonly branches: Schema.optional<Schema.Number>;
395
407
  readonly statements: Schema.optional<Schema.Number>;
408
+ readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
409
+ readonly lines: Schema.optional<Schema.Number>;
410
+ readonly functions: Schema.optional<Schema.Number>;
411
+ readonly branches: Schema.optional<Schema.Number>;
412
+ readonly statements: Schema.optional<Schema.Number>;
413
+ }>]>>;
396
414
  }>]>>, never>;
397
415
  }>>;
398
416
  readonly scoped: Schema.withDecodingDefaultKey<Schema.Boolean, never>;
@@ -489,12 +507,29 @@ declare const DetailLevel: Schema.Literals<readonly ["minimal", "neutral", "stan
489
507
  type DetailLevel = typeof DetailLevel.Type;
490
508
  //#endregion
491
509
  //#region src/formatters/types.d.ts
492
- /** @public */
493
- interface RenderedOutput {
510
+ /**
511
+ * One unit of rendered reporter output, tagged by where it goes.
512
+ *
513
+ * `stdout` writes to the process stdout stream, `github-summary`
514
+ * appends to the GitHub Actions step-summary file, and `file` is
515
+ * reserved for reporters that embed their own destination path.
516
+ *
517
+ * `report` writes `filename` into `.vitest/<scope>/` through Vitest 5's
518
+ * `createReport`. The plugin drops these when reporting is disabled,
519
+ * exactly as it drops `github-summary` outside GitHub Actions.
520
+ *
521
+ * @public
522
+ */
523
+ type RenderedOutput = {
494
524
  readonly target: "stdout" | "file" | "github-summary";
495
525
  readonly content: string;
496
526
  readonly contentType: string;
497
- }
527
+ } | {
528
+ readonly target: "report";
529
+ readonly filename: string;
530
+ readonly content: string;
531
+ readonly contentType: string;
532
+ };
498
533
  /**
499
534
  * The reducer projects this union into `RenderState`. Renderers
500
535
  * never read the raw event stream — they read the projected state.
@@ -588,11 +623,36 @@ declare const RunEvent: Schema.Union<readonly [Schema.TaggedStruct<"RunStarted",
588
623
  readonly testName: Schema.String;
589
624
  readonly suitePath: Schema.$Array<Schema.String>;
590
625
  readonly annotation: Schema.String;
626
+ readonly annotationType: Schema.String;
627
+ readonly location: Schema.optional<Schema.Struct<{
628
+ readonly file: Schema.String;
629
+ readonly line: Schema.Number;
630
+ readonly column: Schema.Number;
631
+ }>>;
632
+ readonly attachments: Schema.$Array<Schema.Struct<{
633
+ readonly contentType: Schema.optional<Schema.String>;
634
+ readonly path: Schema.optional<Schema.String>;
635
+ readonly body: Schema.optional<Schema.String>;
636
+ readonly bodyEncoding: Schema.optional<Schema.Literals<readonly ["base64", "utf-8"]>>;
637
+ readonly byteSize: Schema.Number;
638
+ }>>;
591
639
  }>, Schema.TaggedStruct<"TestArtifactRecorded", {
592
640
  readonly modulePath: Schema.String;
593
641
  readonly testName: Schema.String;
594
642
  readonly suitePath: Schema.$Array<Schema.String>;
595
643
  readonly artifact: Schema.String;
644
+ readonly location: Schema.optional<Schema.Struct<{
645
+ readonly file: Schema.String;
646
+ readonly line: Schema.Number;
647
+ readonly column: Schema.Number;
648
+ }>>;
649
+ readonly attachments: Schema.$Array<Schema.Struct<{
650
+ readonly contentType: Schema.optional<Schema.String>;
651
+ readonly path: Schema.optional<Schema.String>;
652
+ readonly body: Schema.optional<Schema.String>;
653
+ readonly bodyEncoding: Schema.optional<Schema.Literals<readonly ["base64", "utf-8"]>>;
654
+ readonly byteSize: Schema.Number;
655
+ }>>;
596
656
  }>, Schema.TaggedStruct<"WatcherReady", {}>, Schema.TaggedStruct<"WatcherRerun", {
597
657
  readonly triggerFiles: Schema.$Array<Schema.String>;
598
658
  readonly reason: Schema.optional<Schema.String>;
@@ -681,12 +741,23 @@ declare const ResolvedThresholds: Schema.Struct<{
681
741
  readonly branches: Schema.optional<Schema.Number>;
682
742
  readonly statements: Schema.optional<Schema.Number>;
683
743
  }>;
684
- readonly perFile: Schema.withDecodingDefaultKey<Schema.Boolean, never>;
744
+ readonly perFile: Schema.withDecodingDefaultKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
745
+ readonly lines: Schema.optional<Schema.Number>;
746
+ readonly functions: Schema.optional<Schema.Number>;
747
+ readonly branches: Schema.optional<Schema.Number>;
748
+ readonly statements: Schema.optional<Schema.Number>;
749
+ }>]>, never>;
685
750
  readonly patterns: Schema.withDecodingDefaultKey<Schema.$Array<Schema.Tuple<readonly [Schema.String, Schema.Struct<{
686
751
  readonly lines: Schema.optional<Schema.Number>;
687
752
  readonly functions: Schema.optional<Schema.Number>;
688
753
  readonly branches: Schema.optional<Schema.Number>;
689
754
  readonly statements: Schema.optional<Schema.Number>;
755
+ readonly perFile: Schema.optionalKey<Schema.Union<readonly [Schema.Boolean, Schema.Struct<{
756
+ readonly lines: Schema.optional<Schema.Number>;
757
+ readonly functions: Schema.optional<Schema.Number>;
758
+ readonly branches: Schema.optional<Schema.Number>;
759
+ readonly statements: Schema.optional<Schema.Number>;
760
+ }>]>>;
690
761
  }>]>>, never>;
691
762
  }>;
692
763
  /** @public */
package/index.js CHANGED
@@ -9,7 +9,7 @@ import { DefaultVitestAgentReporter, buildDispatchInputs, renderAgentStringForRe
9
9
  *
10
10
  * @public
11
11
  */
12
- const CURRENT_REPORTER_VERSION = "2.2.4";
12
+ const CURRENT_REPORTER_VERSION = "3.0.1";
13
13
 
14
14
  //#endregion
15
15
  export { CURRENT_REPORTER_VERSION, DefaultVitestAgentReporter, createLiveInk as _createLiveInk, buildDispatchInputs, renderAgentStringForReport, renderHumanStringForReport, resolveCellOptions };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitest-agent/reporter",
3
- "version": "2.2.4",
3
+ "version": "3.0.1",
4
4
  "private": false,
5
5
  "description": "Named VitestAgentReporterFactory implementations for the vitest-agent ecosystem.",
6
6
  "keywords": [
@@ -41,16 +41,16 @@
41
41
  "dependencies": {
42
42
  "@effect/platform-node": "4.0.0-rc.112",
43
43
  "@effect/sql-sqlite-node": "4.0.0-rc.112",
44
- "@vitest-agent/sdk": "2.5.1",
45
- "@vitest-agent/ui": "2.3.1",
44
+ "@vitest-agent/sdk": "3.1.0",
45
+ "@vitest-agent/ui": "2.4.1",
46
46
  "effect": "4.0.0-rc.112",
47
47
  "ink": "^7.1.1",
48
48
  "react": "^19.2.8"
49
49
  },
50
50
  "peerDependencies": {
51
- "@vitest/coverage-istanbul": "^4.1.0",
52
- "@vitest/coverage-v8": "^4.1.0",
53
- "vitest": "^4.1.0"
51
+ "@vitest/coverage-istanbul": "^5.0.0",
52
+ "@vitest/coverage-v8": "^5.0.0",
53
+ "vitest": "^5.0.0"
54
54
  },
55
55
  "peerDependenciesMeta": {
56
56
  "@vitest/coverage-istanbul": {