test(zone.js): should invoke XHR task even onload handler throw error. (#41562)
Close #41520. This case related to the issue #41522. ``` Zone.root .fork({ name: 'xhr', onHasTask(delegate, currentZone, zone, taskState) { console.log('hasMacrotask', taskState.macroTask); return delegate.hasTask(zone, taskState); }, }) .run(() => { const xhr = new XMLHttpRequest(); xhr.open('GET', 'https://cdnjs.cloudflare.com/ajax/libs/zone.js/0.11.4/zone.min.js'); xhr.addEventListener('load', () => { throw new Error(); }); xhr.send(); }); ``` zone.js invoke all `onload` event handlers before change the XHR task's state from `scheduled` to `notscheduled`, so if any `onload` listener throw error, the XHR task wlll be hang to `scheduled`, and leave the macroTask status in the zone wrongly. This has been fixed in the previous commit, this commit add test to verify the case. PR Close #41562
This commit is contained in:
parent
008eaf3b7d
commit
c3614662cb
@ -103,6 +103,84 @@ describe('XMLHttpRequest', function() {
|
|||||||
req!.send();
|
req!.send();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should run onload listeners before internal readystatechange', function(done) {
|
||||||
|
const logs: string[] = [];
|
||||||
|
const xhrZone = Zone.current.fork({
|
||||||
|
name: 'xhr',
|
||||||
|
onInvokeTask: (delegate, curr, target, task, applyThis, applyArgs) => {
|
||||||
|
logs.push('invokeTask ' + task.source);
|
||||||
|
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xhrZone.run(function() {
|
||||||
|
const req = new XMLHttpRequest();
|
||||||
|
req.onload = function() {
|
||||||
|
logs.push('onload');
|
||||||
|
(window as any)[Zone.__symbol__('setTimeout')](() => {
|
||||||
|
expect(logs).toEqual([
|
||||||
|
'invokeTask XMLHttpRequest.addEventListener:load', 'onload',
|
||||||
|
'invokeTask XMLHttpRequest.send'
|
||||||
|
])
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
req.open('get', '/', true);
|
||||||
|
req.send();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should invoke xhr task even onload listener throw error', function(done) {
|
||||||
|
const oriWindowError = window.onerror;
|
||||||
|
window.onerror = function() {};
|
||||||
|
try {
|
||||||
|
const logs: string[] = [];
|
||||||
|
const xhrZone = Zone.current.fork({
|
||||||
|
name: 'xhr',
|
||||||
|
onInvokeTask: (delegate, curr, target, task, applyThis, applyArgs) => {
|
||||||
|
logs.push('invokeTask ' + task.source);
|
||||||
|
return delegate.invokeTask(target, task, applyThis, applyArgs);
|
||||||
|
},
|
||||||
|
onHasTask: (delegate, curr, target, hasTaskState) => {
|
||||||
|
if (hasTaskState.change === 'macroTask') {
|
||||||
|
logs.push('hasTask ' + hasTaskState.macroTask);
|
||||||
|
}
|
||||||
|
return delegate.hasTask(target, hasTaskState);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
xhrZone.run(function() {
|
||||||
|
const req = new XMLHttpRequest();
|
||||||
|
req.onload = function() {
|
||||||
|
logs.push('onload');
|
||||||
|
throw new Error('test');
|
||||||
|
};
|
||||||
|
const unhandledRejection = (e: PromiseRejectionEvent) => {
|
||||||
|
logs.push(e.reason.message);
|
||||||
|
};
|
||||||
|
window.addEventListener('unhandledrejection', unhandledRejection);
|
||||||
|
req.addEventListener('load', () => {
|
||||||
|
logs.push('onload1');
|
||||||
|
(window as any)[Zone.__symbol__('setTimeout')](() => {
|
||||||
|
expect(logs).toEqual([
|
||||||
|
'hasTask true', 'invokeTask XMLHttpRequest.addEventListener:load', 'onload',
|
||||||
|
'invokeTask XMLHttpRequest.addEventListener:load', 'onload1',
|
||||||
|
'invokeTask XMLHttpRequest.send', 'hasTask false',
|
||||||
|
'invokeTask Window.addEventListener:unhandledrejection', 'test'
|
||||||
|
]);
|
||||||
|
window.removeEventListener('unhandledrejection', unhandledRejection);
|
||||||
|
window.onerror = oriWindowError;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
req.open('get', '/', true);
|
||||||
|
req.send();
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
window.onerror = oriWindowError;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should return null when access ontimeout first time without error', function() {
|
it('should return null when access ontimeout first time without error', function() {
|
||||||
let req: XMLHttpRequest = new XMLHttpRequest();
|
let req: XMLHttpRequest = new XMLHttpRequest();
|
||||||
expect(req.ontimeout).toBe(null);
|
expect(req.ontimeout).toBe(null);
|
||||||
|
@ -2493,92 +2493,109 @@ describe('Zone', function() {
|
|||||||
|
|
||||||
it('should be able to continue to invoke remaining listeners even some listener throw error',
|
it('should be able to continue to invoke remaining listeners even some listener throw error',
|
||||||
function(done: DoneFn) {
|
function(done: DoneFn) {
|
||||||
let logs: string[] = [];
|
// override global.onerror to prevent jasmine report error
|
||||||
const listener1 = function() {
|
let oriWindowOnError = window.onerror;
|
||||||
logs.push('listener1');
|
window.onerror = function() {};
|
||||||
};
|
try {
|
||||||
const listener2 = function() {
|
let logs: string[] = [];
|
||||||
throw new Error('test1');
|
const listener1 = function() {
|
||||||
};
|
logs.push('listener1');
|
||||||
const listener3 = function() {
|
};
|
||||||
throw new Error('test2');
|
const listener2 = function() {
|
||||||
};
|
throw new Error('test1');
|
||||||
const listener4 = {
|
};
|
||||||
handleEvent: function() {
|
const listener3 = function() {
|
||||||
logs.push('listener2');
|
throw new Error('test2');
|
||||||
}
|
};
|
||||||
};
|
const listener4 = {
|
||||||
|
handleEvent: function() {
|
||||||
|
logs.push('listener2');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
button.addEventListener('click', listener1);
|
button.addEventListener('click', listener1);
|
||||||
button.addEventListener('click', listener2);
|
button.addEventListener('click', listener2);
|
||||||
button.addEventListener('click', listener3);
|
button.addEventListener('click', listener3);
|
||||||
button.addEventListener('click', listener4);
|
button.addEventListener('click', listener4);
|
||||||
|
|
||||||
const mouseEvent = document.createEvent('MouseEvent');
|
const mouseEvent = document.createEvent('MouseEvent');
|
||||||
mouseEvent.initEvent('click', true, true);
|
mouseEvent.initEvent('click', true, true);
|
||||||
|
|
||||||
const unhandledRejection = (e: PromiseRejectionEvent) => {
|
const unhandledRejection = (e: PromiseRejectionEvent) => {
|
||||||
logs.push(e.reason.message);
|
logs.push(e.reason.message);
|
||||||
};
|
};
|
||||||
window.addEventListener('unhandledrejection', unhandledRejection);
|
window.addEventListener('unhandledrejection', unhandledRejection);
|
||||||
|
|
||||||
button.dispatchEvent(mouseEvent);
|
button.dispatchEvent(mouseEvent);
|
||||||
expect(logs).toEqual(['listener1', 'listener2']);
|
expect(logs).toEqual(['listener1', 'listener2']);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(logs).toEqual(['listener1', 'listener2', 'test1', 'test2']);
|
expect(logs).toEqual(['listener1', 'listener2', 'test1', 'test2']);
|
||||||
window.removeEventListener('unhandledrejection', unhandledRejection);
|
window.removeEventListener('unhandledrejection', unhandledRejection);
|
||||||
done()
|
window.onerror = oriWindowOnError;
|
||||||
});
|
done()
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
window.onerror = oriWindowOnError;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should be able to continue to invoke remaining listeners even some listener throw error in the different zones',
|
it('should be able to continue to invoke remaining listeners even some listener throw error in the different zones',
|
||||||
function(done: DoneFn) {
|
function(done: DoneFn) {
|
||||||
let logs: string[] = [];
|
// override global.onerror to prevent jasmine report error
|
||||||
const zone1 = Zone.current.fork({
|
let oriWindowOnError = window.onerror;
|
||||||
name: 'zone1',
|
window.onerror = function() {};
|
||||||
onHandleError: (delegate, curr, target, error) => {
|
try {
|
||||||
logs.push(error.message);
|
let logs: string[] = [];
|
||||||
return false;
|
const zone1 = Zone.current.fork({
|
||||||
}
|
name: 'zone1',
|
||||||
});
|
onHandleError: (delegate, curr, target, error) => {
|
||||||
const listener1 = function() {
|
logs.push(error.message);
|
||||||
logs.push('listener1');
|
return false;
|
||||||
};
|
}
|
||||||
const listener2 = function() {
|
});
|
||||||
throw new Error('test1');
|
const listener1 = function() {
|
||||||
};
|
logs.push('listener1');
|
||||||
const listener3 = function() {
|
};
|
||||||
throw new Error('test2');
|
const listener2 = function() {
|
||||||
};
|
throw new Error('test1');
|
||||||
const listener4 = {
|
};
|
||||||
handleEvent: function() {
|
const listener3 = function() {
|
||||||
logs.push('listener2');
|
throw new Error('test2');
|
||||||
}
|
};
|
||||||
};
|
const listener4 = {
|
||||||
|
handleEvent: function() {
|
||||||
|
logs.push('listener2');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
button.addEventListener('click', listener1);
|
button.addEventListener('click', listener1);
|
||||||
zone1.run(() => {
|
zone1.run(() => {
|
||||||
button.addEventListener('click', listener2);
|
button.addEventListener('click', listener2);
|
||||||
});
|
});
|
||||||
button.addEventListener('click', listener3);
|
button.addEventListener('click', listener3);
|
||||||
button.addEventListener('click', listener4);
|
button.addEventListener('click', listener4);
|
||||||
|
|
||||||
const mouseEvent = document.createEvent('MouseEvent');
|
const mouseEvent = document.createEvent('MouseEvent');
|
||||||
mouseEvent.initEvent('click', true, true);
|
mouseEvent.initEvent('click', true, true);
|
||||||
|
|
||||||
const unhandledRejection = (e: PromiseRejectionEvent) => {
|
const unhandledRejection = (e: PromiseRejectionEvent) => {
|
||||||
logs.push(e.reason.message);
|
logs.push(e.reason.message);
|
||||||
};
|
};
|
||||||
window.addEventListener('unhandledrejection', unhandledRejection);
|
window.addEventListener('unhandledrejection', unhandledRejection);
|
||||||
|
|
||||||
button.dispatchEvent(mouseEvent);
|
button.dispatchEvent(mouseEvent);
|
||||||
expect(logs).toEqual(['listener1', 'test1', 'listener2']);
|
expect(logs).toEqual(['listener1', 'test1', 'listener2']);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(logs).toEqual(['listener1', 'test1', 'listener2', 'test2']);
|
expect(logs).toEqual(['listener1', 'test1', 'listener2', 'test2']);
|
||||||
done()
|
window.removeEventListener('unhandledrejection', unhandledRejection);
|
||||||
});
|
window.onerror = oriWindowOnError;
|
||||||
|
done()
|
||||||
|
});
|
||||||
|
} catch (e: any) {
|
||||||
|
window.onerror = oriWindowOnError;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user