Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions packages/zone.js/lib/zone-spec/async-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ class AsyncTestZoneSpec implements ZoneSpec {
_pendingMacroTasks: boolean = false;
_alreadyErrored: boolean = false;
_isSync: boolean = false;
entryFunction: Function|null = null;
runZone = Zone.current;
unresolvedChainedPromiseCount = 0;

Expand Down Expand Up @@ -108,13 +109,25 @@ class AsyncTestZoneSpec implements ZoneSpec {
onInvoke(
parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, delegate: Function,
applyThis: any, applyArgs?: any[], source?: string): any {
let previousTaskCounts: any = null;
if (!this.entryFunction) {
this.entryFunction = delegate;
}
try {
this._isSync = true;
return parentZoneDelegate.invoke(targetZone, delegate, applyThis, applyArgs, source);
} finally {
const afterTaskCounts: any = (parentZoneDelegate as any)._taskCounts;
if (this._isSync) {
// We need to check the delegate is the same as entryFunction or not.
// Consider the following case.
//
// asyncTestZone.run(() => { // Here the delegate will be the entryFunction
// Zone.current.run(() => { // Here the delegate will not be the entryFunction
// });
// });
//
// We only want to check whether there are async tasks scheduled
// for the entry function.
if (this._isSync && this.entryFunction === delegate) {
this._finishCallbackIfDone();
}
}
Expand All @@ -133,6 +146,21 @@ class AsyncTestZoneSpec implements ZoneSpec {

onHasTask(delegate: ZoneDelegate, current: Zone, target: Zone, hasTaskState: HasTaskState) {
delegate.hasTask(target, hasTaskState);
// We should only trigger finishCallback when the target zone is the AsyncTestZone
// Consider the following cases.
//
// const childZone = asyncTestZone.fork({
// name: 'child',
// onHasTask: ...
// });
//
// So we have nested zones declared the onHasTask hook, in this case,
// the onHasTask will be triggered twice, and cause the finishCallbackIfDone()
// is also be invoked twice. So we need to only trigger the finishCallbackIfDone()
// when the current zone is the same as the target zone.
if (current !== target) {
return;
}
if (hasTaskState.change == 'microTask') {
this._pendingMicroTasks = hasTaskState.microTask;
this._finishCallbackIfDone();
Expand Down
6 changes: 4 additions & 2 deletions packages/zone.js/test/browser/XMLHttpRequest.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -336,14 +336,16 @@ describe('XMLHttpRequest', function() {
const req = new XMLHttpRequest();
req.open('get', '/', true);
req.send();
req.addEventListener('readystatechange', function(ev) {
const listener = function(ev: any) {
if (req.readyState >= 2) {
expect(() => {
req.abort();
}).not.toThrow();
req.removeEventListener('readystatechange', listener);
done();
}
});
};
req.addEventListener('readystatechange', listener);
});
});

Expand Down
1 change: 1 addition & 0 deletions packages/zone.js/test/browser/browser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2907,6 +2907,7 @@ describe('Zone', function() {

expect(entries.length).toBe(1);
expect(entries[0].target).toBe(div);
observer.disconnect();
done();
});

Expand Down
1 change: 0 additions & 1 deletion packages/zone.js/test/extra/cordova.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ describe('cordova test', () => {
cordova.exec(
() => {
expect(Zone.current.name).toEqual('cordova');
done();
},
() => {
fail('should not fail');
Expand Down
49 changes: 49 additions & 0 deletions packages/zone.js/test/zone-spec/async-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,55 @@ describe('AsyncTestZoneSpec', function() {
});
});

it('should not call done multiple times in sync test', (done) => {
const testFn = () => {
Zone.current.run(() => {});
Zone.current.run(() => {});
};
let doneCalledCount = 0;
const testZoneSpec = new AsyncTestZoneSpec(() => {
doneCalledCount++;
}, () => {}, 'name');

const atz = Zone.current.fork(testZoneSpec);

atz.run(testFn);
setTimeout(() => {
expect(doneCalledCount).toBe(1);
done();
});
});

it('should not call done multiple times in async test with nested zone', (done) => {
const testFn = () => {
Promise.resolve(1).then(() => {});
};
let doneCalledCount = 0;
const testZoneSpec = new AsyncTestZoneSpec(() => {
doneCalledCount++;
}, () => {}, 'name');

const atz = Zone.current.fork(testZoneSpec);
const c1 = atz.fork({
name: 'child1',
onHasTask: (delegate, current, target, hasTaskState) => {
return delegate.hasTask(target, hasTaskState);
}
});
const c2 = c1.fork({
name: 'child2',
onHasTask: (delegate, current, target, hasTaskState) => {
return delegate.hasTask(target, hasTaskState);
}
});

c2.run(testFn);
setTimeout(() => {
expect(doneCalledCount).toBe(1);
done();
}, 50);
});

describe('event tasks', ifEnvSupports('document', () => {
let button: HTMLButtonElement;
beforeEach(function() {
Expand Down