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:
JiaLiPassion 2021-04-11 14:58:00 +08:00 committed by Andrew Kushnir
parent 5c48cd30b5
commit 6910118b0d
1 changed files with 71 additions and 0 deletions

View File

@ -103,6 +103,77 @@ describe('XMLHttpRequest', function() {
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 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);
done();
});
});
req.open('get', '/', true);
req.send();
});
});
it('should return null when access ontimeout first time without error', function() {
let req: XMLHttpRequest = new XMLHttpRequest();
expect(req.ontimeout).toBe(null);