@kinora/vitest-reporter 0.2.0 → 0.3.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.
package/README.md CHANGED
@@ -37,6 +37,13 @@ Create an API token in the kinora dashboard (Settings → Workspace). Self-hosti
37
37
 
38
38
  Only browser tests are uploaded by default: Node unit tests are usually thousands of fast tests with no trace, and would eat your plan's monthly result quota. Upload them too with `include: 'all'`.
39
39
 
40
+ ## Accessibility
41
+
42
+ With Storybook's a11y addon, each story's check is uploaded too: the rules broken, their impact
43
+ and the elements they hit, ranked by component on the Components page. Use the addon's `todo`
44
+ mode to keep the impact: in `error` mode, Storybook drops the check when the story fails and
45
+ kinora reads the violations from the failure instead.
46
+
40
47
  ## Documentation
41
48
 
42
49
  **https://docs.kinora.dev/guides/vitest/**
package/dist/index.d.mts CHANGED
@@ -24,10 +24,18 @@ export interface KinoraVitestReporterOptions {
24
24
  * plan's monthly result quota with large unit suites.
25
25
  */
26
26
  include?: IncludeTests;
27
+ /**
28
+ * Sharded runs (`--shard`): the uploads of every shard of a CI run are assembled into a single
29
+ * kinora run. Shards are grouped by this key, defaulting to env KINORA_GROUP_KEY, then the CI run
30
+ * (GITHUB_RUN_ID / CI_PIPELINE_ID). Set it when a matrix runs the sharded suite once per leg, to
31
+ * tell the legs apart. `false` uploads each shard as its own run.
32
+ */
33
+ groupKey?: string | false;
27
34
  }
28
35
  export default class KinoraVitestReporter implements Reporter {
29
36
  private readonly options;
30
37
  private startedAt;
38
+ private shard;
31
39
  constructor(options: KinoraVitestReporterOptions);
32
40
  onInit(vitest: Vitest): void;
33
41
  onTestRunStart(): void;
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { readFile } from "node:fs/promises";
2
2
  import process from "node:process";
3
- import { A11Y_IMPACTS, DEFAULT_KINORA_URL, DEFAULT_UPLOAD_ATTACHMENTS, IngestError, MAX_A11Y_NODES, MAX_A11Y_VIOLATIONS, STORY_ANNOTATION, createIngestClient, detectCiEnv, isUploadableAttachment, makeTestKey, stripAnsi } from "@kinora/core";
3
+ import { DEFAULT_KINORA_URL, DEFAULT_UPLOAD_ATTACHMENTS, IngestError, MAX_A11Y_NODES, MAX_A11Y_VIOLATIONS, STORY_ANNOTATION, axeViolations, createIngestClient, detectCiEnv, isUploadableAttachment, makeTestKey, resolveShardGroup, stripAnsi } from "@kinora/core";
4
4
  //#region src/convert.ts
5
5
  function identity(test) {
6
6
  const file = test.module.relativeModuleId;
@@ -30,23 +30,6 @@ function storyOf(test, titlePath) {
30
30
  }
31
31
  const MAX_TEXT = 300;
32
32
  const truncate = (s) => s.length > MAX_TEXT ? `${s.slice(0, MAX_TEXT)}…` : s;
33
- const isImpact = (v) => A11Y_IMPACTS.includes(v);
34
- function selector(target) {
35
- if (!Array.isArray(target)) return "";
36
- return truncate(target.map((t) => Array.isArray(t) ? t.join(" >>> ") : String(t)).join(" "));
37
- }
38
- function violation(v) {
39
- if (!v.id) return [];
40
- const nodes = v.nodes ?? [];
41
- return [{
42
- id: v.id,
43
- ...isImpact(v.impact) && { impact: v.impact },
44
- help: truncate(v.help ?? v.id),
45
- ...v.helpUrl && { helpUrl: v.helpUrl },
46
- nodes: nodes.slice(0, MAX_A11Y_NODES).map((n) => selector(n.target)).filter(Boolean),
47
- nodeCount: nodes.length
48
- }];
49
- }
50
33
  const AXE_FAILURE = /Expected the HTML found at \$\('(.+?)'\) to have no violations:[\s\S]*?Received:\s*"(.+?) \(([\w-]+)\)"[\s\S]*?more information on this issue here:\s*(https:\/\/\S+)/g;
51
34
  function a11yFromError(test) {
52
35
  const message = test.result().errors?.find((e) => e.message.includes("toHaveNoViolations"))?.message;
@@ -85,7 +68,7 @@ function a11yOf(test) {
85
68
  }
86
69
  return {
87
70
  status,
88
- violations: (result.violations ?? []).slice(0, MAX_A11Y_VIOLATIONS).flatMap(violation)
71
+ violations: axeViolations(result.violations)
89
72
  };
90
73
  }
91
74
  function slug(name) {
@@ -196,11 +179,17 @@ function countsOf(tests) {
196
179
  var KinoraVitestReporter = class {
197
180
  options;
198
181
  startedAt = /* @__PURE__ */ new Date();
182
+ shard;
199
183
  constructor(options) {
200
184
  this.options = options;
201
185
  }
202
186
  onInit(vitest) {
203
187
  this.startedAt = /* @__PURE__ */ new Date();
188
+ const shard = vitest.config.shard;
189
+ this.shard = shard ? {
190
+ current: shard.index,
191
+ total: shard.count
192
+ } : void 0;
204
193
  vitest.config.includeTaskLocation = true;
205
194
  for (const project of vitest.projects) project.config.includeTaskLocation = true;
206
195
  }
@@ -215,9 +204,13 @@ var KinoraVitestReporter = class {
215
204
  return;
216
205
  }
217
206
  const { tests, skippedNodeTests } = toNormTests(modules, this.options.include);
207
+ const group = this.shard && this.options.groupKey !== false ? resolveShardGroup(process.env, { groupKey: this.options.groupKey }) : void 0;
218
208
  if (!tests.length) {
219
- console.warn(skippedNodeTests ? `[kinora] nothing uploaded: no browser tests in this run (${skippedNodeTests} Node tests left out). Set include: 'all' to upload Node tests too.` : "[kinora] nothing uploaded: no tests ran");
220
- return;
209
+ if (!group) {
210
+ console.warn(skippedNodeTests ? `[kinora] nothing uploaded: no browser tests in this run (${skippedNodeTests} Node tests left out). Set include: 'all' to upload Node tests too.` : "[kinora] nothing uploaded: no tests ran");
211
+ return;
212
+ }
213
+ console.warn("[kinora] no tests to upload in this shard: filling its slot so the run can complete");
221
214
  }
222
215
  const detected = detectCiEnv(process.env);
223
216
  const payload = {
@@ -230,7 +223,11 @@ var KinoraVitestReporter = class {
230
223
  duration: Date.now() - this.startedAt.getTime(),
231
224
  counts: countsOf(tests),
232
225
  git: this.options.git ?? detected.git,
233
- ci: this.options.ci ?? detected.ci
226
+ ci: this.options.ci ?? detected.ci,
227
+ ...this.shard && group && {
228
+ ...group,
229
+ shard: this.shard
230
+ }
234
231
  },
235
232
  tests
236
233
  };
@@ -257,7 +254,7 @@ var KinoraVitestReporter = class {
257
254
  console.warn(`[kinora] ${a.name} upload failed for ${t.testKey}:`, err instanceof Error ? err.message : err);
258
255
  }
259
256
  }
260
- console.log(`[kinora] uploaded ${res.tests} tests + ${artifacts} artifacts (run ${res.runId})`);
257
+ console.log(`[kinora] uploaded ${res.tests} tests + ${artifacts} artifacts (run ${res.runId}${res.shardProgress ? `, ${res.shardProgress.landed} of ${res.shardProgress.total} shards` : ""})`);
261
258
  } catch (err) {
262
259
  if (err instanceof IngestError && err.status === 402) console.warn(`[kinora] ${err.message}`);
263
260
  else console.error("[kinora] upload failed:", err instanceof Error ? err.message : err);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@kinora/vitest-reporter",
3
3
  "type": "module",
4
- "version": "0.2.0",
4
+ "version": "0.3.0",
5
5
  "description": "Vitest reporter that uploads browser-mode and Storybook test results to a kinora server",
6
6
  "author": "Joris Gallot",
7
7
  "license": "MIT",
@@ -34,17 +34,17 @@
34
34
  "access": "public"
35
35
  },
36
36
  "peerDependencies": {
37
- "vitest": ">=4.1.11"
37
+ "vitest": ">=4.1.12"
38
38
  },
39
39
  "dependencies": {
40
- "@kinora/core": "0.9.0"
40
+ "@kinora/core": "0.13.0"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^26.6.5",
44
- "@vitest/coverage-v8": "^5.0.3",
44
+ "@vitest/coverage-v8": "^5.0.5",
45
45
  "tsdown": "^0.23.1",
46
46
  "typescript": "~6.0.3",
47
- "vitest": "^5.0.3"
47
+ "vitest": "^5.0.5"
48
48
  },
49
49
  "scripts": {
50
50
  "build": "tsdown",