@@ -3,6 +3,7 @@ const path = require('path')
3
3
const mkdirp = require ( 'mkdirp' )
4
4
const crypto = require ( 'crypto' )
5
5
const { template } = require ( '../utils' )
6
+ const { getMachineInfo } = require ( '../command/info' )
6
7
7
8
const event = require ( '../event' )
8
9
const output = require ( '../output' )
@@ -329,10 +330,10 @@ module.exports = function (config) {
329
330
}
330
331
331
332
// Get system information
332
- const systemInfo = await getSystemInfo ( )
333
+ const systemInfo = await getMachineInfo ( )
333
334
334
335
const html = template ( getHtmlTemplate ( ) , {
335
- title : ' CodeceptJS Test Report' ,
336
+ title : ` CodeceptJS Test Report v ${ Codecept . version ( ) } ` ,
336
337
timestamp : data . endTime . toISOString ( ) ,
337
338
duration : formatDuration ( data . duration ) ,
338
339
stats : JSON . stringify ( data . stats ) ,
@@ -348,7 +349,6 @@ module.exports = function (config) {
348
349
failuresDisplay : data . failures && data . failures . length > 0 ? 'block' : 'none' ,
349
350
codeceptVersion : Codecept . version ( ) ,
350
351
systemInfoHtml : generateSystemInfoHtml ( systemInfo ) ,
351
- codeceptLogo : getCodeceptLogo ( ) ,
352
352
} )
353
353
354
354
fs . writeFileSync ( reportPath , html )
@@ -402,7 +402,7 @@ module.exports = function (config) {
402
402
return tests
403
403
. map ( test => {
404
404
const statusClass = test . state || 'unknown'
405
- const feature = test . isBdd && test . feature ? test . feature . name : ( test . parent ?. title || 'Unknown Feature' )
405
+ const feature = test . isBdd && test . feature ? test . feature . name : test . parent ?. title || 'Unknown Feature'
406
406
const steps = config . showSteps && test . steps ? ( test . isBdd ? generateBddStepsHtml ( test . steps ) : generateStepsHtml ( test . steps ) ) : ''
407
407
const featureDetails = test . isBdd && test . feature ? generateBddFeatureHtml ( test . feature ) : ''
408
408
const hooks = test . hooks && test . hooks . length > 0 ? generateHooksHtml ( test . hooks ) : ''
@@ -506,8 +506,7 @@ module.exports = function (config) {
506
506
if ( ! feature ) return ''
507
507
508
508
const description = feature . description ? `<div class="feature-description">${ escapeHtml ( feature . description ) } </div>` : ''
509
- const featureTags = feature . tags && feature . tags . length > 0 ?
510
- `<div class="feature-tags">${ feature . tags . map ( tag => `<span class="feature-tag">${ escapeHtml ( tag ) } </span>` ) . join ( '' ) } </div>` : ''
509
+ const featureTags = feature . tags && feature . tags . length > 0 ? `<div class="feature-tags">${ feature . tags . map ( tag => `<span class="feature-tag">${ escapeHtml ( tag ) } </span>` ) . join ( '' ) } </div>` : ''
511
510
512
511
return `
513
512
<div class="bdd-feature-section">
@@ -677,6 +676,48 @@ module.exports = function (config) {
677
676
return unsafe . replace ( / & / g, '&' ) . replace ( / < / g, '<' ) . replace ( / > / g, '>' ) . replace ( / " / g, '"' ) . replace ( / ' / g, ''' )
678
677
}
679
678
679
+ function generateSystemInfoHtml ( systemInfo ) {
680
+ if ( ! systemInfo ) return ''
681
+
682
+ const formatInfo = ( key , value ) => {
683
+ if ( Array . isArray ( value ) && value . length > 1 ) {
684
+ return `<div class="info-item"><span class="info-key">${ key } :</span> <span class="info-value">${ escapeHtml ( value [ 1 ] ) } </span></div>`
685
+ } else if ( typeof value === 'string' && value !== 'N/A' && value !== 'undefined' ) {
686
+ return `<div class="info-item"><span class="info-key">${ key } :</span> <span class="info-value">${ escapeHtml ( value ) } </span></div>`
687
+ }
688
+ return ''
689
+ }
690
+
691
+ const infoItems = [
692
+ formatInfo ( 'Node.js' , systemInfo . nodeInfo ) ,
693
+ formatInfo ( 'OS' , systemInfo . osInfo ) ,
694
+ formatInfo ( 'CPU' , systemInfo . cpuInfo ) ,
695
+ formatInfo ( 'Chrome' , systemInfo . chromeInfo ) ,
696
+ formatInfo ( 'Edge' , systemInfo . edgeInfo ) ,
697
+ formatInfo ( 'Firefox' , systemInfo . firefoxInfo ) ,
698
+ formatInfo ( 'Safari' , systemInfo . safariInfo ) ,
699
+ formatInfo ( 'Playwright Browsers' , systemInfo . playwrightBrowsers ) ,
700
+ ]
701
+ . filter ( item => item )
702
+ . join ( '' )
703
+
704
+ if ( ! infoItems ) return ''
705
+
706
+ return `
707
+ <section class="system-info-section">
708
+ <div class="system-info-header" onclick="toggleSystemInfo()">
709
+ <h3>Environment Information</h3>
710
+ <span class="toggle-icon">▼</span>
711
+ </div>
712
+ <div class="system-info-content" id="systemInfoContent">
713
+ <div class="system-info-grid">
714
+ ${ infoItems }
715
+ </div>
716
+ </div>
717
+ </section>
718
+ `
719
+ }
720
+
680
721
function getHtmlTemplate ( ) {
681
722
return `
682
723
<!DOCTYPE html>
@@ -697,6 +738,8 @@ module.exports = function (config) {
697
738
</header>
698
739
699
740
<main class="report-content">
741
+ {{systemInfoHtml}}
742
+
700
743
<section class="stats-section">
701
744
<h2>Test Statistics</h2>
702
745
{{statsHtml}}
@@ -830,7 +873,7 @@ body {
830
873
padding: 0 1rem;
831
874
}
832
875
833
- .stats-section, .tests-section, .failures-section, .retries-section, .filters-section, .history-section {
876
+ .stats-section, .tests-section, .failures-section, .retries-section, .filters-section, .history-section, .system-info-section {
834
877
background: white;
835
878
margin-bottom: 2rem;
836
879
border-radius: 8px;
@@ -1352,6 +1395,82 @@ body {
1352
1395
display: none !important;
1353
1396
}
1354
1397
1398
+ /* System Info Section */
1399
+ .system-info-section {
1400
+ background: white;
1401
+ margin-bottom: 2rem;
1402
+ border-radius: 8px;
1403
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
1404
+ overflow: hidden;
1405
+ }
1406
+
1407
+ .system-info-header {
1408
+ background: #2c3e50;
1409
+ color: white;
1410
+ padding: 1rem;
1411
+ cursor: pointer;
1412
+ display: flex;
1413
+ justify-content: space-between;
1414
+ align-items: center;
1415
+ transition: background-color 0.2s;
1416
+ }
1417
+
1418
+ .system-info-header:hover {
1419
+ background: #34495e;
1420
+ }
1421
+
1422
+ .system-info-header h3 {
1423
+ margin: 0;
1424
+ font-size: 1.2rem;
1425
+ }
1426
+
1427
+ .toggle-icon {
1428
+ font-size: 1rem;
1429
+ transition: transform 0.3s ease;
1430
+ }
1431
+
1432
+ .toggle-icon.rotated {
1433
+ transform: rotate(-180deg);
1434
+ }
1435
+
1436
+ .system-info-content {
1437
+ display: none;
1438
+ padding: 1.5rem;
1439
+ background: #f8f9fa;
1440
+ border-top: 1px solid #e9ecef;
1441
+ }
1442
+
1443
+ .system-info-content.visible {
1444
+ display: block;
1445
+ }
1446
+
1447
+ .system-info-grid {
1448
+ display: grid;
1449
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1450
+ gap: 1rem;
1451
+ }
1452
+
1453
+ .info-item {
1454
+ padding: 0.75rem;
1455
+ background: white;
1456
+ border-radius: 6px;
1457
+ border-left: 4px solid #3498db;
1458
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
1459
+ }
1460
+
1461
+ .info-key {
1462
+ font-weight: bold;
1463
+ color: #2c3e50;
1464
+ display: inline-block;
1465
+ min-width: 100px;
1466
+ }
1467
+
1468
+ .info-value {
1469
+ color: #34495e;
1470
+ font-family: 'Courier New', monospace;
1471
+ font-size: 0.9rem;
1472
+ }
1473
+
1355
1474
/* BDD/Gherkin specific styles */
1356
1475
.bdd-test {
1357
1476
border-left: 4px solid #8e44ad;
@@ -1501,6 +1620,19 @@ function closeImageModal() {
1501
1620
modal.style.display = 'none';
1502
1621
}
1503
1622
1623
+ function toggleSystemInfo() {
1624
+ const content = document.getElementById('systemInfoContent');
1625
+ const icon = document.querySelector('.toggle-icon');
1626
+
1627
+ if (content.classList.contains('visible')) {
1628
+ content.classList.remove('visible');
1629
+ icon.classList.remove('rotated');
1630
+ } else {
1631
+ content.classList.add('visible');
1632
+ icon.classList.add('rotated');
1633
+ }
1634
+ }
1635
+
1504
1636
// Filter functionality
1505
1637
function applyFilters() {
1506
1638
const statusFilter = Array.from(document.getElementById('statusFilter').selectedOptions).map(opt => opt.value);
0 commit comments