license import { ifEnvSupports } from '../test-util' ; describe ( 'AsyncTestZoneSpec' , function ( ) { let log ; const AsyncTestZoneSpec = ( Zone as any ) [ 'AsyncTestZoneSpec' ] ; function finishCallback ( ) { log . push ( 'finish' ) ; } function failCallback ( ) { log . push ( 'fail' ) ; } beforeEach ( ( ) => { log = [ ] ; } ) ; it ( 'should call finish after zone is run in sync call' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , failCallback , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { finished = true ; } ) ; } ) ; it ( 'should call finish after a setTimeout is done' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { setTimeout ( ( ) => { finished = true ; } , 10 ) ; } ) ; } ) ; it ( 'should call finish after microtasks are done' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { Promise . resolve ( ) . then ( ( ) => { finished = true ; } ) ; } ) ; } ) ; it ( 'should call finish after both micro and macrotasks are done' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { new Promise ( ( resolve ) => { setTimeout ( ( ) => { resolve ( ) ; } , 10 ) ; } ) . then ( ( ) => { finished = true ; } ) ; } ) ; } ) ; it ( 'should call finish after both macro and microtasks are done' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { Promise . resolve ( ) . then ( ( ) => { setTimeout ( ( ) => { finished = true ; } , 10 ) ; } ) ; } ) ; } ) ; describe ( 'event tasks' , ifEnvSupports ( 'document' , ( ) => { let button ; beforeEach ( function ( ) { button = document . createElement ( 'button' ) ; document . body . appendChild ( button ) ; } ) ; afterEach ( function ( ) { document . body . removeChild ( button ) ; } ) ; it ( 'should call finish because an event task is considered as sync' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { const listener = ( ) => { finished = true ; } ; button . addEventListener ( 'click' , listener ) ; const clickEvent = document . createEvent ( 'Event' ) ; clickEvent . initEvent ( 'click' , true , true ) ; button . dispatchEvent ( clickEvent ) ; } ) ; } ) ; it ( 'should call finish after an event task is done asynchronously' , ( done ) => { let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { button . addEventListener ( 'click' , ( ) => { setTimeout ( ( ) => { finished = true ; } , 10 ) ; } ) ; const clickEvent = document . createEvent ( 'Event' ) ; clickEvent . initEvent ( 'click' , true , true ) ; button . dispatchEvent ( clickEvent ) ; } ) ; } ) ; } ) ) ; describe ( 'XHRs' , ifEnvSupports ( 'XMLHttpRequest' , ( ) => { it ( 'should wait for XHRs to complete' , function ( done ) { let req ; let finished = false ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { expect ( finished ) . toBe ( true ) ; done ( ) ; } , ( err ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { req = new XMLHttpRequest ( ) ; req . onreadystatechange = ( ) => { if ( req . readyState === XMLHttpRequest . DONE ) { finished = true ; } } ; req . open ( 'get' , '/' , true ) ; req . send ( ) ; } ) ; } ) ; it ( 'should fail if an xhr fails' , function ( done ) { let req ; const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { done . fail ( 'expected failCallback to be called' ) ; } , ( err ) => { expect ( err . message ) . toEqual ( 'bad url failure' ) ; done ( ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { req = new XMLHttpRequest ( ) ; req . onload = ( ) => { if ( req . status != 200 ) { throw new Error ( 'bad url failure' ) ; } } ; req . open ( 'get' , '/bad-url' , true ) ; req . send ( ) ; } ) ; } ) ; } ) ) ; it ( 'should not fail if setInterval is used and canceled' , ( done ) => { const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { done ( ) ; } , ( err ) => { done . fail ( 'async zone called failCallback unexpectedly' ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { let id = setInterval ( ( ) => { clearInterval ( id ) ; } , 100 ) ; } ) ; } ) ; it ( 'should fail if an error is thrown asynchronously' , ( done ) => { const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { done . fail ( 'expected failCallback to be called' ) ; } , ( err ) => { expect ( err . message ) . toEqual ( 'my error' ) ; done ( ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { setTimeout ( ( ) => { throw new Error ( 'my error' ) ; } , 10 ) ; } ) ; } ) ; it ( 'should fail if a promise rejection is unhandled' , ( done ) => { const testZoneSpec = new AsyncTestZoneSpec ( ( ) => { done . fail ( 'expected failCallback to be called' ) ; } , ( err ) => { expect ( err . message ) . toEqual ( 'Uncaught (in promise): my reason' ) ; done ( ) ; } , 'name' ) ; const atz = Zone . current . fork ( testZoneSpec ) ; atz . run ( function ( ) { Promise . reject ( 'my reason' ) ; } ) ; } ) ; const asyncTest = ( Zone as any ) [ Zone . __symbol__ ( 'asyncTest' ) ] ; function wrapAsyncTest ( fn , doneFn ? ) { return function ( done ) { const asyncWrapper = asyncTest ( fn ) ; return asyncWrapper . apply ( this , [ function ( ) { if ( doneFn ) { doneFn ( ) ; } return done . apply ( this , arguments ) ; } ] ) ; } ; } describe ( 'async' , ( ) => { describe ( 'non zone aware async task in promise should be detected' , ( ) => { let finished = false ; const _global = typeof window !== 'undefined' && window || typeof self !== 'undefined' && self || global ; beforeEach ( ( ) => { _global [ Zone . __symbol__ ( 'supportWaitUnResolvedChainedPromise' ) ] = true ; } ) ; afterEach ( ( ) => { _global [ Zone . __symbol__ ( 'supportWaitUnResolvedChainedPromise' ) ] = false ; } ) ; it ( 'should be able to detect non zone aware async task in promise' , wrapAsyncTest ( ( ) => { new Promise ( ( res , rej ) => { const g = typeof window === 'undefined' ? global : window ; g [ Zone . __symbol__ ( 'setTimeout' ) ] ( res , 100 ) ; } ) . then ( ( ) => { finished = true ; } ) ; } , ( ) => { expect ( finished ) . toBe ( true ) ; } ) ) ; } ) ; describe ( 'test without beforeEach' , ( ) => { const logs = [ ] ; it ( 'should automatically done after async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'timeout' ] ) ; logs . splice ( 0 ) ; } ) ) ; it ( 'should automatically done after all nested async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; setTimeout ( ( ) => { logs . push ( 'nested timeout' ) ; } , 100 ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'timeout' , 'nested timeout' ] ) ; logs . splice ( 0 ) ; } ) ) ; it ( 'should automatically done after multiple async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( '1st timeout' ) ; } , 100 ) ; setTimeout ( ( ) => { logs . push ( '2nd timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ '1st timeout' , '2nd timeout' ] ) ; logs . splice ( 0 ) ; } ) ) ; } ) ; describe ( 'test with sync beforeEach' , ( ) => { const logs = [ ] ; beforeEach ( ( ) => { logs . splice ( 0 ) ; logs . push ( 'beforeEach' ) ; } ) ; it ( 'should automatically done after async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'beforeEach' , 'timeout' ] ) ; } ) ) ; } ) ; describe ( 'test with async beforeEach' , ( ) => { const logs = [ ] ; beforeEach ( wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . splice ( 0 ) ; logs . push ( 'beforeEach' ) ; } , 100 ) ; } ) ) ; it ( 'should automatically done after async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'beforeEach' , 'timeout' ] ) ; } ) ) ; it ( 'should automatically done after all nested async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; setTimeout ( ( ) => { logs . push ( 'nested timeout' ) ; } , 100 ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'beforeEach' , 'timeout' , 'nested timeout' ] ) ; } ) ) ; it ( 'should automatically done after multiple async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( '1st timeout' ) ; } , 100 ) ; setTimeout ( ( ) => { logs . push ( '2nd timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'beforeEach' , '1st timeout' , '2nd timeout' ] ) ; } ) ) ; } ) ; describe ( 'test with async beforeEach and sync afterEach' , ( ) => { const logs = [ ] ; beforeEach ( wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { expect ( logs ) . toEqual ( [ ] ) ; logs . push ( 'beforeEach' ) ; } , 100 ) ; } ) ) ; afterEach ( ( ) => { logs . splice ( 0 ) ; } ) ; it ( 'should automatically done after async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'beforeEach' , 'timeout' ] ) ; } ) ) ; } ) ; describe ( 'test with async beforeEach and async afterEach' , ( ) => { const logs = [ ] ; beforeEach ( wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { expect ( logs ) . toEqual ( [ ] ) ; logs . push ( 'beforeEach' ) ; } , 100 ) ; } ) ) ; afterEach ( wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . splice ( 0 ) ; } , 100 ) ; } ) ) ; it ( 'should automatically done after async tasks finished' , wrapAsyncTest ( ( ) => { setTimeout ( ( ) => { logs . push ( 'timeout' ) ; } , 100 ) ; } , ( ) => { expect ( logs ) . toEqual ( [ 'beforeEach' , 'timeout' ] ) ; } ) ) ; } ) ; } ) ; } ) ;