@kinora/vitest-reporter 0.1.0 → 0.2.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/dist/index.d.mts +3 -3
- package/dist/index.mjs +64 -2
- package/package.json +6 -6
package/dist/index.d.mts
CHANGED
|
@@ -4,7 +4,7 @@ import { Reporter, TestModule, Vitest } from "vitest/node";
|
|
|
4
4
|
type IncludeTests = 'browser' | 'all';
|
|
5
5
|
//#endregion
|
|
6
6
|
//#region src/index.d.ts
|
|
7
|
-
interface KinoraVitestReporterOptions {
|
|
7
|
+
export interface KinoraVitestReporterOptions {
|
|
8
8
|
/** kinora server base URL. Defaults to env KINORA_URL, then the hosted cloud. Set for self-host. */
|
|
9
9
|
url?: string;
|
|
10
10
|
/** Project API token. Defaults to env KINORA_TOKEN (keep it out of the config file). */
|
|
@@ -25,7 +25,7 @@ interface KinoraVitestReporterOptions {
|
|
|
25
25
|
*/
|
|
26
26
|
include?: IncludeTests;
|
|
27
27
|
}
|
|
28
|
-
|
|
28
|
+
export default class KinoraVitestReporter implements Reporter {
|
|
29
29
|
private readonly options;
|
|
30
30
|
private startedAt;
|
|
31
31
|
constructor(options: KinoraVitestReporterOptions);
|
|
@@ -34,4 +34,4 @@ declare class KinoraVitestReporter implements Reporter {
|
|
|
34
34
|
onTestRunEnd(modules: ReadonlyArray<TestModule>): Promise<void>;
|
|
35
35
|
}
|
|
36
36
|
//#endregion
|
|
37
|
-
export {
|
|
37
|
+
export {};
|
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 { DEFAULT_KINORA_URL, DEFAULT_UPLOAD_ATTACHMENTS, IngestError, STORY_ANNOTATION, createIngestClient, detectCiEnv, isUploadableAttachment, makeTestKey, stripAnsi } from "@kinora/core";
|
|
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";
|
|
4
4
|
//#region src/convert.ts
|
|
5
5
|
function identity(test) {
|
|
6
6
|
const file = test.module.relativeModuleId;
|
|
@@ -28,6 +28,66 @@ function storyOf(test, titlePath) {
|
|
|
28
28
|
if (!meta.componentName) return meta.storyId.replace("--", "/");
|
|
29
29
|
return `${meta.componentName}/${titlePath[1]}`;
|
|
30
30
|
}
|
|
31
|
+
const MAX_TEXT = 300;
|
|
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
|
+
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
|
+
function a11yFromError(test) {
|
|
52
|
+
const message = test.result().errors?.find((e) => e.message.includes("toHaveNoViolations"))?.message;
|
|
53
|
+
if (!message) return void 0;
|
|
54
|
+
const byRule = /* @__PURE__ */ new Map();
|
|
55
|
+
for (const [, target, help, id, helpUrl] of stripAnsi(message).matchAll(AXE_FAILURE)) {
|
|
56
|
+
const v = byRule.get(id) ?? {
|
|
57
|
+
id,
|
|
58
|
+
help: truncate(help),
|
|
59
|
+
helpUrl,
|
|
60
|
+
nodes: [],
|
|
61
|
+
nodeCount: 0
|
|
62
|
+
};
|
|
63
|
+
if (v.nodes.length < MAX_A11Y_NODES) v.nodes.push(truncate(target));
|
|
64
|
+
v.nodeCount++;
|
|
65
|
+
byRule.set(id, v);
|
|
66
|
+
}
|
|
67
|
+
return byRule.size ? {
|
|
68
|
+
status: "failed",
|
|
69
|
+
violations: [...byRule.values()].slice(0, MAX_A11Y_VIOLATIONS)
|
|
70
|
+
} : void 0;
|
|
71
|
+
}
|
|
72
|
+
function a11yOf(test) {
|
|
73
|
+
const report = test.meta().reports?.find((r) => r.type === "a11y");
|
|
74
|
+
const status = report?.status;
|
|
75
|
+
if (status !== "passed" && status !== "warning" && status !== "failed") return a11yFromError(test);
|
|
76
|
+
const result = report?.result ?? {};
|
|
77
|
+
if (result.error) {
|
|
78
|
+
const { message } = result.error;
|
|
79
|
+
const text = typeof message === "string" ? message : typeof result.error === "string" ? result.error : "The accessibility check failed.";
|
|
80
|
+
return {
|
|
81
|
+
status: "failed",
|
|
82
|
+
violations: [],
|
|
83
|
+
error: truncate(text)
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
status,
|
|
88
|
+
violations: (result.violations ?? []).slice(0, MAX_A11Y_VIOLATIONS).flatMap(violation)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
31
91
|
function slug(name) {
|
|
32
92
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "screenshot";
|
|
33
93
|
}
|
|
@@ -75,6 +135,7 @@ function toNormTest(test) {
|
|
|
75
135
|
const { file, titlePath, projectName } = identity(test);
|
|
76
136
|
const diagnostic = test.diagnostic();
|
|
77
137
|
const story = storyOf(test, titlePath);
|
|
138
|
+
const a11y = a11yOf(test);
|
|
78
139
|
return {
|
|
79
140
|
testKey: makeTestKey(file, titlePath, projectName),
|
|
80
141
|
title: test.name,
|
|
@@ -99,7 +160,8 @@ function toNormTest(test) {
|
|
|
99
160
|
message: cleanMessage(e.message),
|
|
100
161
|
stack: e.stack
|
|
101
162
|
})),
|
|
102
|
-
attachments: attachmentsOf(test)
|
|
163
|
+
attachments: attachmentsOf(test),
|
|
164
|
+
...a11y && { a11y }
|
|
103
165
|
};
|
|
104
166
|
}
|
|
105
167
|
function toNormTests(modules, include = "browser") {
|
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.2.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",
|
|
@@ -28,21 +28,21 @@
|
|
|
28
28
|
"dist"
|
|
29
29
|
],
|
|
30
30
|
"engines": {
|
|
31
|
-
"node": ">=20"
|
|
31
|
+
"node": ">=20.20.2"
|
|
32
32
|
},
|
|
33
33
|
"publishConfig": {
|
|
34
34
|
"access": "public"
|
|
35
35
|
},
|
|
36
36
|
"peerDependencies": {
|
|
37
|
-
"vitest": ">=4.1.
|
|
37
|
+
"vitest": ">=4.1.11"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@kinora/core": "0.
|
|
40
|
+
"@kinora/core": "0.9.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
|
-
"@types/node": "^26.5
|
|
43
|
+
"@types/node": "^26.6.5",
|
|
44
44
|
"@vitest/coverage-v8": "^5.0.3",
|
|
45
|
-
"tsdown": "^0.
|
|
45
|
+
"tsdown": "^0.23.1",
|
|
46
46
|
"typescript": "~6.0.3",
|
|
47
47
|
"vitest": "^5.0.3"
|
|
48
48
|
},
|