1
1
'use strict' ;
2
2
const {
3
3
ArrayPrototypeFlatMap,
4
+ ArrayPrototypeForEach,
4
5
ArrayPrototypeJoin,
5
6
ArrayPrototypeMap,
6
7
ArrayPrototypePush,
7
8
ArrayPrototypeReduce,
8
9
ArrayPrototypeSome,
9
- MathFloor,
10
10
MathMax,
11
11
MathMin,
12
12
NumberParseInt,
@@ -406,6 +406,32 @@ const kColumns = ['line %', 'branch %', 'funcs %'];
406
406
const kColumnsKeys = [ 'coveredLinePercent' , 'coveredBranchPercent' , 'coveredFunctionPercent' ] ;
407
407
const kSeparator = ' | ' ;
408
408
409
+ function buildFileTree ( summary ) {
410
+ const tree = { __proto__ : null } ;
411
+ let treeDepth = 0 ;
412
+ let longestFile = 0 ;
413
+
414
+ ArrayPrototypeForEach ( summary . files , ( file ) => {
415
+ const parts = relative ( summary . workingDirectory , file . path ) . split ( '/' ) ;
416
+ let current = tree ;
417
+ ArrayPrototypeForEach ( parts , ( part , index ) => {
418
+ if ( ! current [ part ] ) {
419
+ current [ part ] = { __proto__ : null } ;
420
+ }
421
+ current = current [ part ] ;
422
+ // If this is the last part, add the file to the tree
423
+ if ( index === parts . length - 1 ) {
424
+ current . file = file ;
425
+ current . finalPath = parts ;
426
+ }
427
+ } ) ;
428
+ treeDepth = MathMax ( treeDepth , parts . length ) ;
429
+ longestFile = MathMax ( longestFile , ...parts . map ( ( part ) => part . length ) ) ;
430
+ } ) ;
431
+
432
+ return { __proto__ : null , tree, treeDepth, longestFile } ;
433
+ }
434
+
409
435
function getCoverageReport ( pad , summary , symbol , color , table ) {
410
436
const prefix = `${ pad } ${ symbol } ` ;
411
437
let report = `${ color } ${ prefix } start of coverage report\n` ;
@@ -415,10 +441,15 @@ function getCoverageReport(pad, summary, symbol, color, table) {
415
441
let uncoveredLinesPadLength ;
416
442
let tableWidth ;
417
443
444
+ // Create a tree of file paths
445
+ const { tree, treeDepth, longestFile } = buildFileTree ( summary ) ;
446
+
418
447
if ( table ) {
419
- // Get expected column sizes
420
- filePadLength = table && ArrayPrototypeReduce ( summary . files , ( acc , file ) =>
421
- MathMax ( acc , relative ( summary . workingDirectory , file . path ) . length ) , 0 ) ;
448
+ // Calculate expected column sizes based on the tree
449
+ // Every level of the tree adds 2 to the column size + the longest file name
450
+ filePadLength = table && longestFile ;
451
+ filePadLength += 2 * treeDepth ;
452
+ // Get the maximum length of the file path or 'file'
422
453
filePadLength = MathMax ( filePadLength , 'file' . length ) ;
423
454
const fileWidth = filePadLength + 2 ;
424
455
@@ -437,9 +468,7 @@ function getCoverageReport(pad, summary, symbol, color, table) {
437
468
const columnsExtras = tableWidth - availableWidth ;
438
469
if ( table && columnsExtras > 0 ) {
439
470
// Ensure file name is sufficiently visible
440
- const minFilePad = MathMin ( 8 , filePadLength ) ;
441
- filePadLength -= MathFloor ( columnsExtras * 0.2 ) ;
442
- filePadLength = MathMax ( filePadLength , minFilePad ) ;
471
+ filePadLength = MathMin ( availableWidth * 0.5 , filePadLength ) ;
443
472
444
473
// Get rest of available space, subtracting margins
445
474
uncoveredLinesPadLength = MathMax ( availableWidth - columnsWidth - ( filePadLength + 2 ) - 2 , 1 ) ;
@@ -465,75 +494,49 @@ function getCoverageReport(pad, summary, symbol, color, table) {
465
494
return result ;
466
495
}
467
496
468
- function getfilePathMultiline ( string , width , pad , coverage ) {
469
- if ( ! table ) return string ;
470
-
471
- const lines = [ ] ;
472
- let currentLine = '' ;
473
-
474
- for ( const word of StringPrototypeSplit ( string , '\\' ) ) {
475
- if ( currentLine . length + word . length + 1 <= width ) {
476
- // If adding the next word fits in the current line, append it
477
- currentLine += ( currentLine . length > 0 ? '\\' : '' ) + word ;
478
- } else {
479
- // If adding the next word exceeds the width, start a new line
480
- ArrayPrototypePush ( lines , currentLine ) ;
481
- currentLine = word ;
482
- }
483
- }
484
-
485
- // Add the last line if any
486
- if ( currentLine . length > 0 ) {
487
- ArrayPrototypePush ( lines , currentLine ) ;
488
- }
489
-
490
- const truncatedLines = ArrayPrototypeMap ( lines , ( line ) => StringPrototypeSlice ( line , 0 , width ) ) ;
491
-
492
- // Delete empty lines if any
493
- for ( let i = 0 ; i < truncatedLines . length ; ++ i ) {
494
- if ( truncatedLines [ i ] . length === 0 ) {
495
- truncatedLines . splice ( i , 1 ) ;
496
- }
497
- }
498
-
499
- return getCell (
500
- ArrayPrototypeJoin ( truncatedLines , '\n' ) ,
501
- width ,
502
- pad ,
503
- false ,
504
- coverage ,
505
- ) ;
506
- }
507
-
508
497
// Head
509
498
if ( table ) report += addTableLine ( prefix , tableWidth ) ;
510
499
report += `${ prefix } ${ getCell ( 'file' , filePadLength , StringPrototypePadEnd , truncateEnd ) } ${ kSeparator } ` +
511
- `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( kColumns , ( column , i ) => getCell ( column , columnPadLengths [ i ] , StringPrototypePadStart ) ) , kSeparator ) } ${ kSeparator } ` +
512
- `${ getCell ( 'uncovered lines' , uncoveredLinesPadLength , false , truncateEnd ) } \n` ;
500
+ `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( kColumns , ( column , i ) => getCell ( column , columnPadLengths [ i ] , StringPrototypePadStart ) ) , kSeparator ) } ${ kSeparator } ` +
501
+ `${ getCell ( 'uncovered lines' , uncoveredLinesPadLength , false , truncateEnd ) } \n` ;
513
502
if ( table ) report += addTableLine ( prefix , tableWidth ) ;
514
503
515
- // Body
516
- for ( let i = 0 ; i < summary . files . length ; ++ i ) {
517
- const file = summary . files [ i ] ;
518
- const relativePath = relative ( summary . workingDirectory , file . path ) ;
519
-
520
- let fileCoverage = 0 ;
521
- const coverages = ArrayPrototypeMap ( kColumnsKeys , ( columnKey ) => {
522
- const percent = file [ columnKey ] ;
523
- fileCoverage += percent ;
524
- return percent ;
525
- } ) ;
526
- fileCoverage /= kColumnsKeys . length ;
504
+ // Body using the tree
505
+ function printTree ( tree , depth ) {
506
+ for ( const key in tree ) {
507
+ if ( tree [ key ] . file ) {
508
+ const file = tree [ key ] . file ;
509
+ const fileName = file . path . split ( '/' ) . pop ( ) ;
510
+
511
+ let fileCoverage = 0 ;
512
+ const coverages = ArrayPrototypeMap ( kColumnsKeys , ( columnKey ) => {
513
+ const percent = file [ columnKey ] ;
514
+ fileCoverage += percent ;
515
+ return percent ;
516
+ } ) ;
517
+ fileCoverage /= kColumnsKeys . length ;
518
+
519
+ report += `${ prefix } ${ StringPrototypeRepeat ( ' ' , depth ) } ${ getCell ( fileName , filePadLength - depth , StringPrototypePadEnd , false , fileCoverage ) } ${ kSeparator } ` +
520
+ `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( coverages , ( coverage , j ) => getCell ( NumberPrototypeToFixed ( coverage , 2 ) , columnPadLengths [ j ] , StringPrototypePadStart , false , coverage ) ) , kSeparator ) } ${ kSeparator } ` +
521
+ `${ getCell ( formatUncoveredLines ( getUncoveredLines ( file . lines ) , table ) , uncoveredLinesPadLength , false , truncateEnd ) } \n` ;
522
+ } else {
523
+ // Print a line for the directory with empty columns
524
+ report += `${ prefix } ${ StringPrototypeRepeat ( ' ' , depth ) } ${ getCell ( key , filePadLength - depth , StringPrototypePadEnd , false ) } ${ kSeparator } ` +
525
+ `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( columnPadLengths , ( columnPadLength ) => getCell ( '' , columnPadLength , StringPrototypePadStart , false ) ) , kSeparator ) } ${ kSeparator } ` +
526
+ `${ getCell ( '' , uncoveredLinesPadLength , false , truncateEnd ) } \n` ;
527
527
528
- report += `${ prefix } ${ getfilePathMultiline ( relativePath , filePadLength , StringPrototypePadEnd , fileCoverage ) } ${ kSeparator } ` +
529
- `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( coverages , ( coverage , j ) => getCell ( NumberPrototypeToFixed ( coverage , 2 ) , columnPadLengths [ j ] , StringPrototypePadStart , false , coverage ) ) , kSeparator ) } ${ kSeparator } ` +
530
- `${ getCell ( formatUncoveredLines ( getUncoveredLines ( file . lines ) , table ) , uncoveredLinesPadLength , false , truncateEnd ) } \n` ;
528
+ // Continue printing the tree recursively
529
+ printTree ( tree [ key ] , depth + 1 ) ;
530
+ }
531
+ }
531
532
}
533
+ printTree ( tree , 0 ) ;
534
+
532
535
533
536
// Foot
534
537
if ( table ) report += addTableLine ( prefix , tableWidth ) ;
535
538
report += `${ prefix } ${ getCell ( 'all files' , filePadLength , StringPrototypePadEnd , truncateEnd ) } ${ kSeparator } ` +
536
- `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( kColumnsKeys , ( columnKey , j ) => getCell ( NumberPrototypeToFixed ( summary . totals [ columnKey ] , 2 ) , columnPadLengths [ j ] , StringPrototypePadStart , false , summary . totals [ columnKey ] ) ) , kSeparator ) } |\n` ;
539
+ `${ ArrayPrototypeJoin ( ArrayPrototypeMap ( kColumnsKeys , ( columnKey , j ) => getCell ( NumberPrototypeToFixed ( summary . totals [ columnKey ] , 2 ) , columnPadLengths [ j ] , StringPrototypePadStart , false , summary . totals [ columnKey ] ) ) , kSeparator ) } |\n` ;
537
540
if ( table ) report += addTableLine ( prefix , tableWidth ) ;
538
541
539
542
report += `${ prefix } end of coverage report\n` ;
@@ -542,7 +545,6 @@ function getCoverageReport(pad, summary, symbol, color, table) {
542
545
}
543
546
return report ;
544
547
}
545
-
546
548
module . exports = {
547
549
convertStringToRegExp,
548
550
countCompletedTest,
0 commit comments