angular/packages/zone.js/test/zone-spec/task-tracking.spec.ts
Krzysztof Platis f19b36f462 fix(zone.js): in TaskTrackingZoneSpec track a periodic task until it is cancelled (#45391)
Before this change, the macrotask for `setInterval(callback, ms)` was no
longer tracked by `TaskTrackingZoneSpec` after the `callback` was
invoked for the first time. Now the periodic macrotask is tracked until
it is cancelled, e.g. `clearInterval(id)`.

BREAKING CHANGE: in TaskTrackingZoneSpec track a periodic task until it is cancelled

The breaking change is scoped only to the plugin
`zone.js/plugins/task-tracking`. If you used `TaskTrackingZoneSpec` and
checked the pending macroTasks e.g. using `(this.ngZone as any)._inner
._parent._properties.TaskTrackingZone.getTasksFor('macroTask')`, then
its behavior slightly changed for periodic macrotasks. For example,
previously the `setInterval` macrotask was no longer tracked after its
callback was executed for the first time. Now it's tracked until
the task is explicitly cancelled, e.g  with `clearInterval(id)`.

fixes 45350

PR Close #45391
2022-03-24 10:53:36 -07:00

99 lines
3.6 KiB
TypeScript

/**
* @license
* Copyright Google LLC 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 {supportPatchXHROnProperty} from '../test-util';
declare const global: any;
describe('TaskTrackingZone', function() {
let _TaskTrackingZoneSpec: typeof TaskTrackingZoneSpec = (Zone as any)['TaskTrackingZoneSpec'];
let taskTrackingZoneSpec: TaskTrackingZoneSpec|null = null;
let taskTrackingZone: Zone;
beforeEach(() => {
taskTrackingZoneSpec = new _TaskTrackingZoneSpec();
taskTrackingZone = Zone.current.fork(taskTrackingZoneSpec);
});
it('should track tasks', (done: Function) => {
taskTrackingZone.run(() => {
taskTrackingZone.scheduleMicroTask('test1', () => {});
expect(taskTrackingZoneSpec!.microTasks.length).toBe(1);
expect(taskTrackingZoneSpec!.microTasks[0].source).toBe('test1');
setTimeout(() => {});
expect(taskTrackingZoneSpec!.macroTasks.length).toBe(1);
expect(taskTrackingZoneSpec!.macroTasks[0].source).toBe('setTimeout');
taskTrackingZone.cancelTask(taskTrackingZoneSpec!.macroTasks[0]);
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);
// If a browser does not have XMLHttpRequest, then end test here.
if (typeof global['XMLHttpRequest'] == 'undefined') return done();
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);
if (supportPatchXHROnProperty()) {
expect(taskTrackingZoneSpec!.eventTasks.length).not.toBe(0);
}
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');
if (supportPatchXHROnProperty()) {
expect(taskTrackingZoneSpec!.eventTasks[0].source)
.toMatch(/\.addEventListener:readystatechange/);
}
});
});
});
it('should capture task creation stacktrace', (done) => {
taskTrackingZone.run(() => {
setTimeout(() => {
done();
});
expect((taskTrackingZoneSpec!.macroTasks[0] as any)['creationLocation']).toBeTruthy();
});
});
it('should track periodic task until it is canceled', (done) => {
taskTrackingZone.run(() => {
const intervalCallback = jasmine.createSpy('intervalCallback');
const interval = setInterval(intervalCallback, 1);
expect(intervalCallback).not.toHaveBeenCalled();
expect(taskTrackingZoneSpec!.macroTasks.length).toBe(1);
expect(taskTrackingZoneSpec!.macroTasks[0].source).toBe('setInterval');
setTimeout(() => {
expect(intervalCallback).toHaveBeenCalled();
expect(taskTrackingZoneSpec!.macroTasks.length).toBe(1);
expect(taskTrackingZoneSpec!.macroTasks[0].source).toBe('setInterval');
clearInterval(interval);
expect(taskTrackingZoneSpec!.macroTasks.length).toBe(0);
done();
}, 2);
});
});
});