| 
									
										
										
										
											2019-06-01 00:56:07 +09:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							|  |  |  |  * Copyright Google Inc. All Rights Reserved. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const noop = function() {}; | 
					
						
							|  |  |  | let log: {zone: string, taskZone: undefined | string, toState: TaskState, fromState: TaskState}[] = | 
					
						
							|  |  |  |     []; | 
					
						
							|  |  |  | const detectTask = Zone.current.scheduleMacroTask('detectTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  | const originalTransitionTo = detectTask.constructor.prototype._transitionTo; | 
					
						
							|  |  |  | // patch _transitionTo of ZoneTask to add log for test
 | 
					
						
							|  |  |  | const logTransitionTo: Function = function( | 
					
						
							| 
									
										
										
										
											2019-06-26 10:33:02 +02:00
										 |  |  |     this: Task&{_state: TaskState}, toState: TaskState, fromState1: TaskState, | 
					
						
							|  |  |  |     fromState2?: TaskState) { | 
					
						
							| 
									
										
										
										
											2019-06-01 00:56:07 +09:00
										 |  |  |   log.push({ | 
					
						
							|  |  |  |     zone: Zone.current.name, | 
					
						
							|  |  |  |     taskZone: this.zone && this.zone.name, | 
					
						
							|  |  |  |     toState: toState, | 
					
						
							|  |  |  |     fromState: this._state | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   originalTransitionTo.apply(this, arguments); | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function testFnWithLoggedTransitionTo(testFn: Function) { | 
					
						
							| 
									
										
										
										
											2019-06-26 10:33:02 +02:00
										 |  |  |   return function(this: unknown) { | 
					
						
							| 
									
										
										
										
											2019-06-01 00:56:07 +09:00
										 |  |  |     detectTask.constructor.prototype._transitionTo = logTransitionTo; | 
					
						
							|  |  |  |     testFn.apply(this, arguments); | 
					
						
							|  |  |  |     detectTask.constructor.prototype._transitionTo = originalTransitionTo; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | describe('task lifecycle', () => { | 
					
						
							|  |  |  |   describe('event task lifecycle', () => { | 
					
						
							|  |  |  |     beforeEach(() => { log = []; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduling then to scheduled state when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduling to unknown when zoneSpec onScheduleTask callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testEventTaskZone', | 
					
						
							|  |  |  |                onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |                  throw Error('error in onScheduleTask'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to running when task is invoked then from running to scheduled after invoke', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = | 
					
						
							|  |  |  |                Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to canceling then from canceling to notScheduled when task is canceled before running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = | 
					
						
							|  |  |  |                Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            Zone.current.cancelTask(task); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to canceling then from canceling to notScheduled when task is canceled in running state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleEventTask( | 
					
						
							|  |  |  |                'testEventTask', () => { Zone.current.cancelTask(task); }, undefined, noop, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'running'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to scheduled when task.callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleEventTask( | 
					
						
							|  |  |  |                'testEventTask', () => { throw Error('invoke error'); }, undefined, noop, noop); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              task.invoke(); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from canceling to unknown when zoneSpec.onCancelTask throw error before task running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleEventTask( | 
					
						
							|  |  |  |                'testEventTask', noop, undefined, noop, () => { throw Error('cancel task'); }); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from canceling to unknown when zoneSpec.onCancelTask throw error in running state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testEventTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleEventTask( | 
					
						
							|  |  |  |                'testEventTask', noop, undefined, noop, () => { throw Error('cancel task'); }); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduled if zoneSpec.onHasTask throw error when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testEventTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  throw Error('hasTask Error'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit to notScheduled state if zoneSpec.onHasTask throw error when task is canceled', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          let task: Task; | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testEventTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  if (task && task.state === 'canceling') { | 
					
						
							|  |  |  |                    throw Error('hasTask Error'); | 
					
						
							|  |  |  |                  } | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = | 
					
						
							|  |  |  |                      Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                  Zone.current.cancelTask(task); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('non periodical macroTask lifecycle', () => { | 
					
						
							|  |  |  |     beforeEach(() => { log = []; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduling then to scheduled state when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            Zone.current.scheduleMacroTask('testMacroTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduling to unknown when zoneSpec onScheduleTask callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMacroTaskZone', | 
					
						
							|  |  |  |                onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |                  throw Error('error in onScheduleTask'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  Zone.current.scheduleMacroTask('testMacroTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to running when task is invoked then from running to noScheduled after invoke', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = | 
					
						
							|  |  |  |                Zone.current.scheduleMacroTask('testMacroTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to canceling then from canceling to notScheduled when task is canceled before running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = | 
					
						
							|  |  |  |                Zone.current.scheduleMacroTask('testMacrotask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            Zone.current.cancelTask(task); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to canceling then from canceling to notScheduled when task is canceled in running state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testMacroTask', () => { Zone.current.cancelTask(task); }, undefined, noop, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'running'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to noScheduled when task.callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testMacroTask', () => { throw Error('invoke error'); }, undefined, noop, noop); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              task.invoke(); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from canceling to unknown when zoneSpec.onCancelTask throw error before task running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testMacroTask', noop, undefined, noop, () => { throw Error('cancel task'); }); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from canceling to unknown when zoneSpec.onCancelTask throw error in running state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMacroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testMacroTask', noop, undefined, noop, () => { throw Error('cancel task'); }); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduling then to scheduled if zoneSpec.onHasTask throw error when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMacroTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  throw Error('hasTask Error'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  Zone.current.scheduleMacroTask('testMacroTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit to notScheduled state if zoneSpec.onHasTask throw error after task.callback being invoked', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          let task: Task; | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMacroTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  if (task && task.state === 'running') { | 
					
						
							|  |  |  |                    throw Error('hasTask Error'); | 
					
						
							|  |  |  |                  } | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = | 
					
						
							|  |  |  |                      Zone.current.scheduleMacroTask('testMacroTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                  task.invoke(); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit to notScheduled state if zoneSpec.onHasTask throw error when task is canceled before running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          let task: Task; | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMacroTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  if (task && task.state === 'canceling') { | 
					
						
							|  |  |  |                    throw Error('hasTask Error'); | 
					
						
							|  |  |  |                  } | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = | 
					
						
							|  |  |  |                      Zone.current.scheduleMacroTask('testMacroTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                  Zone.current.cancelTask(task); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('periodical macroTask lifecycle', () => { | 
					
						
							|  |  |  |     let task: Task|null; | 
					
						
							|  |  |  |     beforeEach(() => { | 
					
						
							|  |  |  |       log = []; | 
					
						
							|  |  |  |       task = null; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     afterEach(() => { | 
					
						
							|  |  |  |       task && task.state !== 'notScheduled' && task.state !== 'canceling' && | 
					
						
							|  |  |  |           task.state !== 'unknown' && task.zone.cancelTask(task); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduling then to scheduled state when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testPeriodicalTask', noop, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduling to unknown when zoneSpec onScheduleTask callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testPeriodicalTaskZone', | 
					
						
							|  |  |  |                onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |                  throw Error('error in onScheduleTask'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                      'testPeriodicalTask', noop, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to running when task is invoked then from running to scheduled after invoke', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testPeriodicalTask', noop, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to canceling then from canceling to notScheduled when task is canceled before running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testPeriodicalTask', noop, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |            Zone.current.cancelTask(task); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to canceling then from canceling to notScheduled when task is canceled in running state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask('testPeriodicalTask', () => { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task !); | 
					
						
							|  |  |  |            }, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'running'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to scheduled when task.callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask('testPeriodicalTask', () => { | 
					
						
							|  |  |  |              throw Error('invoke error'); | 
					
						
							|  |  |  |            }, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              task.invoke(); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from canceling to unknown when zoneSpec.onCancelTask throw error before task running', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testPeriodicalTask', noop, {isPeriodic: true}, noop, | 
					
						
							|  |  |  |                () => { throw Error('cancel task'); }); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from canceling to unknown when zoneSpec.onCancelTask throw error in running state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testPeriodicalTaskZone'}).run(() => { | 
					
						
							|  |  |  |            task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testPeriodicalTask', noop, {isPeriodic: true}, noop, | 
					
						
							|  |  |  |                () => { throw Error('cancel task'); }); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduled if zoneSpec.onHasTask throw error when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testPeriodicalTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  throw Error('hasTask Error'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                      'testPeriodicalTask', noop, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit to notScheduled state if zoneSpec.onHasTask throw error when task is canceled', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testPeriodicalTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  if (task && task.state === 'canceling') { | 
					
						
							|  |  |  |                    throw Error('hasTask Error'); | 
					
						
							|  |  |  |                  } | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                      'testPeriodicalTask', noop, {isPeriodic: true}, noop, noop); | 
					
						
							|  |  |  |                  Zone.current.cancelTask(task); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('microTask lifecycle', () => { | 
					
						
							|  |  |  |     beforeEach(() => { log = []; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduling then to scheduled state when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMicroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            Zone.current.scheduleMicroTask('testMicroTask', noop, undefined, noop); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduling to unknown when zoneSpec onScheduleTask callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMicroTaskZone', | 
					
						
							|  |  |  |                onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |                  throw Error('error in onScheduleTask'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  Zone.current.scheduleMicroTask('testMicroTask', noop, undefined, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'unknown', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from scheduled to running when task is invoked then from running to noScheduled after invoke', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMicroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMicroTask('testMicroTask', noop, undefined, noop); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should throw error when try to cancel a microTask', testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMicroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMicroTask('testMicroTask', () => {}, undefined, noop); | 
					
						
							|  |  |  |            expect(() => { Zone.current.cancelTask(task); }).toThrowError('Task is not cancelable'); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from running to notScheduled when task.callback throw error', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testMicroTaskZone'}).run(() => { | 
					
						
							|  |  |  |            const task = Zone.current.scheduleMicroTask( | 
					
						
							|  |  |  |                'testMicroTask', () => { throw Error('invoke error'); }, undefined, noop); | 
					
						
							|  |  |  |            try { | 
					
						
							|  |  |  |              task.invoke(); | 
					
						
							|  |  |  |            } catch (err) { | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit from notScheduled to scheduling then to scheduled if zoneSpec.onHasTask throw error when scheduleTask', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMicroTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  throw Error('hasTask Error'); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  Zone.current.scheduleMicroTask('testMicroTask', noop, undefined, noop); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should transit to notScheduled state if zoneSpec.onHasTask throw error after task.callback being invoked', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          let task: Task; | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'testMicroTaskZone', | 
					
						
							|  |  |  |                onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |                  if (task && task.state === 'running') { | 
					
						
							|  |  |  |                    throw Error('hasTask Error'); | 
					
						
							|  |  |  |                  } | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                try { | 
					
						
							|  |  |  |                  task = Zone.current.scheduleMicroTask('testMicroTask', noop, undefined, noop); | 
					
						
							|  |  |  |                  task.invoke(); | 
					
						
							|  |  |  |                } catch (err) { | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'running', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'running'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('task should not run if task transite to notScheduled state which was canceled', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          let task: Task; | 
					
						
							|  |  |  |          Zone.current.fork({name: 'testCancelZone'}).run(() => { | 
					
						
							|  |  |  |            const task = | 
					
						
							|  |  |  |                Zone.current.scheduleEventTask('testEventTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            Zone.current.cancelTask(task); | 
					
						
							|  |  |  |            task.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          expect(log.map(item => { return {toState: item.toState, fromState: item.fromState}; })) | 
					
						
							|  |  |  |              .toEqual([ | 
					
						
							|  |  |  |                {toState: 'scheduling', fromState: 'notScheduled'}, | 
					
						
							|  |  |  |                {toState: 'scheduled', fromState: 'scheduling'}, | 
					
						
							|  |  |  |                {toState: 'canceling', fromState: 'scheduled'}, | 
					
						
							|  |  |  |                {toState: 'notScheduled', fromState: 'canceling'} | 
					
						
							|  |  |  |              ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   describe('reschedule zone', () => { | 
					
						
							|  |  |  |     let callbackLogs: ({pos: string, method: string, zone: string, task: string} | HasTaskState)[]; | 
					
						
							|  |  |  |     const newZone = Zone.root.fork({ | 
					
						
							|  |  |  |       name: 'new', | 
					
						
							|  |  |  |       onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'before', method: 'onScheduleTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         return delegate.scheduleTask(targetZone, task); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onInvokeTask: (delegate, currZone, targetZone, task, applyThis, applyArgs) => { | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'before', method: 'onInvokeTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         return delegate.invokeTask(targetZone, task, applyThis, applyArgs); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onCancelTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'before', method: 'onCancelTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         return delegate.cancelTask(targetZone, task); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |         (hasTaskState as any)['zone'] = targetZone.name; | 
					
						
							|  |  |  |         callbackLogs.push(hasTaskState); | 
					
						
							|  |  |  |         return delegate.hasTask(targetZone, hasTaskState); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     const zone = Zone.root.fork({ | 
					
						
							|  |  |  |       name: 'original', | 
					
						
							|  |  |  |       onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'before', method: 'onScheduleTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         task.cancelScheduleRequest(); | 
					
						
							|  |  |  |         task = newZone.scheduleTask(task); | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'after', method: 'onScheduleTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         return task; | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onInvokeTask: (delegate, currZone, targetZone, task, applyThis, applyArgs) => { | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'before', method: 'onInvokeTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         return delegate.invokeTask(targetZone, task, applyThis, applyArgs); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onCancelTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |         callbackLogs.push( | 
					
						
							|  |  |  |             {pos: 'before', method: 'onCancelTask', zone: currZone.name, task: task.zone.name}); | 
					
						
							|  |  |  |         return delegate.cancelTask(targetZone, task); | 
					
						
							|  |  |  |       }, | 
					
						
							|  |  |  |       onHasTask: (delegate, currZone, targetZone, hasTaskState) => { | 
					
						
							|  |  |  |         (<any>hasTaskState)['zone'] = targetZone.name; | 
					
						
							|  |  |  |         callbackLogs.push(hasTaskState); | 
					
						
							|  |  |  |         return delegate.hasTask(targetZone, hasTaskState); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     beforeEach(() => { callbackLogs = []; }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should be able to reschedule zone when in scheduling state, after that, task will completely go to new zone, has nothing to do with original one', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          zone.run(() => { | 
					
						
							|  |  |  |            const t = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testRescheduleZoneTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            t.invoke(); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          expect(callbackLogs).toEqual([ | 
					
						
							|  |  |  |            {pos: 'before', method: 'onScheduleTask', zone: 'original', task: 'original'}, | 
					
						
							|  |  |  |            {pos: 'before', method: 'onScheduleTask', zone: 'new', task: 'new'}, | 
					
						
							|  |  |  |            {microTask: false, macroTask: true, eventTask: false, change: 'macroTask', zone: 'new'}, | 
					
						
							|  |  |  |            {pos: 'after', method: 'onScheduleTask', zone: 'original', task: 'new'}, | 
					
						
							|  |  |  |            {pos: 'before', method: 'onInvokeTask', zone: 'new', task: 'new'}, { | 
					
						
							|  |  |  |              microTask: false, | 
					
						
							|  |  |  |              macroTask: false, | 
					
						
							|  |  |  |              eventTask: false, | 
					
						
							|  |  |  |              change: 'macroTask', | 
					
						
							|  |  |  |              zone: 'new' | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          ]); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should not be able to reschedule task in notScheduled / running / canceling state', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          Zone.current.fork({name: 'rescheduleNotScheduled'}).run(() => { | 
					
						
							|  |  |  |            const t = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                'testRescheduleZoneTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            Zone.current.cancelTask(t); | 
					
						
							|  |  |  |            expect(() => { t.cancelScheduleRequest(); }) | 
					
						
							|  |  |  |                .toThrow(Error( | 
					
						
							|  |  |  |                    `macroTask 'testRescheduleZoneTask': can not transition to ` + | 
					
						
							|  |  |  |                    `'notScheduled', expecting state 'scheduling', was 'notScheduled'.`)); | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'rescheduleRunning', | 
					
						
							|  |  |  |                onInvokeTask: (delegate, currZone, targetZone, task, applyThis, applyArgs) => { | 
					
						
							|  |  |  |                  expect(() => { task.cancelScheduleRequest(); }) | 
					
						
							|  |  |  |                      .toThrow(Error( | 
					
						
							|  |  |  |                          `macroTask 'testRescheduleZoneTask': can not transition to ` + | 
					
						
							|  |  |  |                          `'notScheduled', expecting state 'scheduling', was 'running'.`)); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                const t = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                    'testRescheduleZoneTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                t.invoke(); | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          Zone.current | 
					
						
							|  |  |  |              .fork({ | 
					
						
							|  |  |  |                name: 'rescheduleCanceling', | 
					
						
							|  |  |  |                onCancelTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |                  expect(() => { task.cancelScheduleRequest(); }) | 
					
						
							|  |  |  |                      .toThrow(Error( | 
					
						
							|  |  |  |                          `macroTask 'testRescheduleZoneTask': can not transition to ` + | 
					
						
							|  |  |  |                          `'notScheduled', expecting state 'scheduling', was 'canceling'.`)); | 
					
						
							|  |  |  |                } | 
					
						
							|  |  |  |              }) | 
					
						
							|  |  |  |              .run(() => { | 
					
						
							|  |  |  |                const t = Zone.current.scheduleMacroTask( | 
					
						
							|  |  |  |                    'testRescheduleZoneTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |                Zone.current.cancelTask(t); | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('can not reschedule a task to a zone which is the descendants of the original zone', | 
					
						
							|  |  |  |        testFnWithLoggedTransitionTo(() => { | 
					
						
							|  |  |  |          const originalZone = Zone.root.fork({ | 
					
						
							|  |  |  |            name: 'originalZone', | 
					
						
							|  |  |  |            onScheduleTask: (delegate, currZone, targetZone, task) => { | 
					
						
							|  |  |  |              callbackLogs.push({ | 
					
						
							|  |  |  |                pos: 'before', | 
					
						
							|  |  |  |                method: 'onScheduleTask', | 
					
						
							|  |  |  |                zone: currZone.name, | 
					
						
							|  |  |  |                task: task.zone.name | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |              task.cancelScheduleRequest(); | 
					
						
							|  |  |  |              task = rescheduleZone.scheduleTask(task); | 
					
						
							|  |  |  |              callbackLogs.push({ | 
					
						
							|  |  |  |                pos: 'after', | 
					
						
							|  |  |  |                method: 'onScheduleTask', | 
					
						
							|  |  |  |                zone: currZone.name, | 
					
						
							|  |  |  |                task: task.zone.name | 
					
						
							|  |  |  |              }); | 
					
						
							|  |  |  |              return task; | 
					
						
							|  |  |  |            } | 
					
						
							|  |  |  |          }); | 
					
						
							|  |  |  |          const rescheduleZone = originalZone.fork({name: 'rescheduleZone'}); | 
					
						
							|  |  |  |          expect(() => { | 
					
						
							|  |  |  |            originalZone.run(() => { | 
					
						
							|  |  |  |              Zone.current.scheduleMacroTask('testRescheduleZoneTask', noop, undefined, noop, noop); | 
					
						
							|  |  |  |            }); | 
					
						
							|  |  |  |          }) | 
					
						
							|  |  |  |              .toThrowError( | 
					
						
							|  |  |  |                  'can not reschedule task to rescheduleZone which is descendants of the original zone originalZone'); | 
					
						
							|  |  |  |        })); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | }); |