@nhonh/qabot 1.2.0 → 1.2.1
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/package.json +1 -1
- package/src/cli/commands/test.js +95 -27
- package/src/core/constants.js +1 -1
package/package.json
CHANGED
package/src/cli/commands/test.js
CHANGED
|
@@ -421,29 +421,27 @@ async function generateE2EReport(
|
|
|
421
421
|
result,
|
|
422
422
|
) {
|
|
423
423
|
try {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
const
|
|
424
|
+
const tests = await parsePlaywrightJsonReport(projectDir);
|
|
425
|
+
|
|
426
|
+
const passed = tests.filter((t) => t.status === "passed").length;
|
|
427
|
+
const failed = tests.filter((t) => t.status === "failed").length;
|
|
428
|
+
const skipped = tests.filter((t) => t.status === "skipped").length;
|
|
429
|
+
const total = tests.length;
|
|
430
|
+
|
|
431
|
+
const reportData = {
|
|
427
432
|
summary: {
|
|
428
|
-
totalTests: result.passed + result.failed + result.skipped,
|
|
429
|
-
totalPassed: result.passed,
|
|
430
|
-
totalFailed: result.failed,
|
|
431
|
-
totalSkipped: result.skipped,
|
|
432
|
-
overallPassRate:
|
|
433
|
-
result.passed + result.failed + result.skipped > 0
|
|
434
|
-
? Math.round(
|
|
435
|
-
(result.passed /
|
|
436
|
-
(result.passed + result.failed + result.skipped)) *
|
|
437
|
-
100,
|
|
438
|
-
)
|
|
439
|
-
: 0,
|
|
433
|
+
totalTests: total || result.passed + result.failed + result.skipped,
|
|
434
|
+
totalPassed: total ? passed : result.passed,
|
|
435
|
+
totalFailed: total ? failed : result.failed,
|
|
436
|
+
totalSkipped: total ? skipped : result.skipped,
|
|
437
|
+
overallPassRate: total > 0 ? Math.round((passed / total) * 100) : 0,
|
|
440
438
|
totalDuration: result.duration,
|
|
441
439
|
byLayer: {
|
|
442
440
|
e2e: {
|
|
443
|
-
total: result.passed + result.failed + result.skipped,
|
|
444
|
-
passed: result.passed,
|
|
445
|
-
failed: result.failed,
|
|
446
|
-
skipped: result.skipped,
|
|
441
|
+
total: total || result.passed + result.failed + result.skipped,
|
|
442
|
+
passed: total ? passed : result.passed,
|
|
443
|
+
failed: total ? failed : result.failed,
|
|
444
|
+
skipped: total ? skipped : result.skipped,
|
|
447
445
|
},
|
|
448
446
|
},
|
|
449
447
|
},
|
|
@@ -453,17 +451,13 @@ async function generateE2EReport(
|
|
|
453
451
|
layer: "e2e",
|
|
454
452
|
feature,
|
|
455
453
|
tests,
|
|
456
|
-
summary: {
|
|
457
|
-
total: result.passed + result.failed + result.skipped,
|
|
458
|
-
passed: result.passed,
|
|
459
|
-
failed: result.failed,
|
|
460
|
-
skipped: result.skipped,
|
|
461
|
-
},
|
|
454
|
+
summary: { total, passed, failed, skipped },
|
|
462
455
|
},
|
|
463
456
|
],
|
|
464
457
|
};
|
|
465
458
|
|
|
466
|
-
const
|
|
459
|
+
const reporter = new ReportGenerator(config);
|
|
460
|
+
const reportPaths = await reporter.generate(reportData, {
|
|
467
461
|
feature,
|
|
468
462
|
environment: env,
|
|
469
463
|
projectName: config.project?.name || "unknown",
|
|
@@ -472,7 +466,81 @@ async function generateE2EReport(
|
|
|
472
466
|
});
|
|
473
467
|
|
|
474
468
|
logger.info(`Report: ${chalk.underline(reportPaths.htmlPath)}`);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
logger.dim(`Report generation failed: ${err.message}`);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
async function parsePlaywrightJsonReport(projectDir) {
|
|
475
|
+
const jsonPath = path.join(
|
|
476
|
+
projectDir,
|
|
477
|
+
"qabot-reports",
|
|
478
|
+
"playwright",
|
|
479
|
+
"results.json",
|
|
480
|
+
);
|
|
481
|
+
if (!(await fileExists(jsonPath))) return [];
|
|
482
|
+
|
|
483
|
+
try {
|
|
484
|
+
const raw = await readFile(jsonPath, "utf-8");
|
|
485
|
+
const data = JSON.parse(raw);
|
|
486
|
+
const tests = [];
|
|
487
|
+
flattenPwSuites(data.suites || [], tests);
|
|
488
|
+
return tests;
|
|
475
489
|
} catch {
|
|
476
|
-
|
|
490
|
+
return [];
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function flattenPwSuites(suites, tests) {
|
|
495
|
+
for (const suite of suites) {
|
|
496
|
+
for (const spec of suite.specs || []) {
|
|
497
|
+
for (const t of spec.tests || []) {
|
|
498
|
+
const lastResult = t.results?.[t.results.length - 1];
|
|
499
|
+
const annotations = t.annotations || [];
|
|
500
|
+
const skipAnnotation = annotations.find(
|
|
501
|
+
(a) => a.type === "skip" || a.type === "fixme",
|
|
502
|
+
);
|
|
503
|
+
|
|
504
|
+
let status;
|
|
505
|
+
if (
|
|
506
|
+
t.expectedStatus === "skipped" ||
|
|
507
|
+
lastResult?.status === "skipped"
|
|
508
|
+
) {
|
|
509
|
+
status = "skipped";
|
|
510
|
+
} else if (lastResult?.status === "passed") {
|
|
511
|
+
status = "passed";
|
|
512
|
+
} else {
|
|
513
|
+
status = "failed";
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const screenshots = (lastResult?.attachments || [])
|
|
517
|
+
.filter((a) => a.contentType?.startsWith("image/"))
|
|
518
|
+
.map((a) => a.path);
|
|
519
|
+
|
|
520
|
+
tests.push({
|
|
521
|
+
name: spec.title,
|
|
522
|
+
suite: suite.title,
|
|
523
|
+
file: suite.file || spec.file || "",
|
|
524
|
+
status,
|
|
525
|
+
duration: lastResult?.duration || 0,
|
|
526
|
+
error: lastResult?.error
|
|
527
|
+
? {
|
|
528
|
+
message: lastResult.error.message || "",
|
|
529
|
+
stack: lastResult.error.stack || "",
|
|
530
|
+
expected: lastResult.error.expected,
|
|
531
|
+
actual: lastResult.error.actual,
|
|
532
|
+
}
|
|
533
|
+
: null,
|
|
534
|
+
skipReason:
|
|
535
|
+
skipAnnotation?.description ||
|
|
536
|
+
(status === "skipped"
|
|
537
|
+
? "Test was skipped (conditional skip or missing prerequisite)"
|
|
538
|
+
: null),
|
|
539
|
+
screenshots,
|
|
540
|
+
retries: (t.results?.length || 1) - 1,
|
|
541
|
+
});
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (suite.suites) flattenPwSuites(suite.suites, tests);
|
|
477
545
|
}
|
|
478
546
|
}
|
package/src/core/constants.js
CHANGED