2020-03-31 20:42:07 +08:00
'use strict' ;
Object . defineProperty ( exports , '__esModule' , {
value : true
} ) ;
exports . invariant = invariant ;
exports . addErrorToEachTestUnderDescribe = exports . getTestID = exports . makeRunResult = exports . getTestDuration = exports . callAsyncCircusFn = exports . describeBlockHasTests = exports . getEachHooksForTest = exports . getAllHooksForDescribe = exports . makeTest = exports . makeDescribe = void 0 ;
var _jestUtil = require ( 'jest-util' ) ;
var _isGeneratorFn = _interopRequireDefault ( require ( 'is-generator-fn' ) ) ;
var _co = _interopRequireDefault ( require ( 'co' ) ) ;
2020-05-15 05:33:08 +08:00
var _dedent = _interopRequireDefault ( require ( 'dedent' ) ) ;
2020-03-31 20:42:07 +08:00
var _stackUtils = _interopRequireDefault ( require ( 'stack-utils' ) ) ;
var _prettyFormat = _interopRequireDefault ( require ( 'pretty-format' ) ) ;
var _state = require ( './state' ) ;
function _interopRequireDefault ( obj ) {
return obj && obj . _ _esModule ? obj : { default : obj } ;
}
var Symbol = global [ 'jest-symbol-do-not-touch' ] || global . Symbol ;
var Symbol = global [ 'jest-symbol-do-not-touch' ] || global . Symbol ;
var jestNow = global [ Symbol . for ( 'jest-native-now' ) ] || global . Date . now ;
var Symbol = global [ 'jest-symbol-do-not-touch' ] || global . Symbol ;
var Promise = global [ Symbol . for ( 'jest-native-promise' ) ] || global . Promise ;
const stackUtils = new _stackUtils . default ( {
cwd : 'A path that does not exist'
} ) ;
const makeDescribe = ( name , parent , mode ) => {
let _mode = mode ;
if ( parent && ! mode ) {
// If not set explicitly, inherit from the parent describe.
_mode = parent . mode ;
}
return {
2020-05-15 05:33:08 +08:00
type : 'describeBlock' ,
// eslint-disable-next-line sort-keys
2020-03-31 20:42:07 +08:00
children : [ ] ,
hooks : [ ] ,
mode : _mode ,
name : ( 0 , _jestUtil . convertDescriptorToString ) ( name ) ,
parent ,
tests : [ ]
} ;
} ;
exports . makeDescribe = makeDescribe ;
const makeTest = ( fn , mode , name , parent , timeout , asyncError ) => ( {
2020-05-15 05:33:08 +08:00
type : 'test' ,
// eslint-disable-next-line sort-keys
2020-03-31 20:42:07 +08:00
asyncError ,
duration : null ,
errors : [ ] ,
fn ,
invocations : 0 ,
mode ,
name : ( 0 , _jestUtil . convertDescriptorToString ) ( name ) ,
parent ,
startedAt : null ,
status : null ,
timeout
} ) ; // Traverse the tree of describe blocks and return true if at least one describe
// block has an enabled test.
exports . makeTest = makeTest ;
const hasEnabledTest = describeBlock => {
const { hasFocusedTests , testNamePattern } = ( 0 , _state . getState ) ( ) ;
2020-05-15 05:33:08 +08:00
return describeBlock . children . some ( child =>
child . type === 'describeBlock'
? hasEnabledTest ( child )
: ! (
child . mode === 'skip' ||
( hasFocusedTests && child . mode !== 'only' ) ||
( testNamePattern && ! testNamePattern . test ( getTestID ( child ) ) )
)
2020-03-31 20:42:07 +08:00
) ;
} ;
const getAllHooksForDescribe = describe => {
const result = {
afterAll : [ ] ,
beforeAll : [ ]
} ;
if ( hasEnabledTest ( describe ) ) {
for ( const hook of describe . hooks ) {
switch ( hook . type ) {
case 'beforeAll' :
result . beforeAll . push ( hook ) ;
break ;
case 'afterAll' :
result . afterAll . push ( hook ) ;
break ;
}
}
}
return result ;
} ;
exports . getAllHooksForDescribe = getAllHooksForDescribe ;
const getEachHooksForTest = test => {
const result = {
afterEach : [ ] ,
beforeEach : [ ]
} ;
let block = test . parent ;
do {
const beforeEachForCurrentBlock = [ ] ; // TODO: inline after https://github.com/microsoft/TypeScript/pull/34840 is released
let hook ;
for ( hook of block . hooks ) {
switch ( hook . type ) {
case 'beforeEach' :
beforeEachForCurrentBlock . push ( hook ) ;
break ;
case 'afterEach' :
result . afterEach . push ( hook ) ;
break ;
}
} // 'beforeEach' hooks are executed from top to bottom, the opposite of the
// way we traversed it.
result . beforeEach = [ ... beforeEachForCurrentBlock , ... result . beforeEach ] ;
} while ( ( block = block . parent ) ) ;
return result ;
} ;
exports . getEachHooksForTest = getEachHooksForTest ;
const describeBlockHasTests = describe =>
2020-05-15 05:33:08 +08:00
describe . children . some (
child => child . type === 'test' || describeBlockHasTests ( child )
) ;
2020-03-31 20:42:07 +08:00
exports . describeBlockHasTests = describeBlockHasTests ;
const _makeTimeoutMessage = ( timeout , isHook ) =>
2020-05-15 05:33:08 +08:00
` Exceeded timeout of ${ ( 0 , _jestUtil . formatTime ) ( timeout ) } for a ${
2020-03-31 20:42:07 +08:00
isHook ? 'hook' : 'test'
} . \ nUse jest . setTimeout ( newTimeout ) to increase the timeout value , if this is a long - running test . ` ; // Global values can be overwritten by mocks or tests. We'll capture
// the original values in the variables before we require any files.
const { setTimeout , clearTimeout } = global ;
function checkIsError ( error ) {
return ! ! ( error && error . message && error . stack ) ;
}
2020-05-15 05:33:08 +08:00
const callAsyncCircusFn = ( fn , testContext , asyncError , { isHook , timeout } ) => {
2020-03-31 20:42:07 +08:00
let timeoutID ;
let completed = false ;
return new Promise ( ( resolve , reject ) => {
timeoutID = setTimeout (
( ) => reject ( _makeTimeoutMessage ( timeout , ! ! isHook ) ) ,
timeout
) ; // If this fn accepts `done` callback we return a promise that fulfills as
// soon as `done` called.
if ( fn . length ) {
2020-05-15 05:33:08 +08:00
let returnedValue = undefined ;
2020-03-31 20:42:07 +08:00
const done = reason => {
2020-05-15 05:33:08 +08:00
// We need to keep a stack here before the promise tick
const errorAtDone = new Error ( ) ; // Use `Promise.resolve` to allow the event loop to go a single tick in case `done` is called synchronously
Promise . resolve ( ) . then ( ( ) => {
if ( returnedValue !== undefined ) {
asyncError . message = ( 0 , _dedent . default ) `
Test functions cannot both take a 'done' callback and return something . Either use a 'done' callback , or return a promise .
Returned value : $ { ( 0 , _prettyFormat . default ) ( returnedValue , {
maxDepth : 3
} ) }
` ;
return reject ( asyncError ) ;
}
let errorAsErrorObject ;
if ( checkIsError ( reason ) ) {
errorAsErrorObject = reason ;
} else {
errorAsErrorObject = errorAtDone ;
errorAtDone . message = ` Failed: ${ ( 0 , _prettyFormat . default ) (
reason ,
{
2020-03-31 20:42:07 +08:00
maxDepth : 3
2020-05-15 05:33:08 +08:00
}
) } ` ;
} // Consider always throwing, regardless if `reason` is set or not
if ( completed && reason ) {
errorAsErrorObject . message =
'Caught error after test environment was torn down\n\n' +
errorAsErrorObject . message ;
throw errorAsErrorObject ;
}
return reason ? reject ( errorAsErrorObject ) : resolve ( ) ;
} ) ;
2020-03-31 20:42:07 +08:00
} ;
2020-05-15 05:33:08 +08:00
returnedValue = fn . call ( testContext , done ) ;
return ;
2020-03-31 20:42:07 +08:00
}
let returnedValue ;
if ( ( 0 , _isGeneratorFn . default ) ( fn ) ) {
returnedValue = _co . default . wrap ( fn ) . call ( { } ) ;
} else {
try {
returnedValue = fn . call ( testContext ) ;
} catch ( error ) {
2020-05-15 05:33:08 +08:00
reject ( error ) ;
return ;
2020-03-31 20:42:07 +08:00
}
} // If it's a Promise, return it. Test for an object with a `then` function
// to support custom Promise implementations.
if (
typeof returnedValue === 'object' &&
returnedValue !== null &&
typeof returnedValue . then === 'function'
) {
2020-05-15 05:33:08 +08:00
returnedValue . then ( resolve , reject ) ;
return ;
2020-03-31 20:42:07 +08:00
}
2020-05-15 05:33:08 +08:00
if ( ! isHook && returnedValue !== undefined ) {
reject (
new Error ( ( 0 , _dedent . default ) `
2020-03-31 20:42:07 +08:00
test functions can only return Promise or undefined .
2020-05-15 05:33:08 +08:00
Returned value : $ { ( 0 , _prettyFormat . default ) ( returnedValue , {
maxDepth : 3
} ) }
2020-03-31 20:42:07 +08:00
` )
) ;
2020-05-15 05:33:08 +08:00
return ;
2020-03-31 20:42:07 +08:00
} // Otherwise this test is synchronous, and if it didn't throw it means
// it passed.
2020-05-15 05:33:08 +08:00
resolve ( ) ;
2020-03-31 20:42:07 +08:00
} )
. then ( ( ) => {
completed = true ; // If timeout is not cleared/unrefed the node process won't exit until
// it's resolved.
timeoutID . unref && timeoutID . unref ( ) ;
clearTimeout ( timeoutID ) ;
} )
. catch ( error => {
completed = true ;
timeoutID . unref && timeoutID . unref ( ) ;
clearTimeout ( timeoutID ) ;
throw error ;
} ) ;
} ;
exports . callAsyncCircusFn = callAsyncCircusFn ;
const getTestDuration = test => {
const { startedAt } = test ;
return typeof startedAt === 'number' ? jestNow ( ) - startedAt : null ;
} ;
exports . getTestDuration = getTestDuration ;
const makeRunResult = ( describeBlock , unhandledErrors ) => ( {
testResults : makeTestResults ( describeBlock ) ,
unhandledErrors : unhandledErrors . map ( _formatError )
} ) ;
exports . makeRunResult = makeRunResult ;
const makeTestResults = describeBlock => {
const { includeTestLocationInResult } = ( 0 , _state . getState ) ( ) ;
2020-05-15 05:33:08 +08:00
const testResults = [ ] ;
2020-03-31 20:42:07 +08:00
2020-05-15 05:33:08 +08:00
for ( const child of describeBlock . children ) {
switch ( child . type ) {
case 'describeBlock' : {
testResults . push ( ... makeTestResults ( child ) ) ;
break ;
2020-03-31 20:42:07 +08:00
}
2020-05-15 05:33:08 +08:00
case 'test' :
{
const testPath = [ ] ;
let parent = child ;
do {
testPath . unshift ( parent . name ) ;
} while ( ( parent = parent . parent ) ) ;
const { status } = child ;
if ( ! status ) {
throw new Error ( 'Status should be present after tests are run.' ) ;
}
let location = null ;
if ( includeTestLocationInResult ) {
const stackLine = child . asyncError . stack . split ( '\n' ) [ 1 ] ;
const parsedLine = stackUtils . parseLine ( stackLine ) ;
if (
parsedLine &&
typeof parsedLine . column === 'number' &&
typeof parsedLine . line === 'number'
) {
location = {
column : parsedLine . column ,
line : parsedLine . line
} ;
}
}
testResults . push ( {
duration : child . duration ,
errors : child . errors . map ( _formatError ) ,
invocations : child . invocations ,
location ,
status ,
testPath
} ) ;
}
break ;
}
2020-03-31 20:42:07 +08:00
}
return testResults ;
} ; // Return a string that identifies the test (concat of parent describe block
// names + test title)
const getTestID = test => {
const titles = [ ] ;
let parent = test ;
do {
titles . unshift ( parent . name ) ;
} while ( ( parent = parent . parent ) ) ;
titles . shift ( ) ; // remove TOP_DESCRIBE_BLOCK_NAME
return titles . join ( ' ' ) ;
} ;
exports . getTestID = getTestID ;
const _formatError = errors => {
let error ;
let asyncError ;
if ( Array . isArray ( errors ) ) {
error = errors [ 0 ] ;
asyncError = errors [ 1 ] ;
} else {
error = errors ;
asyncError = new Error ( ) ;
}
if ( error ) {
if ( error . stack ) {
return error . stack ;
}
if ( error . message ) {
return error . message ;
}
}
asyncError . message = ` thrown: ${ ( 0 , _prettyFormat . default ) ( error , {
maxDepth : 3
} ) } ` ;
return asyncError . stack ;
} ;
const addErrorToEachTestUnderDescribe = ( describeBlock , error , asyncError ) => {
for ( const child of describeBlock . children ) {
2020-05-15 05:33:08 +08:00
switch ( child . type ) {
case 'describeBlock' :
addErrorToEachTestUnderDescribe ( child , error , asyncError ) ;
break ;
case 'test' :
child . errors . push ( [ error , asyncError ] ) ;
break ;
}
2020-03-31 20:42:07 +08:00
}
} ;
exports . addErrorToEachTestUnderDescribe = addErrorToEachTestUnderDescribe ;
function invariant ( condition , message ) {
if ( ! condition ) {
throw new Error ( message ) ;
}
}