2017-12-08 00:37:22 +00:00
/ * *
* Copyright 2017 Google Inc . All rights reserved .
*
* Licensed under the Apache License , Version 2.0 ( the "License" ) ;
* you may not use this file except in compliance with the License .
* You may obtain a copy of the License at
*
* http : //www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing , software
* distributed under the License is distributed on an "AS IS" BASIS ,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND , either express or implied .
* See the License for the specific language governing permissions and
* limitations under the License .
* /
const RED _COLOR = '\x1b[31m' ;
const GREEN _COLOR = '\x1b[32m' ;
const YELLOW _COLOR = '\x1b[33m' ;
const RESET _COLOR = '\x1b[0m' ;
class Reporter {
2019-02-05 23:28:35 +00:00
constructor ( runner , options = { } ) {
const {
projectFolder = null ,
showSlowTests = 3 ,
2019-02-07 21:17:29 +00:00
verbose = false ,
summary = true ,
2019-02-05 23:28:35 +00:00
} = options ;
2017-12-08 00:37:22 +00:00
this . _runner = runner ;
2019-02-05 23:28:35 +00:00
this . _projectFolder = projectFolder ;
this . _showSlowTests = showSlowTests ;
2019-02-07 21:17:29 +00:00
this . _verbose = verbose ;
this . _summary = summary ;
this . _testCounter = 0 ;
2017-12-08 00:37:22 +00:00
runner . on ( 'started' , this . _onStarted . bind ( this ) ) ;
runner . on ( 'terminated' , this . _onTerminated . bind ( this ) ) ;
runner . on ( 'finished' , this . _onFinished . bind ( this ) ) ;
runner . on ( 'teststarted' , this . _onTestStarted . bind ( this ) ) ;
runner . on ( 'testfinished' , this . _onTestFinished . bind ( this ) ) ;
2018-07-31 01:57:48 +00:00
this . _workersState = new Map ( ) ;
2017-12-08 00:37:22 +00:00
}
2019-02-05 23:28:35 +00:00
_onStarted ( runnableTests ) {
2019-02-07 21:26:35 +00:00
this . _testCounter = 0 ;
2017-12-08 00:37:22 +00:00
this . _timestamp = Date . now ( ) ;
2019-02-05 23:28:35 +00:00
const allTests = this . _runner . tests ( ) ;
if ( allTests . length === runnableTests . length )
console . log ( ` Running all ${ YELLOW _COLOR } ${ runnableTests . length } ${ RESET _COLOR } tests on ${ YELLOW _COLOR } ${ this . _runner . parallel ( ) } ${ RESET _COLOR } worker(s): \n ` ) ;
else
console . log ( ` Running ${ YELLOW _COLOR } ${ runnableTests . length } ${ RESET _COLOR } focused tests out of total ${ YELLOW _COLOR } ${ allTests . length } ${ RESET _COLOR } on ${ YELLOW _COLOR } ${ this . _runner . parallel ( ) } ${ RESET _COLOR } worker(s): \n ` ) ;
2017-12-08 00:37:22 +00:00
}
_onTerminated ( message , error ) {
this . _printTestResults ( ) ;
console . log ( ` ${ RED _COLOR } ## TERMINATED ## ${ RESET _COLOR } ` ) ;
console . log ( 'Message:' ) ;
console . log ( ` ${ RED _COLOR } ${ message } ${ RESET _COLOR } ` ) ;
if ( error && error . stack ) {
console . log ( 'Stack:' ) ;
console . log ( error . stack . split ( '\n' ) . map ( line => ' ' + line ) . join ( '\n' ) ) ;
}
2018-07-31 01:57:48 +00:00
console . log ( 'WORKERS STATE' ) ;
const workerIds = Array . from ( this . _workersState . keys ( ) ) ;
2019-01-16 00:40:58 +00:00
workerIds . sort ( ( a , b ) => a - b ) ;
2018-07-31 01:57:48 +00:00
for ( const workerId of workerIds ) {
const { isRunning , test } = this . _workersState . get ( workerId ) ;
let description = '' ;
if ( isRunning )
description = ` ${ YELLOW _COLOR } RUNNING ${ RESET _COLOR } ` ;
else if ( test . result === 'ok' )
description = ` ${ GREEN _COLOR } OK ${ RESET _COLOR } ` ;
else if ( test . result === 'skipped' )
description = ` ${ YELLOW _COLOR } SKIPPED ${ RESET _COLOR } ` ;
else if ( test . result === 'failed' )
description = ` ${ RED _COLOR } FAILED ${ RESET _COLOR } ` ;
else if ( test . result === 'timedout' )
description = ` ${ RED _COLOR } TIMEDOUT ${ RESET _COLOR } ` ;
else
description = ` ${ RED _COLOR } <UNKNOWN> ${ RESET _COLOR } ` ;
console . log ( ` ${ workerId } : [ ${ description } ] ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
}
2019-07-16 23:34:41 +00:00
process . exitCode = 2 ;
2017-12-08 00:37:22 +00:00
}
_onFinished ( ) {
this . _printTestResults ( ) ;
const failedTests = this . _runner . failedTests ( ) ;
2019-07-16 23:34:41 +00:00
process . exitCode = failedTests . length > 0 ? 1 : 0 ;
2017-12-08 00:37:22 +00:00
}
_printTestResults ( ) {
// 2 newlines after completing all tests.
console . log ( '\n' ) ;
const failedTests = this . _runner . failedTests ( ) ;
2019-02-07 21:17:29 +00:00
if ( this . _summary && failedTests . length > 0 ) {
2017-12-08 00:37:22 +00:00
console . log ( '\nFailures:' ) ;
for ( let i = 0 ; i < failedTests . length ; ++ i ) {
const test = failedTests [ i ] ;
2018-07-31 01:57:48 +00:00
console . log ( ` ${ i + 1 } ) ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
2017-12-08 00:37:22 +00:00
if ( test . result === 'timedout' ) {
console . log ( ' Message:' ) ;
2019-02-07 21:17:29 +00:00
console . log ( ` ${ RED _COLOR } Timeout Exceeded ${ this . _runner . timeout ( ) } ms ${ RESET _COLOR } ` ) ;
2017-12-08 00:37:22 +00:00
} else {
console . log ( ' Message:' ) ;
2018-01-30 23:23:47 +00:00
console . log ( ` ${ RED _COLOR } ${ test . error . message || test . error } ${ RESET _COLOR } ` ) ;
2017-12-08 00:37:22 +00:00
console . log ( ' Stack:' ) ;
2018-09-17 22:22:53 +00:00
if ( test . error . stack )
console . log ( this . _beautifyStack ( test . error . stack ) ) ;
2017-12-08 00:37:22 +00:00
}
2018-05-29 22:45:03 +00:00
if ( test . output ) {
console . log ( ' Output:' ) ;
console . log ( test . output . split ( '\n' ) . map ( line => ' ' + line ) . join ( '\n' ) ) ;
}
2017-12-08 00:37:22 +00:00
console . log ( '' ) ;
}
}
2019-02-05 23:28:35 +00:00
const skippedTests = this . _runner . skippedTests ( ) ;
2019-02-07 21:17:29 +00:00
if ( this . _summary && skippedTests . length > 0 ) {
2017-12-08 00:37:22 +00:00
console . log ( '\nSkipped:' ) ;
for ( let i = 0 ; i < skippedTests . length ; ++ i ) {
const test = skippedTests [ i ] ;
2019-02-06 03:02:29 +00:00
console . log ( ` ${ i + 1 } ) ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
2019-02-05 23:28:35 +00:00
console . log ( ` ${ YELLOW _COLOR } Temporary disabled with xit ${ RESET _COLOR } ` ) ;
2017-12-08 00:37:22 +00:00
}
}
2019-02-05 23:28:35 +00:00
if ( this . _showSlowTests ) {
const slowTests = this . _runner . passedTests ( ) . sort ( ( a , b ) => {
const aDuration = a . endTimestamp - a . startTimestamp ;
const bDuration = b . endTimestamp - b . startTimestamp ;
return bDuration - aDuration ;
} ) . slice ( 0 , this . _showSlowTests ) ;
console . log ( ` \n Slowest tests: ` ) ;
for ( let i = 0 ; i < slowTests . length ; ++ i ) {
const test = slowTests [ i ] ;
const duration = test . endTimestamp - test . startTimestamp ;
2019-02-06 03:02:29 +00:00
console . log ( ` ( ${ i + 1 } ) ${ YELLOW _COLOR } ${ duration / 1000 } s ${ RESET _COLOR } - ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
2019-02-05 23:28:35 +00:00
}
}
const tests = this . _runner . tests ( ) ;
2017-12-08 00:37:22 +00:00
const executedTests = tests . filter ( test => test . result ) ;
2019-02-07 21:17:29 +00:00
const okTestsLength = executedTests . length - failedTests . length - skippedTests . length ;
let summaryText = '' ;
if ( failedTests . length || skippedTests . length ) {
const summary = [ ` ok - ${ GREEN _COLOR } ${ okTestsLength } ${ RESET _COLOR } ` ] ;
if ( failedTests . length )
summary . push ( ` failed - ${ RED _COLOR } ${ failedTests . length } ${ RESET _COLOR } ` ) ;
if ( skippedTests . length )
summary . push ( ` skipped - ${ YELLOW _COLOR } ${ skippedTests . length } ${ RESET _COLOR } ` ) ;
summaryText = ` ( ${ summary . join ( ', ' ) } ) ` ;
}
console . log ( ` \n Ran ${ executedTests . length } ${ summaryText } of ${ tests . length } test(s) ` ) ;
2017-12-08 00:37:22 +00:00
const milliseconds = Date . now ( ) - this . _timestamp ;
const seconds = milliseconds / 1000 ;
console . log ( ` Finished in ${ YELLOW _COLOR } ${ seconds } ${ RESET _COLOR } seconds ` ) ;
}
2018-09-17 22:22:53 +00:00
_beautifyStack ( stack ) {
2019-02-05 23:28:35 +00:00
if ( ! this . _projectFolder )
2018-09-17 22:22:53 +00:00
return stack ;
const lines = stack . split ( '\n' ) . map ( line => ' ' + line ) ;
// Find last stack line that include testrunner code.
let index = 0 ;
while ( index < lines . length && ! lines [ index ] . includes ( _ _dirname ) )
++ index ;
while ( index < lines . length && lines [ index ] . includes ( _ _dirname ) )
++ index ;
if ( index >= lines . length )
return stack ;
const line = lines [ index ] ;
2019-02-05 23:28:35 +00:00
const fromIndex = line . lastIndexOf ( this . _projectFolder ) + this . _projectFolder . length ;
2018-09-17 22:22:53 +00:00
const toIndex = line . lastIndexOf ( ')' ) ;
lines [ index ] = line . substring ( 0 , fromIndex ) + YELLOW _COLOR + line . substring ( fromIndex , toIndex ) + RESET _COLOR + line . substring ( toIndex ) ;
return lines . join ( '\n' ) ;
}
2018-07-31 01:57:48 +00:00
_onTestStarted ( test , workerId ) {
this . _workersState . set ( workerId , { test , isRunning : true } ) ;
2017-12-08 00:37:22 +00:00
}
2018-07-31 01:57:48 +00:00
_onTestFinished ( test , workerId ) {
this . _workersState . set ( workerId , { test , isRunning : false } ) ;
2019-02-07 21:17:29 +00:00
if ( this . _verbose ) {
++ this . _testCounter ;
if ( test . result === 'ok' ) {
console . log ( ` ${ this . _testCounter } ) ${ GREEN _COLOR } [ OK ] ${ RESET _COLOR } ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
} else if ( test . result === 'skipped' ) {
console . log ( ` ${ this . _testCounter } ) ${ YELLOW _COLOR } [SKIP] ${ RESET _COLOR } ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
} else if ( test . result === 'failed' ) {
console . log ( ` ${ this . _testCounter } ) ${ RED _COLOR } [FAIL] ${ RESET _COLOR } ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
console . log ( ' Message:' ) ;
console . log ( ` ${ RED _COLOR } ${ test . error . message || test . error } ${ RESET _COLOR } ` ) ;
console . log ( ' Stack:' ) ;
if ( test . error . stack )
console . log ( this . _beautifyStack ( test . error . stack ) ) ;
if ( test . output ) {
console . log ( ' Output:' ) ;
console . log ( test . output . split ( '\n' ) . map ( line => ' ' + line ) . join ( '\n' ) ) ;
}
} else if ( test . result === 'timedout' ) {
console . log ( ` ${ this . _testCounter } ) ${ RED _COLOR } [TIME] ${ RESET _COLOR } ${ test . fullName } ( ${ formatTestLocation ( test ) } ) ` ) ;
console . log ( ' Message:' ) ;
console . log ( ` ${ RED _COLOR } Timeout Exceeded ${ this . _runner . timeout ( ) } ms ${ RESET _COLOR } ` ) ;
}
} else {
if ( test . result === 'ok' )
process . stdout . write ( ` ${ GREEN _COLOR } . ${ RESET _COLOR } ` ) ;
else if ( test . result === 'skipped' )
process . stdout . write ( ` ${ YELLOW _COLOR } * ${ RESET _COLOR } ` ) ;
else if ( test . result === 'failed' )
process . stdout . write ( ` ${ RED _COLOR } F ${ RESET _COLOR } ` ) ;
else if ( test . result === 'timedout' )
process . stdout . write ( ` ${ RED _COLOR } T ${ RESET _COLOR } ` ) ;
}
2017-12-08 00:37:22 +00:00
}
}
2018-07-31 01:57:48 +00:00
function formatTestLocation ( test ) {
const location = test . location ;
if ( ! location )
return '' ;
2019-02-06 03:02:29 +00:00
return ` ${ YELLOW _COLOR } ${ location . fileName } : ${ location . lineNumber } : ${ location . columnNumber } ${ RESET _COLOR } ` ;
2018-07-31 01:57:48 +00:00
}
2017-12-08 00:37:22 +00:00
module . exports = Reporter ;