@kinora/vitest-reporter 0.2.0 → 0.4.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 +7 -0
- package/dist/index.d.mts +8 -4
- package/dist/index.mjs +24 -28
- package/package.json +6 -6
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
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { AttachmentKind, CiMeta, GitMeta } from "@kinora/core";
|
|
2
2
|
import { Reporter, TestModule, Vitest } from "vitest/node";
|
|
3
|
-
//#region src/convert.d.ts
|
|
4
3
|
type IncludeTests = 'browser' | 'all';
|
|
5
|
-
//#endregion
|
|
6
|
-
//#region src/index.d.ts
|
|
7
4
|
export interface KinoraVitestReporterOptions {
|
|
8
5
|
/** kinora server base URL. Defaults to env KINORA_URL, then the hosted cloud. Set for self-host. */
|
|
9
6
|
url?: string;
|
|
@@ -24,14 +21,21 @@ export interface KinoraVitestReporterOptions {
|
|
|
24
21
|
* plan's monthly result quota with large unit suites.
|
|
25
22
|
*/
|
|
26
23
|
include?: IncludeTests;
|
|
24
|
+
/**
|
|
25
|
+
* Sharded runs (`--shard`): the uploads of every shard of a CI run are assembled into a single
|
|
26
|
+
* kinora run. Shards are grouped by this key, defaulting to env KINORA_GROUP_KEY, then the CI run
|
|
27
|
+
* (GITHUB_RUN_ID / CI_PIPELINE_ID). Set it when a matrix runs the sharded suite once per leg, to
|
|
28
|
+
* tell the legs apart. `false` uploads each shard as its own run.
|
|
29
|
+
*/
|
|
30
|
+
groupKey?: string | false;
|
|
27
31
|
}
|
|
28
32
|
export default class KinoraVitestReporter implements Reporter {
|
|
29
33
|
private readonly options;
|
|
30
34
|
private startedAt;
|
|
35
|
+
private shard;
|
|
31
36
|
constructor(options: KinoraVitestReporterOptions);
|
|
32
37
|
onInit(vitest: Vitest): void;
|
|
33
38
|
onTestRunStart(): void;
|
|
34
39
|
onTestRunEnd(modules: ReadonlyArray<TestModule>): Promise<void>;
|
|
35
40
|
}
|
|
36
|
-
//#endregion
|
|
37
41
|
export {};
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readFile } from "node:fs/promises";
|
|
2
2
|
import process from "node:process";
|
|
3
|
-
import {
|
|
4
|
-
|
|
3
|
+
import { DEFAULT_KINORA_URL, DEFAULT_UPLOAD_ATTACHMENTS, IngestError, MAX_A11Y_NODES, MAX_A11Y_VIOLATIONS, STORY_ANNOTATION, axeViolations, createIngestClient, describeRetry, detectCiEnv, isUploadableAttachment, makeTestKey, resolveShardGroup, stripAnsi } from "@kinora/core";
|
|
4
|
+
// src/convert.ts
|
|
5
5
|
function identity(test) {
|
|
6
6
|
const file = test.module.relativeModuleId;
|
|
7
7
|
const suites = [];
|
|
@@ -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
|
|
71
|
+
violations: axeViolations(result.violations)
|
|
89
72
|
};
|
|
90
73
|
}
|
|
91
74
|
function slug(name) {
|
|
@@ -180,8 +163,7 @@ function toNormTests(modules, include = "browser") {
|
|
|
180
163
|
skippedNodeTests
|
|
181
164
|
};
|
|
182
165
|
}
|
|
183
|
-
|
|
184
|
-
//#region src/index.ts
|
|
166
|
+
// src/index.ts
|
|
185
167
|
function countsOf(tests) {
|
|
186
168
|
const counts = {
|
|
187
169
|
total: tests.length,
|
|
@@ -196,11 +178,17 @@ function countsOf(tests) {
|
|
|
196
178
|
var KinoraVitestReporter = class {
|
|
197
179
|
options;
|
|
198
180
|
startedAt = /* @__PURE__ */ new Date();
|
|
181
|
+
shard;
|
|
199
182
|
constructor(options) {
|
|
200
183
|
this.options = options;
|
|
201
184
|
}
|
|
202
185
|
onInit(vitest) {
|
|
203
186
|
this.startedAt = /* @__PURE__ */ new Date();
|
|
187
|
+
const shard = vitest.config.shard;
|
|
188
|
+
this.shard = shard ? {
|
|
189
|
+
current: shard.index,
|
|
190
|
+
total: shard.count
|
|
191
|
+
} : void 0;
|
|
204
192
|
vitest.config.includeTaskLocation = true;
|
|
205
193
|
for (const project of vitest.projects) project.config.includeTaskLocation = true;
|
|
206
194
|
}
|
|
@@ -215,9 +203,13 @@ var KinoraVitestReporter = class {
|
|
|
215
203
|
return;
|
|
216
204
|
}
|
|
217
205
|
const { tests, skippedNodeTests } = toNormTests(modules, this.options.include);
|
|
206
|
+
const group = this.shard && this.options.groupKey !== false ? resolveShardGroup(process.env, { groupKey: this.options.groupKey }) : void 0;
|
|
218
207
|
if (!tests.length) {
|
|
219
|
-
|
|
220
|
-
|
|
208
|
+
if (!group) {
|
|
209
|
+
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");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
console.warn("[kinora] no tests to upload in this shard: filling its slot so the run can complete");
|
|
221
213
|
}
|
|
222
214
|
const detected = detectCiEnv(process.env);
|
|
223
215
|
const payload = {
|
|
@@ -230,14 +222,19 @@ var KinoraVitestReporter = class {
|
|
|
230
222
|
duration: Date.now() - this.startedAt.getTime(),
|
|
231
223
|
counts: countsOf(tests),
|
|
232
224
|
git: this.options.git ?? detected.git,
|
|
233
|
-
ci: this.options.ci ?? detected.ci
|
|
225
|
+
ci: this.options.ci ?? detected.ci,
|
|
226
|
+
...this.shard && group && {
|
|
227
|
+
...group,
|
|
228
|
+
shard: this.shard
|
|
229
|
+
}
|
|
234
230
|
},
|
|
235
231
|
tests
|
|
236
232
|
};
|
|
237
233
|
try {
|
|
238
234
|
const client = createIngestClient({
|
|
239
235
|
baseUrl: url,
|
|
240
|
-
token
|
|
236
|
+
token,
|
|
237
|
+
onRetry: (info) => console.warn(`[kinora] ${describeRetry(info)}`)
|
|
241
238
|
});
|
|
242
239
|
const res = await client.uploadRun(payload);
|
|
243
240
|
const kinds = this.options.uploadAttachments ?? DEFAULT_UPLOAD_ATTACHMENTS;
|
|
@@ -257,12 +254,11 @@ 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);
|
|
264
261
|
}
|
|
265
262
|
}
|
|
266
263
|
};
|
|
267
|
-
//#endregion
|
|
268
264
|
export { KinoraVitestReporter as default };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kinora/vitest-reporter",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.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.
|
|
37
|
+
"vitest": ">=4.1.12"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@kinora/core": "0.
|
|
40
|
+
"@kinora/core": "0.14.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@types/node": "^26.6.5",
|
|
44
|
-
"@vitest/coverage-v8": "^5.0.
|
|
45
|
-
"tsdown": "^0.23.
|
|
44
|
+
"@vitest/coverage-v8": "^5.0.5",
|
|
45
|
+
"tsdown": "^0.23.2",
|
|
46
46
|
"typescript": "~6.0.3",
|
|
47
|
-
"vitest": "^5.0.
|
|
47
|
+
"vitest": "^5.0.5"
|
|
48
48
|
},
|
|
49
49
|
"scripts": {
|
|
50
50
|
"build": "tsdown",
|