532 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			532 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| /**
 | |
|  * @license
 | |
|  * Copyright Google LLC 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
 | |
|  */
 | |
| 
 | |
| import {ifEnvSupports} from '../test-util';
 | |
| 
 | |
| describe('AsyncTestZoneSpec', function() {
 | |
|   let log: string[];
 | |
|   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<void>((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: HTMLButtonElement;
 | |
|              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: XMLHttpRequest;
 | |
|                let finished = false;
 | |
| 
 | |
|                const testZoneSpec = new AsyncTestZoneSpec(
 | |
|                    () => {
 | |
|                      expect(finished).toBe(true);
 | |
|                      done();
 | |
|                    },
 | |
|                    (err: Error) => {
 | |
|                      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: XMLHttpRequest;
 | |
| 
 | |
|                const testZoneSpec = new AsyncTestZoneSpec(
 | |
|                    () => {
 | |
|                      done.fail('expected failCallback to be called');
 | |
|                    },
 | |
|                    (err: Error) => {
 | |
|                      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: Error) => {
 | |
|           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: Error) => {
 | |
|           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: Error) => {
 | |
|           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: any = (Zone as any)[Zone.__symbol__('asyncTest')];
 | |
| 
 | |
|   function wrapAsyncTest(fn: Function, doneFn?: Function) {
 | |
|     return function(this: unknown, done: Function) {
 | |
|       const asyncWrapper = asyncTest(fn);
 | |
|       return asyncWrapper.apply(this, [function(this: unknown) {
 | |
|                                   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: any =
 | |
|           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: any = typeof window === 'undefined' ? global : window;
 | |
|                  g[Zone.__symbol__('setTimeout')](res, 100);
 | |
|                }).then(() => {
 | |
|                  finished = true;
 | |
|                });
 | |
|              },
 | |
|              () => {
 | |
|                expect(finished).toBe(true);
 | |
|              }));
 | |
|     });
 | |
| 
 | |
| 
 | |
|     describe('test without beforeEach', () => {
 | |
|       const logs: string[] = [];
 | |
|       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: string[] = [];
 | |
| 
 | |
|       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: string[] = [];
 | |
| 
 | |
|       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: string[] = [];
 | |
| 
 | |
|       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: string[] = [];
 | |
| 
 | |
|       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']);
 | |
|              }));
 | |
|     });
 | |
|   });
 | |
| });
 |