532 lines
15 KiB
TypeScript
532 lines
15 KiB
TypeScript
/**
|
|
* @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
|
|
*/
|
|
|
|
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((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']);
|
|
}));
|
|
});
|
|
});
|
|
});
|