Skip to content
This repository has been archived by the owner on Feb 26, 2024. It is now read-only.

Commit

Permalink
feat(trackingZone): Keep track of tasks to see outstanding tasks.
Browse files Browse the repository at this point in the history
  • Loading branch information
mhevery committed Aug 17, 2016
1 parent 1883589 commit 4942b4a
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 2 deletions.
14 changes: 12 additions & 2 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,19 @@ gulp.task('build/long-stack-trace-zone.min.js', function(cb) {
});

gulp.task('build/proxy-zone.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/proxy.ts', 'proxy-zone.js', false, cb);
return generateBrowserScript('./lib/zone-spec/proxy-zone.ts', 'proxy-zone.js', false, cb);
});

gulp.task('build/proxy-zone.min.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/proxy.ts', 'proxy-zone.min.js', true, cb);
return generateBrowserScript('./lib/zone-spec/proxy-zone.ts', 'proxy-zone.min.js', true, cb);
});

gulp.task('build/task-tracking.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/task-tracking.ts', 'task-tracking.js', false, cb);
});

gulp.task('build/task-tracking.min.js', function(cb) {
return generateBrowserScript('./lib/zone-spec/task-tracking.ts', 'task-tracking.min.js', true, cb);
});

gulp.task('build/wtf.js', function(cb) {
Expand Down Expand Up @@ -136,6 +144,8 @@ gulp.task('build', [
'build/long-stack-trace-zone.min.js',
'build/proxy-zone.js',
'build/proxy-zone.min.js',
'build/task-tracking.js',
'build/task-tracking.min.js',
'build/wtf.js',
'build/wtf.min.js',
'build/async-test.js',
Expand Down
68 changes: 68 additions & 0 deletions lib/zone-spec/task-tracking.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/**
* A `TaskTrackingZoneSpec` allows one to track all outstanding Tasks.
*
* This is useful in tests. For example to see which tasks are preventing a test from completing
* or an automated way of releasing all of the event listeners at the end of the test.
*/
class TaskTrackingZoneSpec implements ZoneSpec {
name = 'TaskTrackingZone';
microTasks: Task[] = [];
macroTasks: Task[] = [];
eventTasks: Task[] = [];
properties: {[key: string]: any} = {'TaskTrackingZone': this};

static get() {
return Zone.current.get('TaskTrackingZone');
}

private getTasksFor(type: string): Task [] {
switch (type) {
case 'microTask': return this.microTasks;
case 'macroTask': return this.macroTasks;
case 'eventTask': return this.eventTasks;
}
throw new Error('Unknown task format: ' + type);
}

onScheduleTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): Task {
task['creationLocation'] = new Error(`Task '${task.type}' from '${task.source}'.`);
const tasks = this.getTasksFor(task.type);
tasks.push(task);
return parentZoneDelegate.scheduleTask(targetZone, task);
}

onCancelTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone, task: Task): any {
const tasks = this.getTasksFor(task.type);
for(var i = 0; i < tasks.length; i++) {
if (tasks[i] == task) {
tasks.splice(i, 1);
break;
}
}
return parentZoneDelegate.cancelTask(targetZone, task);
}

onInvokeTask(parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
task: Task, applyThis: any, applyArgs: any): any
{
if (task.type === 'eventTask') return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
const tasks = this.getTasksFor(task.type);
for(var i = 0; i < tasks.length; i++) {
if (tasks[i] == task) {
tasks.splice(i, 1);
break;
}
}
return parentZoneDelegate.invokeTask(targetZone, task, applyThis, applyArgs);
}

clearEvents() {
while (this.eventTasks.length) {
Zone.current.cancelTask(this.eventTasks[0]);
}
}
}

// Export the class so that new instances can be created with proper
// constructor params.
Zone['TaskTrackingZoneSpec'] = TaskTrackingZoneSpec;
1 change: 1 addition & 0 deletions test/common_tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ import './zone-spec/async-test.spec';
import './zone-spec/sync-test.spec';
import './zone-spec/fake-async-test.spec';
import './zone-spec/proxy.spec';
import './zone-spec/task-tracking.spec';
69 changes: 69 additions & 0 deletions test/zone-spec/task-tracking.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import '../../lib/zone-spec/task-tracking';

describe('TaskTrackingZone', function() {
let _TaskTrackingZoneSpec: typeof TaskTrackingZoneSpec = Zone['TaskTrackingZoneSpec'];
let taskTrackingZoneSpec: TaskTrackingZoneSpec = null;
let taskTrackingZone: Zone;

beforeEach(() => {
taskTrackingZoneSpec = new _TaskTrackingZoneSpec();
taskTrackingZone = Zone.current.fork(taskTrackingZoneSpec);
});

it('should track tasks', (done: Function) => {
taskTrackingZone.run(() => {
const microTask = taskTrackingZone.scheduleMicroTask('test1', () => {});
expect(taskTrackingZoneSpec.microTasks.length).toBe(1);
expect(taskTrackingZoneSpec.microTasks[0].source).toBe('test1');

const macroTask = setTimeout(() => {}) as any as Task;
expect(taskTrackingZoneSpec.macroTasks.length).toBe(1);
expect(taskTrackingZoneSpec.macroTasks[0].source).toBe('setTimeout');
taskTrackingZone.cancelTask(macroTask);
expect(taskTrackingZoneSpec.macroTasks.length).toBe(0);

setTimeout(() => {
// assert on execution it is null
expect(taskTrackingZoneSpec.macroTasks.length).toBe(0);
expect(taskTrackingZoneSpec.microTasks.length).toBe(0);

const xhr = new XMLHttpRequest();
xhr.open('get', '/', true);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4) {
// clear current event tasks using setTimeout
setTimeout(() => {
expect(taskTrackingZoneSpec.macroTasks.length).toBe(0);
expect(taskTrackingZoneSpec.microTasks.length).toBe(0);
expect(taskTrackingZoneSpec.eventTasks.length).toBe(2);
taskTrackingZoneSpec.clearEvents();
expect(taskTrackingZoneSpec.eventTasks.length).toBe(0);
done();
});
}
};
xhr.send();
expect(taskTrackingZoneSpec.macroTasks.length).toBe(1);
expect(taskTrackingZoneSpec.macroTasks[0].source).toBe('XMLHttpRequest.send');

expect(taskTrackingZoneSpec.eventTasks.length).toBe(2);
// one for me
expect(taskTrackingZoneSpec.eventTasks[0].source).toBe('XMLHttpRequest.addEventListener:readystatechange');
// one for internall tracking of XHRs.
expect(taskTrackingZoneSpec.eventTasks[1].source).toBe('XMLHttpRequest.addEventListener:readystatechange');
});

});
});

it('should capture task creation stacktrace', (done) => {
taskTrackingZone.run(() => {
const task = setTimeout(() => {
done();
}) as any as Task;
expect(task['creationLocation']).toBeTruthy();
});
});
});

export var __something__;

0 comments on commit 4942b4a

Please sign in to comment.