@@ -5,6 +5,7 @@ import type { Vitest } from '../core'
55import type { TestSpecification } from '../test-specification'
66import type { Reporter , TestRunEndReason } from '../types/reporter'
77import type { TestCase , TestCollection , TestModule , TestModuleState , TestResult , TestSuite , TestSuiteState } from './reported-tasks'
8+ import { readFileSync } from 'node:fs'
89import { performance } from 'node:perf_hooks'
910import { getSuites , getTestName , getTests , hasFailed } from '@vitest/runner/utils'
1011import { toArray } from '@vitest/utils/helpers'
@@ -14,6 +15,7 @@ import c from 'tinyrainbow'
1415import { groupBy } from '../../utils/base'
1516import { isTTY } from '../../utils/env'
1617import { hasFailedSnapshot } from '../../utils/tasks'
18+ import { generateCodeFrame , printStack } from '../printError'
1719import { F_CHECK , F_DOWN_RIGHT , F_POINTER } from './renderers/figures'
1820import {
1921 countTestErrors ,
@@ -519,6 +521,7 @@ export abstract class BaseReporter implements Reporter {
519521
520522 reportSummary ( files : File [ ] , errors : unknown [ ] ) : void {
521523 this . printErrorsSummary ( files , errors )
524+ this . printLeaksSummary ( )
522525
523526 if ( this . ctx . config . mode === 'benchmark' ) {
524527 this . reportBenchmarkSummary ( files )
@@ -572,6 +575,12 @@ export abstract class BaseReporter implements Reporter {
572575 )
573576 }
574577
578+ const leaks = this . ctx . state . leakSet . size
579+
580+ if ( leaks ) {
581+ this . log ( padSummaryTitle ( 'Leaks' ) , c . bold ( c . red ( `${ leaks } leak${ leaks > 1 ? 's' : '' } ` ) ) )
582+ }
583+
575584 this . log ( padSummaryTitle ( 'Start at' ) , this . _timeStart )
576585
577586 const collectTime = sum ( files , file => file . collectDuration )
@@ -747,6 +756,39 @@ export abstract class BaseReporter implements Reporter {
747756 }
748757 }
749758
759+ private printLeaksSummary ( ) {
760+ const leaks = this . ctx . state . leakSet
761+
762+ if ( leaks . size ) {
763+ this . error ( `\n${ errorBanner ( `Async Leaks ${ leaks . size } ` ) } \n` )
764+
765+ for ( const leak of leaks ) {
766+ const filename = this . relative ( leak . filename )
767+
768+ this . ctx . logger . error ( c . red ( `${ leak . type } leaking in ${ filename } ` ) )
769+
770+ const stacks = parseStacktrace ( leak . stack )
771+
772+ const sourceCode = readFileSync ( stacks [ 0 ] . file , 'utf-8' )
773+ this . ctx . logger . error ( generateCodeFrame (
774+ sourceCode . length > 100_000
775+ ? sourceCode
776+ : this . ctx . logger . highlight ( stacks [ 0 ] . file , sourceCode ) ,
777+ undefined ,
778+ stacks [ 0 ] ,
779+ ) )
780+
781+ printStack (
782+ this . ctx . logger ,
783+ this . ctx . getProjectByName ( leak . projectName ) ,
784+ stacks ,
785+ stacks [ 0 ] ,
786+ { } ,
787+ )
788+ }
789+ }
790+ }
791+
750792 reportBenchmarkSummary ( files : File [ ] ) : void {
751793 const benches = getTests ( files )
752794 const topBenches = benches . filter ( i => i . result ?. benchmark ?. rank === 1 )
0 commit comments