@kaizenreport/kensho-vitest 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/index.js +88 -49
- package/package.json +2 -2
package/index.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
|
-
// @kaizenreport/kensho-vitest — Vitest custom reporter
|
|
2
|
-
//
|
|
1
|
+
// @kaizenreport/kensho-vitest — Vitest custom reporter → kensho-results/.
|
|
2
|
+
//
|
|
3
|
+
// Supports both reporter APIs:
|
|
4
|
+
// • vitest 2.1+/3/4: onTestRunEnd(testModules) — the stable Reported Tasks API
|
|
5
|
+
// (TestModule / TestCase). This is what current Vitest invokes.
|
|
6
|
+
// • vitest 1.x–2.0: onFinished(files) — the legacy task-tree API (fallback).
|
|
7
|
+
// A guard ensures only the first hook to fire writes the report.
|
|
3
8
|
|
|
4
9
|
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
5
10
|
import { resolve, relative } from 'node:path';
|
|
6
11
|
import { emptyRun, computeTotals, stableCaseId, validateRun, envInfo } from '@kaizenreport/kensho-schema';
|
|
7
12
|
|
|
8
|
-
// envInfo() is imported from @kaizenreport/kensho-schema below.
|
|
9
|
-
|
|
10
13
|
function mapStatus(task) {
|
|
11
|
-
//
|
|
14
|
+
// Legacy task.result.state: 'pass' | 'fail' | 'skip' | 'todo' | 'only' | 'run'
|
|
12
15
|
const state = task?.result?.state;
|
|
13
16
|
const mode = task?.mode;
|
|
14
17
|
if (mode === 'skip' || mode === 'todo' || state === 'skip' || state === 'todo') return 'skip';
|
|
@@ -17,6 +20,14 @@ function mapStatus(task) {
|
|
|
17
20
|
return 'broken';
|
|
18
21
|
}
|
|
19
22
|
|
|
23
|
+
function mapReportedState(state) {
|
|
24
|
+
// Reported Tasks API TestResult.state: 'passed' | 'failed' | 'skipped' | 'pending'
|
|
25
|
+
if (state === 'passed') return 'pass';
|
|
26
|
+
if (state === 'failed') return 'fail';
|
|
27
|
+
if (state === 'skipped' || state === 'pending') return 'skip';
|
|
28
|
+
return 'broken';
|
|
29
|
+
}
|
|
30
|
+
|
|
20
31
|
function extractInlineTags(title) {
|
|
21
32
|
const tags = [];
|
|
22
33
|
const re = /@([\w-]+)/g;
|
|
@@ -34,7 +45,6 @@ function severityFromTags(tags) {
|
|
|
34
45
|
}
|
|
35
46
|
|
|
36
47
|
function walkTests(task, suiteChain, out) {
|
|
37
|
-
// Vitest task types: 'suite' | 'test' | 'custom'
|
|
38
48
|
if (task.type === 'suite') {
|
|
39
49
|
const nextChain = task.name ? suiteChain.concat(task.name) : suiteChain;
|
|
40
50
|
for (const child of task.tasks || []) walkTests(child, nextChain, out);
|
|
@@ -53,6 +63,7 @@ export default class KenshoVitestReporter {
|
|
|
53
63
|
this.runId = opts.runId || ('run_' + new Date().toISOString().replace(/[^0-9]/g, '').slice(0, 14));
|
|
54
64
|
this.startedAt = new Date().toISOString();
|
|
55
65
|
this.casesById = new Map();
|
|
66
|
+
this._emitted = false;
|
|
56
67
|
}
|
|
57
68
|
|
|
58
69
|
onInit(/* ctx */) {
|
|
@@ -62,81 +73,109 @@ export default class KenshoVitestReporter {
|
|
|
62
73
|
this.startedAt = new Date().toISOString();
|
|
63
74
|
}
|
|
64
75
|
|
|
76
|
+
// ── vitest 2.1+/3/4 — Reported Tasks API ────────────────────────────────
|
|
77
|
+
onTestRunEnd(testModules /* , unhandledErrors, reason */) {
|
|
78
|
+
if (this._emitted) return;
|
|
79
|
+
try {
|
|
80
|
+
for (const mod of testModules || []) {
|
|
81
|
+
const filePath = mod.moduleId ? relative(process.cwd(), mod.moduleId) : undefined;
|
|
82
|
+
for (const tc of mod.children.allTests()) {
|
|
83
|
+
const caseObj = this._toKenshoCaseReported(tc, filePath);
|
|
84
|
+
writeFileSync(resolve(this.casesDir, caseObj.id + '.json'), JSON.stringify(caseObj, null, 2));
|
|
85
|
+
this.casesById.set(caseObj.id, caseObj);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this._emitted = true;
|
|
89
|
+
this._writeManifest();
|
|
90
|
+
} catch (e) {
|
|
91
|
+
console.error('[kensho] vitest reporter (onTestRunEnd) failed:', (e && e.stack) || e);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ── vitest 1.x–2.0 — legacy task-tree API ───────────────────────────────
|
|
65
96
|
onFinished(files = [] /* , errors = [] */) {
|
|
97
|
+
if (this._emitted) return;
|
|
66
98
|
try {
|
|
67
99
|
const collected = [];
|
|
68
|
-
for (const f of files || [])
|
|
69
|
-
// File-level task: its own name is the file path; its tasks are describe/suite blocks.
|
|
70
|
-
walkTests(f, [], collected);
|
|
71
|
-
this._filePath = f.filepath || f.name;
|
|
72
|
-
}
|
|
100
|
+
for (const f of files || []) walkTests(f, [], collected);
|
|
73
101
|
for (const { task, suiteChain } of collected) {
|
|
74
102
|
const caseObj = this._toKenshoCase(task, suiteChain);
|
|
75
|
-
writeFileSync(
|
|
76
|
-
resolve(this.casesDir, caseObj.id + '.json'),
|
|
77
|
-
JSON.stringify(caseObj, null, 2),
|
|
78
|
-
);
|
|
103
|
+
writeFileSync(resolve(this.casesDir, caseObj.id + '.json'), JSON.stringify(caseObj, null, 2));
|
|
79
104
|
this.casesById.set(caseObj.id, caseObj);
|
|
80
105
|
}
|
|
106
|
+
this._emitted = true;
|
|
81
107
|
this._writeManifest();
|
|
82
108
|
} catch (e) {
|
|
83
|
-
console.error('[kensho] vitest reporter failed:', e && e.
|
|
109
|
+
console.error('[kensho] vitest reporter (onFinished) failed:', (e && e.stack) || e);
|
|
84
110
|
}
|
|
85
111
|
}
|
|
86
112
|
|
|
87
|
-
|
|
88
|
-
const name = task.name || 'unnamed';
|
|
89
|
-
const filePath = (task.file?.filepath || task.file?.name)
|
|
90
|
-
? relative(process.cwd(), task.file.filepath || task.file.name)
|
|
91
|
-
: undefined;
|
|
92
|
-
const fullName = suiteChain.concat(name).join(' › ');
|
|
113
|
+
_uniqueId(fullName, filePath) {
|
|
93
114
|
let id = stableCaseId(fullName, filePath);
|
|
94
115
|
if (this.casesById.has(id)) {
|
|
95
116
|
let i = 2;
|
|
96
117
|
while (this.casesById.has(id + '_' + i)) i++;
|
|
97
118
|
id = id + '_' + i;
|
|
98
119
|
}
|
|
120
|
+
return id;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
_toKenshoCaseReported(tc, filePath) {
|
|
124
|
+
const name = tc.name || 'unnamed';
|
|
125
|
+
const fullName = tc.fullName || name;
|
|
126
|
+
const suiteChain = fullName.includes(' > ') ? fullName.split(' > ').slice(0, -1) : [];
|
|
127
|
+
const id = this._uniqueId(fullName, filePath);
|
|
128
|
+
const res = (typeof tc.result === 'function' ? tc.result() : tc.result) || {};
|
|
129
|
+
const diag = (typeof tc.diagnostic === 'function' ? tc.diagnostic() : {}) || {};
|
|
130
|
+
const duration = Math.max(0, Math.round(diag.duration || 0));
|
|
131
|
+
const startMs = diag.startTime || Date.now();
|
|
132
|
+
const tags = extractInlineTags(name);
|
|
133
|
+
const errors = (res.errors || []).map(e => ({
|
|
134
|
+
message: String(e.message || e), stack: e.stack, type: e.name,
|
|
135
|
+
}));
|
|
136
|
+
return {
|
|
137
|
+
id, name, fullName, filePath,
|
|
138
|
+
suite: suiteChain,
|
|
139
|
+
tags,
|
|
140
|
+
severity: this.severityFromTag ? severityFromTags(tags) : undefined,
|
|
141
|
+
status: mapReportedState(res.state),
|
|
142
|
+
startedAt: new Date(startMs).toISOString(),
|
|
143
|
+
finishedAt: new Date(startMs + duration).toISOString(),
|
|
144
|
+
duration,
|
|
145
|
+
retries: diag.retryCount || 0,
|
|
146
|
+
platform: process.platform,
|
|
147
|
+
steps: [],
|
|
148
|
+
errors: errors.length ? errors : undefined,
|
|
149
|
+
attachments: [],
|
|
150
|
+
logs: [],
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
_toKenshoCase(task, suiteChain) {
|
|
155
|
+
const name = task.name || 'unnamed';
|
|
156
|
+
const filePath = (task.file?.filepath || task.file?.name)
|
|
157
|
+
? relative(process.cwd(), task.file.filepath || task.file.name)
|
|
158
|
+
: undefined;
|
|
159
|
+
const fullName = suiteChain.concat(name).join(' › ');
|
|
160
|
+
const id = this._uniqueId(fullName, filePath);
|
|
99
161
|
const tags = extractInlineTags(name);
|
|
100
162
|
const duration = Math.max(0, Math.round(task.result?.duration || 0));
|
|
101
163
|
const startMs = task.result?.startTime || Date.now();
|
|
102
|
-
const startedAt = new Date(startMs).toISOString();
|
|
103
|
-
|
|
104
164
|
const errors = (task.result?.errors || []).map(e => ({
|
|
105
|
-
message: String(e.message || e),
|
|
106
|
-
stack: e.stack,
|
|
107
|
-
type: e.name,
|
|
165
|
+
message: String(e.message || e), stack: e.stack, type: e.name,
|
|
108
166
|
}));
|
|
109
|
-
|
|
110
|
-
// Nested tasks (e.g. test.each or custom child tasks) → Kensho steps.
|
|
111
|
-
const steps = [];
|
|
112
|
-
if (Array.isArray(task.tasks) && task.tasks.length) {
|
|
113
|
-
task.tasks.forEach((child, i) => {
|
|
114
|
-
const childStatus = mapStatus(child);
|
|
115
|
-
steps.push({
|
|
116
|
-
id: 'step_' + i + '_' + Math.random().toString(36).slice(2, 6),
|
|
117
|
-
title: child.name || `Step ${i + 1}`,
|
|
118
|
-
status: childStatus === 'fail' ? 'fail' : childStatus === 'skip' ? 'skip' : 'pass',
|
|
119
|
-
startedAt: new Date(child.result?.startTime || startMs).toISOString(),
|
|
120
|
-
duration: Math.max(0, Math.round(child.result?.duration || 0)),
|
|
121
|
-
});
|
|
122
|
-
});
|
|
123
|
-
}
|
|
124
|
-
|
|
125
167
|
return {
|
|
126
|
-
id,
|
|
127
|
-
name,
|
|
128
|
-
fullName,
|
|
129
|
-
filePath,
|
|
168
|
+
id, name, fullName, filePath,
|
|
130
169
|
suite: suiteChain,
|
|
131
170
|
tags,
|
|
132
171
|
severity: this.severityFromTag ? severityFromTags(tags) : undefined,
|
|
133
172
|
status: mapStatus(task),
|
|
134
|
-
startedAt,
|
|
173
|
+
startedAt: new Date(startMs).toISOString(),
|
|
135
174
|
finishedAt: new Date(startMs + duration).toISOString(),
|
|
136
175
|
duration,
|
|
137
176
|
retries: task.result?.retryCount || 0,
|
|
138
177
|
platform: process.platform,
|
|
139
|
-
steps,
|
|
178
|
+
steps: [],
|
|
140
179
|
errors: errors.length ? errors : undefined,
|
|
141
180
|
attachments: [],
|
|
142
181
|
logs: [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kaizenreport/kensho-vitest",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Kensho reporter for Vitest — writes kensho-results/ so the Kensho CLI can generate a rich HTML report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@kaizenreport/kensho-schema": "0.1.
|
|
20
|
+
"@kaizenreport/kensho-schema": "0.1.1"
|
|
21
21
|
},
|
|
22
22
|
"engines": {
|
|
23
23
|
"node": ">=22"
|