license import { zoneSymbol } from '../../lib/common/utils' ; describe ( 'Zone' , function ( ) { const rootZone = Zone . current ; it ( 'should have a name' , function ( ) { expect ( Zone . current . name ) . toBeDefined ( ) ; } ) ; describe ( 'hooks' , function ( ) { it ( 'should throw if onError is not defined' , function ( ) { expect ( function ( ) { Zone . current . run ( throwError ) ; } ) . toThrow ( ) ; } ) ; it ( 'should fire onError if a function run by a zone throws' , function ( ) { const errorSpy = jasmine . createSpy ( 'error' ) ; const myZone = Zone . current . fork ( { name : 'spy' , onHandleError : errorSpy } ) ; expect ( errorSpy ) . not . toHaveBeenCalled ( ) ; expect ( function ( ) { myZone . runGuarded ( throwError ) ; } ) . not . toThrow ( ) ; expect ( errorSpy ) . toHaveBeenCalled ( ) ; } ) ; it ( 'should send correct currentZone in hook method when in nested zone' , function ( ) { const zone = Zone . current ; const zoneA = zone . fork ( { name : 'A' , onInvoke : function ( parentDelegate , currentZone , targetZone , callback , applyThis , applyArgs , source ) { expect ( currentZone . name ) . toEqual ( 'A' ) ; return parentDelegate . invoke ( targetZone , callback , applyThis , applyArgs , source ) ; } } ) ; const zoneB = zoneA . fork ( { name : 'B' , onInvoke : function ( parentDelegate , currentZone , targetZone , callback , applyThis , applyArgs , source ) { expect ( currentZone . name ) . toEqual ( 'B' ) ; return parentDelegate . invoke ( targetZone , callback , applyThis , applyArgs , source ) ; } } ) ; const zoneC = zoneB . fork ( { name : 'C' } ) ; zoneC . run ( function ( ) { } ) ; } ) ; } ) ; it ( 'should allow zones to be run from within another zone' , function ( ) { const zone = Zone . current ; const zoneA = zone . fork ( { name : 'A' } ) ; const zoneB = zone . fork ( { name : 'B' } ) ; zoneA . run ( function ( ) { zoneB . run ( function ( ) { expect ( Zone . current ) . toBe ( zoneB ) ; } ) ; expect ( Zone . current ) . toBe ( zoneA ) ; } ) ; expect ( Zone . current ) . toBe ( zone ) ; } ) ; describe ( 'wrap' , function ( ) { it ( 'should throw if argument is not a function' , function ( ) { expect ( function ( ) { ( < Function > Zone . current . wrap ) ( 11 ) ; } ) . toThrowError ( 'Expecting function got: 11' ) ; } ) ; } ) ; describe ( 'run out side of current zone' , function ( ) { it ( 'should be able to get root zone' , function ( ) { Zone . current . fork ( { name : 'testZone' } ) . run ( function ( ) { expect ( Zone . root . name ) . toEqual ( '<root>' ) ; } ) ; } ) ; it ( 'should be able to get run under rootZone' , function ( ) { Zone . current . fork ( { name : 'testZone' } ) . run ( function ( ) { Zone . root . run ( ( ) => { expect ( Zone . current . name ) . toEqual ( '<root>' ) ; } ) ; } ) ; } ) ; it ( 'should be able to get run outside of current zone' , function ( ) { Zone . current . fork ( { name : 'testZone' } ) . run ( function ( ) { Zone . root . fork ( { name : 'newTestZone' } ) . run ( ( ) => { expect ( Zone . current . name ) . toEqual ( 'newTestZone' ) ; expect ( Zone . current . parent ! . name ) . toEqual ( '<root>' ) ; } ) ; } ) ; } ) ; } ) ; describe ( 'get' , function ( ) { it ( 'should store properties' , function ( ) { const testZone = Zone . current . fork ( { name : 'A' , properties : { key : 'value' } } ) ; expect ( testZone . get ( 'key' ) ) . toEqual ( 'value' ) ; expect ( testZone . getZoneWith ( 'key' ) ) . toEqual ( testZone ) ; const childZone = testZone . fork ( { name : 'B' , properties : { key : 'override' } } ) ; expect ( testZone . get ( 'key' ) ) . toEqual ( 'value' ) ; expect ( testZone . getZoneWith ( 'key' ) ) . toEqual ( testZone ) ; expect ( childZone . get ( 'key' ) ) . toEqual ( 'override' ) ; expect ( childZone . getZoneWith ( 'key' ) ) . toEqual ( childZone ) ; } ) ; } ) ; describe ( 'task' , ( ) => { function noop ( ) { } let log ; const zone = Zone . current . fork ( { name : 'parent' , onHasTask : ( delegate , current , target , hasTaskState ) : void => { ( hasTaskState as any ) [ 'zone' ] = target . name ; log . push ( hasTaskState ) ; } , onScheduleTask : ( delegate , current , target , task ) => { return task ; } } ) ; beforeEach ( ( ) => { log = [ ] ; } ) ; it ( 'task can only run in the zone of creation' , ( ) => { const task = zone . fork ( { name : 'createZone' } ) . scheduleMacroTask ( 'test' , noop , undefined , noop , noop ) ; expect ( ( ) => { Zone . current . fork ( { name : 'anotherZone' } ) . runTask ( task ) ; } ) . toThrowError ( 'A task can only be run in the zone of creation! (Creation: createZone; Execution: anotherZone)' ) ; task . zone . cancelTask ( task ) ; } ) ; it ( 'task can only cancel in the zone of creation' , ( ) => { const task = zone . fork ( { name : 'createZone' } ) . scheduleMacroTask ( 'test' , noop , undefined , noop , noop ) ; expect ( ( ) => { Zone . current . fork ( { name : 'anotherZone' } ) . cancelTask ( task ) ; } ) . toThrowError ( 'A task can only be cancelled in the zone of creation! (Creation: createZone; Execution: anotherZone)' ) ; task . zone . cancelTask ( task ) ; } ) ; it ( 'should prevent double cancellation' , ( ) => { const task = zone . scheduleMacroTask ( 'test' , ( ) => log . push ( 'macroTask' ) , undefined , noop , noop ) ; zone . cancelTask ( task ) ; try { zone . cancelTask ( task ) ; } catch ( e ) { expect ( e . message ) . toContain ( 'macroTask \'test\': can not transition to \'canceling\', expecting state \'scheduled\' or \'running\', was \'notScheduled\'.' ) ; } } ) ; it ( 'should not decrement counters on periodic tasks' , ( ) => { zone . run ( ( ) => { const task = zone . scheduleMacroTask ( 'test' , ( ) => log . push ( 'macroTask' ) , { isPeriodic : true } , noop , noop ) ; zone . runTask ( task ) ; zone . runTask ( task ) ; zone . cancelTask ( task ) ; } ) ; expect ( log ) . toEqual ( [ { microTask : false , macroTask : true , eventTask : false , change : 'macroTask' , zone : 'parent' } , 'macroTask' , 'macroTask' , { microTask : false , macroTask : false , eventTask : false , change : 'macroTask' , zone : 'parent' } ] ) ; } ) ; it ( 'should notify of queue status change' , ( ) => { zone . run ( ( ) => { const z = Zone . current ; z . runTask ( z . scheduleMicroTask ( 'test' , ( ) => log . push ( 'microTask' ) ) ) ; z . cancelTask ( z . scheduleMacroTask ( 'test' , ( ) => log . push ( 'macroTask' ) , undefined , noop , noop ) ) ; z . cancelTask ( z . scheduleEventTask ( 'test' , ( ) => log . push ( 'eventTask' ) , undefined , noop , noop ) ) ; } ) ; expect ( log ) . toEqual ( [ { microTask : true , macroTask : false , eventTask : false , change : 'microTask' , zone : 'parent' } , 'microTask' , { microTask : false , macroTask : false , eventTask : false , change : 'microTask' , zone : 'parent' } , { microTask : false , macroTask : true , eventTask : false , change : 'macroTask' , zone : 'parent' } , { microTask : false , macroTask : false , eventTask : false , change : 'macroTask' , zone : 'parent' } , { microTask : false , macroTask : false , eventTask : true , change : 'eventTask' , zone : 'parent' } , { microTask : false , macroTask : false , eventTask : false , change : 'eventTask' , zone : 'parent' } ] ) ; } ) ; it ( 'should notify of queue status change on parent task' , ( ) => { zone . fork ( { name : 'child' } ) . run ( ( ) => { const z = Zone . current ; z . runTask ( z . scheduleMicroTask ( 'test' , ( ) => log . push ( 'microTask' ) ) ) ; } ) ; expect ( log ) . toEqual ( [ { microTask : true , macroTask : false , eventTask : false , change : 'microTask' , zone : 'child' } , { microTask : true , macroTask : false , eventTask : false , change : 'microTask' , zone : 'parent' } , 'microTask' , { microTask : false , macroTask : false , eventTask : false , change : 'microTask' , zone : 'child' } , { microTask : false , macroTask : false , eventTask : false , change : 'microTask' , zone : 'parent' } , ] ) ; } ) ; it ( 'should allow rescheduling a task on a separate zone' , ( ) => { const log = [ ] ; const zone = Zone . current . fork ( { name : 'test-root' , onHasTask : ( delegate , current , target , hasTaskState ) => { ( hasTaskState as any ) [ 'zone' ] = target . name ; log . push ( hasTaskState ) ; } } ) ; const left = zone . fork ( { name : 'left' } ) ; const right = zone . fork ( { name : 'right' , onScheduleTask : ( delegate , current , target , task ) : Task => { log . push ( { pos : 'before' , method : 'onScheduleTask' , zone : current . name , task : task . zone . name } ) ; task . cancelScheduleRequest ( ) ; task = left . scheduleTask ( task ) ; log . push ( { pos : 'after' , method : 'onScheduleTask' , zone : current . name , task : task . zone . name } ) ; return task ; } } ) ; const rchild = right . fork ( { name : 'rchild' , onScheduleTask : ( delegate , current , target , task ) : Task => { log . push ( { pos : 'before' , method : 'onScheduleTask' , zone : current . name , task : task . zone . name } ) ; task = delegate . scheduleTask ( target , task ) ; log . push ( { pos : 'after' , method : 'onScheduleTask' , zone : current . name , task : task . zone . name } ) ; expect ( ( task as any ) . _zoneDelegates . map ( ( zd ) => zd . zone . name ) ) . toEqual ( [ 'left' , 'test-root' , 'ProxyZone' ] ) ; return task ; } } ) ; const task = rchild . scheduleMacroTask ( 'testTask' , ( ) => log . push ( 'WORK' ) , { } , noop , noop ) ; expect ( task . zone ) . toEqual ( left ) ; log . push ( task . zone . name ) ; task . invoke ( ) ; expect ( log ) . toEqual ( [ { pos : 'before' , method : 'onScheduleTask' , zone : 'rchild' , task : 'rchild' } , { pos : 'before' , method : 'onScheduleTask' , zone : 'right' , task : 'rchild' } , { microTask : false , macroTask : true , eventTask : false , change : 'macroTask' , zone : 'left' } , { microTask : false , macroTask : true , eventTask : false , change : 'macroTask' , zone : 'test-root' } , { pos : 'after' , method : 'onScheduleTask' , zone : 'right' , task : 'left' } , { pos : 'after' , method : 'onScheduleTask' , zone : 'rchild' , task : 'left' } , 'left' , 'WORK' , { microTask : false , macroTask : false , eventTask : false , change : 'macroTask' , zone : 'left' } , { microTask : false , macroTask : false , eventTask : false , change : 'macroTask' , zone : 'test-root' } ] ) ; } ) ; it ( 'period task should not transit to scheduled state after being cancelled in running state' , ( ) => { const zone = Zone . current . fork ( { name : 'testZone' } ) ; const task = zone . scheduleMacroTask ( 'testPeriodTask' , ( ) => { zone . cancelTask ( task ) ; } , { isPeriodic : true } , ( ) => { } , ( ) => { } ) ; task . invoke ( ) ; expect ( task . state ) . toBe ( 'notScheduled' ) ; } ) ; it ( 'event task should not transit to scheduled state after being cancelled in running state' , ( ) => { const zone = Zone . current . fork ( { name : 'testZone' } ) ; const task = zone . scheduleEventTask ( 'testEventTask' , ( ) => { zone . cancelTask ( task ) ; } , undefined , ( ) => { } , ( ) => { } ) ; task . invoke ( ) ; expect ( task . state ) . toBe ( 'notScheduled' ) ; } ) ; describe ( 'assert ZoneAwarePromise' , ( ) => { it ( 'should not throw when all is OK' , ( ) => { Zone . assertZonePatched ( ) ; } ) ; it ( 'should keep ZoneAwarePromise has been patched' , ( ) => { class WrongPromise { static resolve ( value ) { } then ( ) { } } const ZoneAwarePromise = global . Promise ; const NativePromise = ( global as any ) [ zoneSymbol ( 'Promise' ) ] ; global . Promise = WrongPromise ; try { expect ( ZoneAwarePromise ) . toBeTruthy ( ) ; Zone . assertZonePatched ( ) ; expect ( global . Promise ) . toBe ( ZoneAwarePromise ) ; } finally { global . Promise = NativePromise ; } Zone . assertZonePatched ( ) ; } ) ; } ) ; } ) ; describe ( 'invoking tasks' , ( ) => { let log ; function noop ( ) { } beforeEach ( ( ) => { log = [ ] ; } ) ; it ( 'should not drain the microtask queue too early' , ( ) => { const z = Zone . current ; const event = z . scheduleEventTask ( 'test' , ( ) => log . push ( 'eventTask' ) , undefined , noop , noop ) ; z . scheduleMicroTask ( 'test' , ( ) => log . push ( 'microTask' ) ) ; const macro = z . scheduleMacroTask ( 'test' , ( ) => { event . invoke ( ) ; expect ( log ) . toEqual ( [ 'eventTask' ] ) ; } , undefined , noop , noop ) ; macro . invoke ( ) ; } ) ; it ( 'should convert task to json without cyclic error' , ( ) => { const z = Zone . current ; const event = z . scheduleEventTask ( 'test' , ( ) => { } , undefined , noop , noop ) ; const micro = z . scheduleMicroTask ( 'test' , ( ) => { } ) ; const macro = z . scheduleMacroTask ( 'test' , ( ) => { } , undefined , noop , noop ) ; expect ( function ( ) { JSON . stringify ( event ) ; } ) . not . toThrow ( ) ; expect ( function ( ) { JSON . stringify ( micro ) ; } ) . not . toThrow ( ) ; expect ( function ( ) { JSON . stringify ( macro ) ; } ) . not . toThrow ( ) ; } ) ; it ( 'should call onHandleError callback when zoneSpec onHasTask throw error' , ( ) => { const spy = jasmine . createSpy ( 'error' ) ; const hasTaskZone = Zone . current . fork ( { name : 'hasTask' , onHasTask : ( delegate , currentZone , targetZone , hasTasState ) => { throw new Error ( 'onHasTask Error' ) ; } , onHandleError : ( delegate , currentZone , targetZone , error ) => { spy ( error . message ) ; return delegate . handleError ( targetZone , error ) ; } } ) ; const microTask = hasTaskZone . scheduleMicroTask ( 'test' , ( ) => { } , undefined , ( ) => { } ) ; expect ( spy ) . toHaveBeenCalledWith ( 'onHasTask Error' ) ; } ) ; } ) ; } ) ; function throwError ( ) { throw new Error ( ) ; }