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

Commit a4e10f5

Browse files
JiaLiPassionmhevery
authored andcommitted
fix(fakeAsyncTest): fix #937, let user be able to customize testable macroTask (#938)
* fix(fakeAsyncTest): fix #937, let user be able to customize testable macroTask * add global flag to define fakeAsyncTest macroTaskOptions * support set callback arguments
1 parent b7b1743 commit a4e10f5

File tree

3 files changed

+145
-2
lines changed

3 files changed

+145
-2
lines changed

lib/zone-spec/fake-async-test.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
target: any;
2424
}
2525

26+
interface MacroTaskOptions {
27+
source: string;
28+
isPeriodic?: boolean;
29+
callbackArgs?: any;
30+
}
31+
2632
class Scheduler {
2733
// Next scheduler id.
2834
public nextId: number = 0;
@@ -172,8 +178,15 @@
172178
pendingPeriodicTimers: number[] = [];
173179
pendingTimers: number[] = [];
174180

175-
constructor(namePrefix: string, private trackPendingRequestAnimationFrame = false) {
181+
constructor(
182+
namePrefix: string, private trackPendingRequestAnimationFrame = false,
183+
private macroTaskOptions?: MacroTaskOptions[]) {
176184
this.name = 'fakeAsyncTestZone for ' + namePrefix;
185+
// in case user can't access the construction of FakyAsyncTestSpec
186+
// user can also define macroTaskOptions by define a global variable.
187+
if (!this.macroTaskOptions) {
188+
this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')];
189+
}
177190
}
178191

179192
private _fnAndFlush(fn: Function, completers: {onSuccess?: Function, onError?: Function}):
@@ -352,6 +365,24 @@
352365
this.trackPendingRequestAnimationFrame);
353366
break;
354367
default:
368+
// user can define which macroTask they want to support by passing
369+
// macroTaskOptions
370+
const macroTaskOption = this.findMacroTaskOption(task);
371+
if (macroTaskOption) {
372+
const args = task.data && (task.data as any)['args'];
373+
const delay = args && args.length > 1 ? args[1] : 0;
374+
let callbackArgs =
375+
macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args;
376+
if (!!macroTaskOption.isPeriodic) {
377+
// periodic macroTask, use setInterval to simulate
378+
task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs);
379+
task.data.isPeriodic = true;
380+
} else {
381+
// not periodic, use setTimout to simulate
382+
task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs);
383+
}
384+
break;
385+
}
355386
throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source);
356387
}
357388
break;
@@ -372,10 +403,31 @@
372403
case 'setInterval':
373404
return this._clearInterval(task.data['handleId']);
374405
default:
406+
// user can define which macroTask they want to support by passing
407+
// macroTaskOptions
408+
const macroTaskOption = this.findMacroTaskOption(task);
409+
if (macroTaskOption) {
410+
const handleId = task.data['handleId'];
411+
return macroTaskOption.isPeriodic ? this._clearInterval(handleId) :
412+
this._clearTimeout(handleId);
413+
}
375414
return delegate.cancelTask(target, task);
376415
}
377416
}
378417

418+
findMacroTaskOption(task: Task) {
419+
if (!this.macroTaskOptions) {
420+
return null;
421+
}
422+
for (let i = 0; i < this.macroTaskOptions.length; i++) {
423+
const macroTaskOption = this.macroTaskOptions[i];
424+
if (macroTaskOption.source === task.source) {
425+
return macroTaskOption;
426+
}
427+
}
428+
return null;
429+
}
430+
379431
onHandleError(
380432
parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
381433
error: any): boolean {

test/test_fake_polyfill.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,5 @@
6464

6565
global['__Zone_ignore_on_properties'] =
6666
[{target: TestTarget.prototype, ignoreProperties: ['prop1']}];
67+
global['__zone_symbol__FakeAsyncTestMacroTask'] = [{source: 'TestClass.myTimeout'}];
6768
})(typeof window === 'object' && window || typeof self === 'object' && self || global);

test/zone-spec/fake-async-test.spec.ts

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import '../../lib/zone-spec/fake-async-test';
1010

11-
import {isNode} from '../../lib/common/utils';
11+
import {isNode, patchMacroTask} from '../../lib/common/utils';
1212
import {ifEnvSupports} from '../test-util';
1313

1414
function supportNode() {
@@ -715,4 +715,94 @@ describe('FakeAsyncTestZoneSpec', () => {
715715

716716
});
717717
}));
718+
719+
describe('should allow user define which macroTask fakeAsyncTest', () => {
720+
let FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
721+
let testZoneSpec: any;
722+
let fakeAsyncTestZone: Zone;
723+
it('should support custom non perodic macroTask', () => {
724+
testZoneSpec = new FakeAsyncTestZoneSpec(
725+
'name', false, [{source: 'TestClass.myTimeout', callbackArgs: ['test']}]);
726+
class TestClass {
727+
myTimeout(callback: Function) {}
728+
}
729+
fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
730+
fakeAsyncTestZone.run(() => {
731+
let ran = false;
732+
patchMacroTask(
733+
TestClass.prototype, 'myTimeout',
734+
(self: any, args: any[]) =>
735+
({name: 'TestClass.myTimeout', target: self, callbackIndex: 0, args: args}));
736+
737+
const testClass = new TestClass();
738+
testClass.myTimeout(function(callbackArgs: any) {
739+
ran = true;
740+
expect(callbackArgs).toEqual('test');
741+
});
742+
743+
expect(ran).toEqual(false);
744+
745+
testZoneSpec.tick();
746+
expect(ran).toEqual(true);
747+
});
748+
});
749+
750+
it('should support custom non perodic macroTask by global flag', () => {
751+
testZoneSpec = new FakeAsyncTestZoneSpec('name');
752+
class TestClass {
753+
myTimeout(callback: Function) {}
754+
}
755+
fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
756+
fakeAsyncTestZone.run(() => {
757+
let ran = false;
758+
patchMacroTask(
759+
TestClass.prototype, 'myTimeout',
760+
(self: any, args: any[]) =>
761+
({name: 'TestClass.myTimeout', target: self, callbackIndex: 0, args: args}));
762+
763+
const testClass = new TestClass();
764+
testClass.myTimeout(() => {
765+
ran = true;
766+
});
767+
768+
expect(ran).toEqual(false);
769+
770+
testZoneSpec.tick();
771+
expect(ran).toEqual(true);
772+
});
773+
});
774+
775+
776+
it('should support custom perodic macroTask', () => {
777+
testZoneSpec = new FakeAsyncTestZoneSpec(
778+
'name', false, [{source: 'TestClass.myInterval', isPeriodic: true}]);
779+
fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
780+
fakeAsyncTestZone.run(() => {
781+
let cycle = 0;
782+
class TestClass {
783+
myInterval(callback: Function, interval: number): any {
784+
return null;
785+
}
786+
}
787+
patchMacroTask(
788+
TestClass.prototype, 'myInterval',
789+
(self: any, args: any[]) =>
790+
({name: 'TestClass.myInterval', target: self, callbackIndex: 0, args: args}));
791+
792+
const testClass = new TestClass();
793+
const id = testClass.myInterval(() => {
794+
cycle++;
795+
}, 10);
796+
797+
expect(cycle).toEqual(0);
798+
799+
testZoneSpec.tick(10);
800+
expect(cycle).toEqual(1);
801+
802+
testZoneSpec.tick(10);
803+
expect(cycle).toEqual(2);
804+
clearInterval(id);
805+
});
806+
});
807+
});
718808
});

0 commit comments

Comments
 (0)