feat: add an tickOptions parameter with property processNewMacroTasksSynchronously. (#33838)
This option will control whether to invoke the new macro tasks when ticking. Close #33799 PR Close #33838
This commit is contained in:

committed by
Miško Hevery

parent
2562a3b1b0
commit
17b862cf82
@ -25,21 +25,21 @@ describe('Angular async helper', () => {
|
|||||||
async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
|
async(() => { setTimeout(() => { actuallyDone = true; }, 0); }));
|
||||||
|
|
||||||
it('should run async test with task', async(() => {
|
it('should run async test with task', async(() => {
|
||||||
const id = setInterval(() => {
|
const id = setInterval(() => {
|
||||||
actuallyDone = true;
|
actuallyDone = true;
|
||||||
clearInterval(id);
|
clearInterval(id);
|
||||||
}, 100);
|
}, 100);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should run async test with successful promise', async(() => {
|
it('should run async test with successful promise', async(() => {
|
||||||
const p = new Promise(resolve => { setTimeout(resolve, 10); });
|
const p = new Promise(resolve => { setTimeout(resolve, 10); });
|
||||||
p.then(() => { actuallyDone = true; });
|
p.then(() => { actuallyDone = true; });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should run async test with failed promise', async(() => {
|
it('should run async test with failed promise', async(() => {
|
||||||
const p = new Promise((resolve, reject) => { setTimeout(reject, 10); });
|
const p = new Promise((resolve, reject) => { setTimeout(reject, 10); });
|
||||||
p.catch(() => { actuallyDone = true; });
|
p.catch(() => { actuallyDone = true; });
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Use done. Can also use async or fakeAsync.
|
// Use done. Can also use async or fakeAsync.
|
||||||
it('should run async test with successful delayed Observable', (done: DoneFn) => {
|
it('should run async test with successful delayed Observable', (done: DoneFn) => {
|
||||||
@ -48,56 +48,84 @@ describe('Angular async helper', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should run async test with successful delayed Observable', async(() => {
|
it('should run async test with successful delayed Observable', async(() => {
|
||||||
const source = of (true).pipe(delay(10));
|
const source = of (true).pipe(delay(10));
|
||||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should run async test with successful delayed Observable', fakeAsync(() => {
|
it('should run async test with successful delayed Observable', fakeAsync(() => {
|
||||||
const source = of (true).pipe(delay(10));
|
const source = of (true).pipe(delay(10));
|
||||||
source.subscribe(val => actuallyDone = true, err => fail(err));
|
source.subscribe(val => actuallyDone = true, err => fail(err));
|
||||||
|
|
||||||
tick(10);
|
tick(10);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('fakeAsync', () => {
|
describe('fakeAsync', () => {
|
||||||
// #docregion fake-async-test-tick
|
// #docregion fake-async-test-tick
|
||||||
it('should run timeout callback with delay after call tick with millis', fakeAsync(() => {
|
it('should run timeout callback with delay after call tick with millis', fakeAsync(() => {
|
||||||
let called = false;
|
let called = false;
|
||||||
setTimeout(() => { called = true; }, 100);
|
setTimeout(() => { called = true; }, 100);
|
||||||
tick(100);
|
tick(100);
|
||||||
expect(called).toBe(true);
|
expect(called).toBe(true);
|
||||||
}));
|
}));
|
||||||
// #enddocregion fake-async-test-tick
|
// #enddocregion fake-async-test-tick
|
||||||
|
|
||||||
|
// #docregion fake-async-test-tick-new-macro-task-sync
|
||||||
|
it('should run new macro task callback with delay after call tick with millis',
|
||||||
|
fakeAsync(() => {
|
||||||
|
function nestedTimer(cb: () => any): void { setTimeout(() => setTimeout(() => cb())); }
|
||||||
|
const callback = jasmine.createSpy('callback');
|
||||||
|
nestedTimer(callback);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
tick(0);
|
||||||
|
// the nested timeout will also be triggered
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
// #enddocregion fake-async-test-tick-new-macro-task-sync
|
||||||
|
|
||||||
|
// #docregion fake-async-test-tick-new-macro-task-async
|
||||||
|
it('should not run new macro task callback with delay after call tick with millis',
|
||||||
|
fakeAsync(() => {
|
||||||
|
function nestedTimer(cb: () => any): void { setTimeout(() => setTimeout(() => cb())); }
|
||||||
|
const callback = jasmine.createSpy('callback');
|
||||||
|
nestedTimer(callback);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
tick(0, {processNewMacroTasksSynchronously: false});
|
||||||
|
// the nested timeout will not be triggered
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
tick(0);
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
// #enddocregion fake-async-test-tick-new-macro-task-async
|
||||||
|
|
||||||
// #docregion fake-async-test-date
|
// #docregion fake-async-test-date
|
||||||
it('should get Date diff correctly in fakeAsync', fakeAsync(() => {
|
it('should get Date diff correctly in fakeAsync', fakeAsync(() => {
|
||||||
const start = Date.now();
|
const start = Date.now();
|
||||||
tick(100);
|
tick(100);
|
||||||
const end = Date.now();
|
const end = Date.now();
|
||||||
expect(end - start).toBe(100);
|
expect(end - start).toBe(100);
|
||||||
}));
|
}));
|
||||||
// #enddocregion fake-async-test-date
|
// #enddocregion fake-async-test-date
|
||||||
|
|
||||||
// #docregion fake-async-test-rxjs
|
// #docregion fake-async-test-rxjs
|
||||||
it('should get Date diff correctly in fakeAsync with rxjs scheduler', fakeAsync(() => {
|
it('should get Date diff correctly in fakeAsync with rxjs scheduler', fakeAsync(() => {
|
||||||
// need to add `import 'zone.js/dist/zone-patch-rxjs-fake-async'
|
// need to add `import 'zone.js/dist/zone-patch-rxjs-fake-async'
|
||||||
// to patch rxjs scheduler
|
// to patch rxjs scheduler
|
||||||
let result = null;
|
let result = null;
|
||||||
of ('hello').pipe(delay(1000)).subscribe(v => { result = v; });
|
of ('hello').pipe(delay(1000)).subscribe(v => { result = v; });
|
||||||
expect(result).toBeNull();
|
expect(result).toBeNull();
|
||||||
tick(1000);
|
tick(1000);
|
||||||
expect(result).toBe('hello');
|
expect(result).toBe('hello');
|
||||||
|
|
||||||
const start = new Date().getTime();
|
const start = new Date().getTime();
|
||||||
let dateDiff = 0;
|
let dateDiff = 0;
|
||||||
interval(1000).pipe(take(2)).subscribe(() => dateDiff = (new Date().getTime() - start));
|
interval(1000).pipe(take(2)).subscribe(() => dateDiff = (new Date().getTime() - start));
|
||||||
|
|
||||||
tick(1000);
|
tick(1000);
|
||||||
expect(dateDiff).toBe(1000);
|
expect(dateDiff).toBe(1000);
|
||||||
tick(1000);
|
tick(1000);
|
||||||
expect(dateDiff).toBe(2000);
|
expect(dateDiff).toBe(2000);
|
||||||
}));
|
}));
|
||||||
// #enddocregion fake-async-test-rxjs
|
// #enddocregion fake-async-test-rxjs
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1266,7 +1266,8 @@ You do have to call [tick()](api/core/testing/tick) to advance the (virtual) clo
|
|||||||
Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish.
|
Calling [tick()](api/core/testing/tick) simulates the passage of time until all pending asynchronous activities finish.
|
||||||
In this case, it waits for the error handler's `setTimeout()`.
|
In this case, it waits for the error handler's `setTimeout()`.
|
||||||
|
|
||||||
The [tick()](api/core/testing/tick) function accepts milliseconds as a parameter (defaults to 0 if not provided). The parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback.
|
The [tick()](api/core/testing/tick) function accepts milliseconds and tickOptions as parameters, the millisecond (defaults to 0 if not provided) parameter represents how much the virtual clock advances. For example, if you have a `setTimeout(fn, 100)` in a `fakeAsync()` test, you need to use tick(100) to trigger the fn callback. The tickOptions is an optional parameter with a property called processNewMacroTasksSynchronously (defaults is true) represents whether to invoke
|
||||||
|
new generated macro tasks when ticking.
|
||||||
|
|
||||||
<code-example
|
<code-example
|
||||||
path="testing/src/app/demo/async-helper.spec.ts"
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
@ -1276,6 +1277,22 @@ The [tick()](api/core/testing/tick) function accepts milliseconds as a parameter
|
|||||||
The [tick()](api/core/testing/tick) function is one of the Angular testing utilities that you import with `TestBed`.
|
The [tick()](api/core/testing/tick) function is one of the Angular testing utilities that you import with `TestBed`.
|
||||||
It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body.
|
It's a companion to `fakeAsync()` and you can only call it within a `fakeAsync()` body.
|
||||||
|
|
||||||
|
#### tickOptions
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
region="fake-async-test-tick-new-macro-task-sync">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
In this example, we have a new macro task (nested setTimeout), by default, when we `tick`, the setTimeout `outside` and `nested` will both be triggered.
|
||||||
|
|
||||||
|
<code-example
|
||||||
|
path="testing/src/app/demo/async-helper.spec.ts"
|
||||||
|
region="fake-async-test-tick-new-macro-task-async">
|
||||||
|
</code-example>
|
||||||
|
|
||||||
|
And in some case, we don't want to trigger the new maco task when ticking, we can use `tick(milliseconds, {processNewMacroTasksSynchronously: false})` to not invoke new maco task.
|
||||||
|
|
||||||
#### Comparing dates inside fakeAsync()
|
#### Comparing dates inside fakeAsync()
|
||||||
|
|
||||||
`fakeAsync()` simulates passage of time, which allows you to calculate the difference between dates inside `fakeAsync()`.
|
`fakeAsync()` simulates passage of time, which allows you to calculate the difference between dates inside `fakeAsync()`.
|
||||||
|
@ -127,6 +127,31 @@ const ProxyZoneSpec: {assertPresent: () => void} = (Zone as any)['ProxyZoneSpec'
|
|||||||
expect(ran).toEqual(true);
|
expect(ran).toEqual(true);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should run new macro tasks created by timer callback', fakeAsync(() => {
|
||||||
|
function nestedTimer(callback: () => any): void {
|
||||||
|
setTimeout(() => setTimeout(() => callback()));
|
||||||
|
}
|
||||||
|
const callback = jasmine.createSpy('callback');
|
||||||
|
nestedTimer(callback);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
tick(0);
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not queue nested timer on tick with processNewMacroTasksSynchronously=false',
|
||||||
|
fakeAsync(() => {
|
||||||
|
function nestedTimer(callback: () => any): void {
|
||||||
|
setTimeout(() => setTimeout(() => callback()));
|
||||||
|
}
|
||||||
|
const callback = jasmine.createSpy('callback');
|
||||||
|
nestedTimer(callback);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
tick(0, {processNewMacroTasksSynchronously: false});
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
flush();
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
it('should run queued timer only once', fakeAsync(() => {
|
it('should run queued timer only once', fakeAsync(() => {
|
||||||
let cycles = 0;
|
let cycles = 0;
|
||||||
setTimeout(() => { cycles++; }, 10);
|
setTimeout(() => { cycles++; }, 10);
|
||||||
|
@ -62,13 +62,54 @@ export function fakeAsync(fn: Function): (...args: any[]) => any {
|
|||||||
*
|
*
|
||||||
* {@example core/testing/ts/fake_async.ts region='basic'}
|
* {@example core/testing/ts/fake_async.ts region='basic'}
|
||||||
*
|
*
|
||||||
|
* @param millis, the number of millisecond to advance the virtual timer
|
||||||
|
* @param tickOptions, the options of tick with a flag called
|
||||||
|
* processNewMacroTasksSynchronously, whether to invoke the new macroTasks, by default is
|
||||||
|
* false, means the new macroTasks will be invoked
|
||||||
|
*
|
||||||
|
* For example,
|
||||||
|
*
|
||||||
|
* it ('test with nested setTimeout', fakeAsync(() => {
|
||||||
|
* let nestedTimeoutInvoked = false;
|
||||||
|
* function funcWithNestedTimeout() {
|
||||||
|
* setTimeout(() => {
|
||||||
|
* nestedTimeoutInvoked = true;
|
||||||
|
* });
|
||||||
|
* };
|
||||||
|
* setTimeout(funcWithNestedTimeout);
|
||||||
|
* tick();
|
||||||
|
* expect(nestedTimeoutInvoked).toBe(true);
|
||||||
|
* }));
|
||||||
|
*
|
||||||
|
* in this case, we have a nested timeout (new macroTask), when we tick, both the
|
||||||
|
* funcWithNestedTimeout and the nested timeout both will be invoked.
|
||||||
|
*
|
||||||
|
* it ('test with nested setTimeout', fakeAsync(() => {
|
||||||
|
* let nestedTimeoutInvoked = false;
|
||||||
|
* function funcWithNestedTimeout() {
|
||||||
|
* setTimeout(() => {
|
||||||
|
* nestedTimeoutInvoked = true;
|
||||||
|
* });
|
||||||
|
* };
|
||||||
|
* setTimeout(funcWithNestedTimeout);
|
||||||
|
* tick(0, {processNewMacroTasksSynchronously: false});
|
||||||
|
* expect(nestedTimeoutInvoked).toBe(false);
|
||||||
|
* }));
|
||||||
|
*
|
||||||
|
* if we pass the tickOptions with processNewMacroTasksSynchronously to be false, the nested timeout
|
||||||
|
* will not be invoked.
|
||||||
|
*
|
||||||
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function tick(millis: number = 0): void {
|
export function tick(
|
||||||
|
millis: number = 0, tickOptions: {processNewMacroTasksSynchronously: boolean} = {
|
||||||
|
processNewMacroTasksSynchronously: true
|
||||||
|
}): void {
|
||||||
if (fakeAsyncTestModule) {
|
if (fakeAsyncTestModule) {
|
||||||
return fakeAsyncTestModule.tick(millis);
|
return fakeAsyncTestModule.tick(millis, tickOptions);
|
||||||
} else {
|
} else {
|
||||||
return tickFallback(millis);
|
return tickFallback(millis, tickOptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,8 +118,11 @@ function _getFakeAsyncZoneSpec(): any {
|
|||||||
*
|
*
|
||||||
* @publicApi
|
* @publicApi
|
||||||
*/
|
*/
|
||||||
export function tickFallback(millis: number = 0): void {
|
export function tickFallback(
|
||||||
_getFakeAsyncZoneSpec().tick(millis);
|
millis: number = 0, tickOptions: {processNewMacroTasksSynchronously: boolean} = {
|
||||||
|
processNewMacroTasksSynchronously: true
|
||||||
|
}): void {
|
||||||
|
_getFakeAsyncZoneSpec().tick(millis, null, tickOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -117,7 +117,9 @@ Zone.__load_patch('fakeasync', (global: any, Zone: ZoneType, api: _ZonePrivate)
|
|||||||
*
|
*
|
||||||
* @experimental
|
* @experimental
|
||||||
*/
|
*/
|
||||||
function tick(millis: number = 0): void { _getFakeAsyncZoneSpec().tick(millis); }
|
function tick(millis: number = 0, ignoreNestedTimeout = false): void {
|
||||||
|
_getFakeAsyncZoneSpec().tick(millis, null, ignoreNestedTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simulates the asynchronous passage of time for the timers in the fakeAsync zone by
|
* Simulates the asynchronous passage of time for the timers in the fakeAsync zone by
|
||||||
|
@ -72,6 +72,8 @@
|
|||||||
private _currentTime: number = 0;
|
private _currentTime: number = 0;
|
||||||
// Current real time in millis.
|
// Current real time in millis.
|
||||||
private _currentRealTime: number = OriginalDate.now();
|
private _currentRealTime: number = OriginalDate.now();
|
||||||
|
// track requeuePeriodicTimer
|
||||||
|
private _currentTickRequeuePeriodicEntries: any[] = [];
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
@ -81,10 +83,24 @@
|
|||||||
|
|
||||||
setCurrentRealTime(realTime: number) { this._currentRealTime = realTime; }
|
setCurrentRealTime(realTime: number) { this._currentRealTime = realTime; }
|
||||||
|
|
||||||
scheduleFunction(
|
scheduleFunction(cb: Function, delay: number, options?: {
|
||||||
cb: Function, delay: number, args: any[] = [], isPeriodic: boolean = false,
|
args?: any[],
|
||||||
isRequestAnimationFrame: boolean = false, id: number = -1): number {
|
isPeriodic?: boolean,
|
||||||
let currentId: number = id < 0 ? Scheduler.nextId++ : id;
|
isRequestAnimationFrame?: boolean,
|
||||||
|
id?: number,
|
||||||
|
isRequeuePeriodic?: boolean
|
||||||
|
}): number {
|
||||||
|
options = {
|
||||||
|
...{
|
||||||
|
args: [],
|
||||||
|
isPeriodic: false,
|
||||||
|
isRequestAnimationFrame: false,
|
||||||
|
id: -1,
|
||||||
|
isRequeuePeriodic: false
|
||||||
|
},
|
||||||
|
...options
|
||||||
|
};
|
||||||
|
let currentId = options.id ! < 0 ? Scheduler.nextId++ : options.id !;
|
||||||
let endTime = this._currentTime + delay;
|
let endTime = this._currentTime + delay;
|
||||||
|
|
||||||
// Insert so that scheduler queue remains sorted by end time.
|
// Insert so that scheduler queue remains sorted by end time.
|
||||||
@ -92,11 +108,14 @@
|
|||||||
endTime: endTime,
|
endTime: endTime,
|
||||||
id: currentId,
|
id: currentId,
|
||||||
func: cb,
|
func: cb,
|
||||||
args: args,
|
args: options.args !,
|
||||||
delay: delay,
|
delay: delay,
|
||||||
isPeriodic: isPeriodic,
|
isPeriodic: options.isPeriodic !,
|
||||||
isRequestAnimationFrame: isRequestAnimationFrame
|
isRequestAnimationFrame: options.isRequestAnimationFrame !
|
||||||
};
|
};
|
||||||
|
if (options.isRequeuePeriodic !) {
|
||||||
|
this._currentTickRequeuePeriodicEntries.push(newEntry);
|
||||||
|
}
|
||||||
let i = 0;
|
let i = 0;
|
||||||
for (; i < this._schedulerQueue.length; i++) {
|
for (; i < this._schedulerQueue.length; i++) {
|
||||||
let currentEntry = this._schedulerQueue[i];
|
let currentEntry = this._schedulerQueue[i];
|
||||||
@ -117,21 +136,37 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(millis: number = 0, doTick?: (elapsed: number) => void): void {
|
tick(millis: number = 0, doTick?: (elapsed: number) => void, tickOptions: {
|
||||||
|
processNewMacroTasksSynchronously: boolean
|
||||||
|
} = {processNewMacroTasksSynchronously: true}): void {
|
||||||
let finalTime = this._currentTime + millis;
|
let finalTime = this._currentTime + millis;
|
||||||
let lastCurrentTime = 0;
|
let lastCurrentTime = 0;
|
||||||
if (this._schedulerQueue.length === 0 && doTick) {
|
// we need to copy the schedulerQueue so nested timeout
|
||||||
|
// will not be wrongly called in the current tick
|
||||||
|
// https://github.com/angular/angular/issues/33799
|
||||||
|
const schedulerQueue = tickOptions.processNewMacroTasksSynchronously ?
|
||||||
|
this._schedulerQueue :
|
||||||
|
this._schedulerQueue.slice();
|
||||||
|
if (schedulerQueue.length === 0 && doTick) {
|
||||||
doTick(millis);
|
doTick(millis);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
while (this._schedulerQueue.length > 0) {
|
while (schedulerQueue.length > 0) {
|
||||||
let current = this._schedulerQueue[0];
|
// clear requeueEntries before each loop
|
||||||
|
this._currentTickRequeuePeriodicEntries = [];
|
||||||
|
let current = schedulerQueue[0];
|
||||||
if (finalTime < current.endTime) {
|
if (finalTime < current.endTime) {
|
||||||
// Done processing the queue since it's sorted by endTime.
|
// Done processing the queue since it's sorted by endTime.
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
// Time to run scheduled function. Remove it from the head of queue.
|
// Time to run scheduled function. Remove it from the head of queue.
|
||||||
let current = this._schedulerQueue.shift() !;
|
let current = schedulerQueue.shift() !;
|
||||||
|
if (!tickOptions.processNewMacroTasksSynchronously) {
|
||||||
|
const idx = this._schedulerQueue.indexOf(current);
|
||||||
|
if (idx >= 0) {
|
||||||
|
this._schedulerQueue.splice(idx, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
lastCurrentTime = this._currentTime;
|
lastCurrentTime = this._currentTime;
|
||||||
this._currentTime = current.endTime;
|
this._currentTime = current.endTime;
|
||||||
if (doTick) {
|
if (doTick) {
|
||||||
@ -143,6 +178,21 @@
|
|||||||
// Uncaught exception in the current scheduled function. Stop processing the queue.
|
// Uncaught exception in the current scheduled function. Stop processing the queue.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check is there any requeue periodic entry is added in
|
||||||
|
// current loop, if there is, we need to add to current loop
|
||||||
|
if (!tickOptions.processNewMacroTasksSynchronously) {
|
||||||
|
this._currentTickRequeuePeriodicEntries.forEach(newEntry => {
|
||||||
|
let i = 0;
|
||||||
|
for (; i < schedulerQueue.length; i++) {
|
||||||
|
const currentEntry = schedulerQueue[i];
|
||||||
|
if (newEntry.endTime < currentEntry.endTime) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
schedulerQueue.splice(i, 0, newEntry);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
lastCurrentTime = this._currentTime;
|
lastCurrentTime = this._currentTime;
|
||||||
@ -274,7 +324,8 @@
|
|||||||
return () => {
|
return () => {
|
||||||
// Requeue the timer callback if it's not been canceled.
|
// Requeue the timer callback if it's not been canceled.
|
||||||
if (this.pendingPeriodicTimers.indexOf(id) !== -1) {
|
if (this.pendingPeriodicTimers.indexOf(id) !== -1) {
|
||||||
this._scheduler.scheduleFunction(fn, interval, args, true, false, id);
|
this._scheduler.scheduleFunction(
|
||||||
|
fn, interval, {args, isPeriodic: true, id, isRequeuePeriodic: true});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -287,7 +338,8 @@
|
|||||||
let removeTimerFn = this._dequeueTimer(Scheduler.nextId);
|
let removeTimerFn = this._dequeueTimer(Scheduler.nextId);
|
||||||
// Queue the callback and dequeue the timer on success and error.
|
// Queue the callback and dequeue the timer on success and error.
|
||||||
let cb = this._fnAndFlush(fn, {onSuccess: removeTimerFn, onError: removeTimerFn});
|
let cb = this._fnAndFlush(fn, {onSuccess: removeTimerFn, onError: removeTimerFn});
|
||||||
let id = this._scheduler.scheduleFunction(cb, delay, args, false, !isTimer);
|
let id =
|
||||||
|
this._scheduler.scheduleFunction(cb, delay, {args, isRequestAnimationFrame: !isTimer});
|
||||||
if (isTimer) {
|
if (isTimer) {
|
||||||
this.pendingTimers.push(id);
|
this.pendingTimers.push(id);
|
||||||
}
|
}
|
||||||
@ -308,7 +360,7 @@
|
|||||||
completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id);
|
completers.onSuccess = this._requeuePeriodicTimer(cb, interval, args, id);
|
||||||
|
|
||||||
// Queue the callback and dequeue the periodic timer only on error.
|
// Queue the callback and dequeue the periodic timer only on error.
|
||||||
this._scheduler.scheduleFunction(cb, interval, args, true);
|
this._scheduler.scheduleFunction(cb, interval, {args, isPeriodic: true});
|
||||||
this.pendingPeriodicTimers.push(id);
|
this.pendingPeriodicTimers.push(id);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
@ -380,10 +432,12 @@
|
|||||||
FakeAsyncTestZoneSpec.resetDate();
|
FakeAsyncTestZoneSpec.resetDate();
|
||||||
}
|
}
|
||||||
|
|
||||||
tick(millis: number = 0, doTick?: (elapsed: number) => void): void {
|
tick(millis: number = 0, doTick?: (elapsed: number) => void, tickOptions: {
|
||||||
|
processNewMacroTasksSynchronously: boolean
|
||||||
|
} = {processNewMacroTasksSynchronously: true}): void {
|
||||||
FakeAsyncTestZoneSpec.assertInZone();
|
FakeAsyncTestZoneSpec.assertInZone();
|
||||||
this.flushMicrotasks();
|
this.flushMicrotasks();
|
||||||
this._scheduler.tick(millis, doTick);
|
this._scheduler.tick(millis, doTick, tickOptions);
|
||||||
if (this._lastError !== null) {
|
if (this._lastError !== null) {
|
||||||
this._resetLastErrorAndThrow();
|
this._resetLastErrorAndThrow();
|
||||||
}
|
}
|
||||||
|
@ -145,6 +145,22 @@ describe('FakeAsyncTestZoneSpec', () => {
|
|||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should not queue new macro task on tick with processNewMacroTasksSynchronously=false',
|
||||||
|
() => {
|
||||||
|
function nestedTimer(callback: () => any): void {
|
||||||
|
setTimeout(() => setTimeout(() => callback()));
|
||||||
|
}
|
||||||
|
fakeAsyncTestZone.run(() => {
|
||||||
|
const callback = jasmine.createSpy('callback');
|
||||||
|
nestedTimer(callback);
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
testZoneSpec.tick(0, null, {processNewMacroTasksSynchronously: false});
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
testZoneSpec.flush();
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should run queued timer after sufficient clock ticks', () => {
|
it('should run queued timer after sufficient clock ticks', () => {
|
||||||
fakeAsyncTestZone.run(() => {
|
fakeAsyncTestZone.run(() => {
|
||||||
let ran = false;
|
let ran = false;
|
||||||
|
4
tools/public_api_guard/core/testing.d.ts
vendored
4
tools/public_api_guard/core/testing.d.ts
vendored
@ -134,7 +134,9 @@ export declare type TestModuleMetadata = {
|
|||||||
aotSummaries?: () => any[];
|
aotSummaries?: () => any[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export declare function tick(millis?: number): void;
|
export declare function tick(millis?: number, tickOptions?: {
|
||||||
|
processNewMacroTasksSynchronously: boolean;
|
||||||
|
}): void;
|
||||||
|
|
||||||
export declare function withModule(moduleDef: TestModuleMetadata): InjectSetupWrapper;
|
export declare function withModule(moduleDef: TestModuleMetadata): InjectSetupWrapper;
|
||||||
export declare function withModule(moduleDef: TestModuleMetadata, fn: Function): () => any;
|
export declare function withModule(moduleDef: TestModuleMetadata, fn: Function): () => any;
|
||||||
|
Reference in New Issue
Block a user