@testomatio/reporter 2.7.3-beta.1-vitest → 2.7.3-beta.3.allure

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.
@@ -19,37 +19,21 @@ const debug = createDebugMessages('@testomatio/reporter:adapter-jest');
19
19
  class VitestReporter {
20
20
  constructor(config = {}) {
21
21
  this.client = new TestomatioClient({ apiKey: config?.apiKey });
22
- /** @type {(TestData & {status: string, _reportKey?: string | null})[]} tests */
22
+ /**
23
+ * @type {(TestData & {status: string})[]} tests
24
+ */
23
25
  this.tests = [];
24
26
  this._finalized = false;
25
27
  this._finalizing = false;
26
- this._runStartedAtMs = null;
27
- this._runStartedAtMicros = null;
28
- this._reportedTestKeys = new Set();
29
- this._liveQueue = Promise.resolve();
30
28
  }
31
29
 
32
30
  // on run start
33
31
  onInit() {
34
- const now = Date.now();
35
32
  this._finalized = false;
36
33
  this._finalizing = false;
37
- this._runStartedAtMs = now;
38
- this._runStartedAtMicros = now * 1000;
39
- this._reportedTestKeys = new Set();
40
- this._liveQueue = Promise.resolve();
41
34
  this.client.createRun();
42
35
  }
43
36
 
44
- /**
45
- * Vitest 3/4 callback fired when test run starts.
46
- */
47
- onTestRunStart() {
48
- const now = Date.now();
49
- this._runStartedAtMs = now;
50
- this._runStartedAtMicros = now * 1000;
51
- }
52
-
53
37
  /**
54
38
  * @param {VitestTestFile[] | undefined} files // array with results;
55
39
  * @param {unknown[] | undefined} errors // errors does not contain errors from tests; probably its testrunner errors
@@ -84,18 +68,13 @@ class VitestReporter {
84
68
 
85
69
  // send tests to Testomat.io
86
70
  for (const test of this.tests) {
87
- if (test._reportKey && this._reportedTestKeys.has(test._reportKey)) continue;
88
- if (test._reportKey) this._reportedTestKeys.add(test._reportKey);
89
71
  await this.client.addTestRun(test.status, test);
90
72
  }
91
- await this._liveQueue;
92
73
 
93
74
  console.log('finished');
94
75
  if (errors.length) console.error('Vitest adapter errors:', errors);
95
76
 
96
- const startedAtMs = this._runStartedAtMs || getEarliestTestStartMs(files) || Date.now();
97
- const duration = Math.max(0, (Date.now() - startedAtMs) / 1000);
98
- await this.client.updateRunStatus(getRunStatusFromResults(files), { duration });
77
+ await this.client.updateRunStatus(getRunStatusFromResults(files));
99
78
  this._finalized = true;
100
79
  } finally {
101
80
  this._finalizing = false;
@@ -115,28 +94,6 @@ class VitestReporter {
115
94
  await this.onFinished(files, errors);
116
95
  }
117
96
 
118
- /**
119
- * Vitest 4 callback fired when single test case is finished.
120
- *
121
- * @param {unknown} testCase
122
- */
123
- async onTestCaseResult(testCase) {
124
- await this.#reportLive(testCase);
125
- }
126
-
127
- /**
128
- * Vitest 3 fallback callback with task updates.
129
- *
130
- * @param {unknown[] | undefined} packs
131
- */
132
- async onTaskUpdate(packs) {
133
- if (!Array.isArray(packs) || !packs.length) return;
134
- for (const pack of packs) {
135
- const test = getTestFromTaskUpdatePack(pack);
136
- if (test) await this.#reportLive(test);
137
- }
138
- }
139
-
140
97
  /* non-used listeners
141
98
  onUserConsoleLog(log) {}
142
99
  onPathsCollected(paths) {} // paths array to files with tests
@@ -171,52 +128,25 @@ class VitestReporter {
171
128
  /**
172
129
  * Processes task and returns test data ready to be sent to Testomat.io
173
130
  *
174
- * @param {any} test
131
+ * @param {VitestTest} test
175
132
  *
176
- * @returns {TestData & {status: 'passed' | 'failed' | 'skipped', _reportKey?: string | null}}
133
+ * @returns {TestData & {status: 'passed' | 'failed' | 'skipped'}}
177
134
  */
178
135
  #getDataFromTest(test) {
179
- const normalized = normalizeVitestTest(test);
180
- const reportKey = getReportKey(test, normalized);
181
- const startMicros =
182
- typeof normalized.startTime === 'number'
183
- ? Math.floor(normalized.startTime * 1000)
184
- : this._runStartedAtMicros || undefined;
185
-
186
136
  return {
187
- _reportKey: reportKey,
188
- error: normalized.error,
189
- file: normalized.file,
190
- logs: normalized.logs,
191
- meta: normalized.meta,
137
+ error: test.result?.errors ? test.result.errors[0] : undefined,
138
+ file: test.file?.name || test.file?.filepath || '',
139
+ logs: test.logs ? transformLogsToString(test.logs) : '',
140
+ meta: test.meta,
192
141
  // @ts-ignore - STATUS values are string literals but type system sees them as string
193
- status: getTestStatus(normalized.state, normalized.mode),
194
- suite_title: normalized.suiteTitle,
195
- test_id: getTestomatIdFromTestTitle(normalized.name),
196
- time: normalized.duration,
197
- timestamp: startMicros,
198
- title: normalized.name,
142
+ status: getTestStatus(test),
143
+ suite_title: test.suite?.name || test.file?.name || test.file?.filepath,
144
+ test_id: getTestomatIdFromTestTitle(test.name),
145
+ time: test.result?.duration || 0,
146
+ title: test.name,
199
147
  // testomatio functions (artifacts, logs, steps, meta) are not supported
200
148
  };
201
149
  }
202
-
203
- /**
204
- * @param {unknown} testCase
205
- */
206
- async #reportLive(testCase) {
207
- if (this._finalized || this._finalizing) return;
208
- const normalized = normalizeVitestTest(testCase);
209
- if (!isLiveReportableState(normalized.state, normalized.mode)) return;
210
-
211
- const data = this.#getDataFromTest(testCase);
212
- if (!data._reportKey || this._reportedTestKeys.has(data._reportKey)) return;
213
- this._reportedTestKeys.add(data._reportKey);
214
-
215
- this._liveQueue = this._liveQueue
216
- .then(() => this.client.addTestRun(data.status, data))
217
- .catch(() => undefined);
218
- await this._liveQueue;
219
- }
220
150
  }
221
151
 
222
152
  /**
@@ -232,16 +162,17 @@ function getRunStatusFromResults(files) {
232
162
  let status = 'finished'; // default status (if no failed or passed tests)
233
163
 
234
164
  files.forEach(file => {
235
- getTasks(file).forEach(taskOrSuite => {
236
- if (isFailedState(taskOrSuite?.result?.state)) {
165
+ // search for failed tests
166
+ file.tasks.forEach(taskOrSuite => {
167
+ if (taskOrSuite.result?.state === 'fail') {
237
168
  status = 'failed'; // set status to failed if any test failed
238
169
  }
239
170
  });
240
171
 
241
172
  // if there are no failed tests > search for passed tests
242
173
  if (status !== 'failed') {
243
- getTasks(file).forEach(taskOrSuite => {
244
- if (isPassedState(taskOrSuite?.result?.state)) {
174
+ file.tasks.forEach(taskOrSuite => {
175
+ if (taskOrSuite.result?.state === 'pass') {
245
176
  status = 'passed'; // set status to passed if any test passed (and there are no failed tests)
246
177
  }
247
178
  });
@@ -254,15 +185,14 @@ function getRunStatusFromResults(files) {
254
185
  /**
255
186
  * Returns test status in Testomat.io format
256
187
  *
257
- * @param {string | undefined} state
258
- * @param {string | undefined} mode
188
+ * @param {VitestTest} test
259
189
  * @returns 'passed' | 'failed' | 'skipped'
260
190
  */
261
- function getTestStatus(state, mode) {
262
- if (isFailedState(state)) return STATUS.FAILED;
263
- if (isPassedState(state)) return STATUS.PASSED;
264
- if (isSkippedState(state) || (!state && mode === 'skip')) return STATUS.SKIPPED;
265
- console.error(pc.red('Unprocessed case for defining test status. Contact dev team. State:'), state);
191
+ function getTestStatus(test) {
192
+ if (test.result?.state === 'fail') return STATUS.FAILED;
193
+ if (test.result?.state === 'pass') return STATUS.PASSED;
194
+ if (test.result?.state === 'skip' || (!test.result && test.mode === 'skip')) return STATUS.SKIPPED;
195
+ console.error(pc.red('Unprocessed case for defining test status. Contact dev team. Test:'), test);
266
196
  return STATUS.SKIPPED;
267
197
  }
268
198
 
@@ -290,166 +220,9 @@ function getTasks(node) {
290
220
  if (!node) return [];
291
221
  if (Array.isArray(node.tasks)) return node.tasks;
292
222
  if (Array.isArray(node.children)) return node.children;
293
- if (node.children && typeof node.children[Symbol.iterator] === 'function') return Array.from(node.children);
294
223
  if (node.task) return [node.task];
295
224
  return [];
296
225
  }
297
226
 
298
- /**
299
- * @param {string | undefined} state
300
- * @returns {boolean}
301
- */
302
- function isFailedState(state) {
303
- return state === 'fail' || state === 'failed';
304
- }
305
-
306
- /**
307
- * @param {string | undefined} state
308
- * @returns {boolean}
309
- */
310
- function isPassedState(state) {
311
- return state === 'pass' || state === 'passed';
312
- }
313
-
314
- /**
315
- * @param {string | undefined} state
316
- * @returns {boolean}
317
- */
318
- function isSkippedState(state) {
319
- return state === 'skip' || state === 'skipped' || state === 'todo';
320
- }
321
-
322
- /**
323
- * Accept only completed test states for live upload to avoid reporting
324
- * intermediate task updates as skipped.
325
- *
326
- * @param {string | undefined} state
327
- * @param {string | undefined} mode
328
- * @returns {boolean}
329
- */
330
- function isLiveReportableState(state, mode) {
331
- if (isFailedState(state) || isPassedState(state) || isSkippedState(state)) return true;
332
- if (!state && mode === 'skip') return true;
333
- return false;
334
- }
335
-
336
- /**
337
- * @param {VitestTestFile[] | undefined} files
338
- * @returns {number | null}
339
- */
340
- function getEarliestTestStartMs(files) {
341
- let earliest = null;
342
- const walk = node => {
343
- if (!node) return;
344
- const startTime = node?.result?.startTime;
345
- if (typeof startTime === 'number' && !Number.isNaN(startTime)) {
346
- if (earliest == null || startTime < earliest) earliest = startTime;
347
- }
348
- getTasks(node).forEach(walk);
349
- };
350
- (files || []).forEach(walk);
351
- return earliest;
352
- }
353
-
354
- /**
355
- * @param {any} test
356
- * @returns {{
357
- * name: string,
358
- * state: string | undefined,
359
- * mode: string | undefined,
360
- * duration: number,
361
- * startTime: number | undefined,
362
- * error: any,
363
- * file: string,
364
- * suiteTitle: string,
365
- * logs: string,
366
- * meta: any
367
- * }}
368
- */
369
- function normalizeVitestTest(test) {
370
- if (test && typeof test.result === 'function') {
371
- const result = test.result();
372
- const diagnostic = typeof test.diagnostic === 'function' ? test.diagnostic() : undefined;
373
- const state = result?.state;
374
- const duration = diagnostic?.duration || 0;
375
- const startTime = diagnostic?.startTime;
376
- const error = Array.isArray(result?.errors) ? result.errors[0] : undefined;
377
- const file =
378
- test.module?.relativeModuleId ||
379
- test.module?.moduleId ||
380
- test.task?.file?.name ||
381
- test.task?.file?.filepath ||
382
- '';
383
- const suiteTitle =
384
- (test.parent?.type === 'suite' ? test.parent?.name : null) ||
385
- test.task?.suite?.name ||
386
- test.task?.file?.name ||
387
- file;
388
-
389
- return {
390
- name: test.name || test.task?.name || '',
391
- state,
392
- mode: test.options?.mode || test.task?.mode,
393
- duration,
394
- startTime,
395
- error,
396
- file,
397
- suiteTitle,
398
- logs: '',
399
- meta: typeof test.meta === 'function' ? test.meta() : {},
400
- };
401
- }
402
-
403
- return {
404
- name: test?.name || '',
405
- state: test?.result?.state,
406
- mode: test?.mode,
407
- duration: test?.result?.duration || 0,
408
- startTime: test?.result?.startTime,
409
- error: test?.result?.errors ? test.result.errors[0] : undefined,
410
- file: test?.file?.name || test?.file?.filepath || '',
411
- suiteTitle: test?.suite?.name || test?.file?.name || test?.file?.filepath || '',
412
- logs: test?.logs ? transformLogsToString(test.logs) : '',
413
- meta: test?.meta,
414
- };
415
- }
416
-
417
- /**
418
- * @param {any} test
419
- * @param {{file: string, suiteTitle: string, name: string, startTime?: number}} normalized
420
- * @returns {string | null}
421
- */
422
- function getReportKey(test, normalized) {
423
- if (test?.id) return String(test.id);
424
- if (test?.task?.id) return String(test.task.id);
425
- if (!normalized?.name) return null;
426
- const loc = test?.location || test?.task?.location;
427
- const locationKey = loc ? `${loc.line || ''}:${loc.column || ''}` : '';
428
- const startKey =
429
- typeof normalized.startTime === 'number' && !Number.isNaN(normalized.startTime) ? String(normalized.startTime) : '';
430
- return `${normalized.file}::${normalized.suiteTitle}::${normalized.name}::${locationKey}::${startKey}`;
431
- }
432
-
433
- /**
434
- * Vitest can pass task updates as tuples. Try to extract a test-like object.
435
- *
436
- * @param {unknown} pack
437
- * @returns {any | null}
438
- */
439
- function getTestFromTaskUpdatePack(pack) {
440
- if (!pack) return null;
441
-
442
- if (Array.isArray(pack)) {
443
- if (pack[2]?.type === 'test') return pack[2];
444
- if (pack[1]?.type === 'test') return pack[1];
445
- if (pack[0]?.type === 'test') return pack[0];
446
- return null;
447
- }
448
-
449
- const objectPack = /** @type {any} */ (pack);
450
- if (typeof objectPack === 'object' && objectPack?.type === 'test') return objectPack;
451
- return null;
452
- }
453
-
454
227
  export default VitestReporter;
455
228
  export { VitestReporter };