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

fix(fakeAsyncTest): fix #937, let user be able to customize testable macroTask #938

Merged
merged 3 commits into from
Dec 27, 2017
Merged
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
54 changes: 53 additions & 1 deletion lib/zone-spec/fake-async-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@
target: any;
}

interface MacroTaskOptions {
source: string;
isPeriodic?: boolean;
callbackArgs?: any;
}

class Scheduler {
// Next scheduler id.
public nextId: number = 0;
Expand Down Expand Up @@ -172,8 +178,15 @@
pendingPeriodicTimers: number[] = [];
pendingTimers: number[] = [];

constructor(namePrefix: string, private trackPendingRequestAnimationFrame = false) {
constructor(
namePrefix: string, private trackPendingRequestAnimationFrame = false,
private macroTaskOptions?: MacroTaskOptions[]) {
this.name = 'fakeAsyncTestZone for ' + namePrefix;
// in case user can't access the construction of FakyAsyncTestSpec
// user can also define macroTaskOptions by define a global variable.
if (!this.macroTaskOptions) {
this.macroTaskOptions = global[Zone.__symbol__('FakeAsyncTestMacroTask')];
}
}

private _fnAndFlush(fn: Function, completers: {onSuccess?: Function, onError?: Function}):
Expand Down Expand Up @@ -352,6 +365,24 @@
this.trackPendingRequestAnimationFrame);
break;
default:
// user can define which macroTask they want to support by passing
// macroTaskOptions
const macroTaskOption = this.findMacroTaskOption(task);
if (macroTaskOption) {
const args = task.data && (task.data as any)['args'];
const delay = args && args.length > 1 ? args[1] : 0;
let callbackArgs =
macroTaskOption.callbackArgs ? macroTaskOption.callbackArgs : args;
if (!!macroTaskOption.isPeriodic) {
// periodic macroTask, use setInterval to simulate
task.data['handleId'] = this._setInterval(task.invoke, delay, callbackArgs);
task.data.isPeriodic = true;
} else {
// not periodic, use setTimout to simulate
task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs);
}
break;
}
throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source);
}
break;
Expand All @@ -372,10 +403,31 @@
case 'setInterval':
return this._clearInterval(task.data['handleId']);
default:
// user can define which macroTask they want to support by passing
// macroTaskOptions
const macroTaskOption = this.findMacroTaskOption(task);
if (macroTaskOption) {
const handleId = task.data['handleId'];
return macroTaskOption.isPeriodic ? this._clearInterval(handleId) :
this._clearTimeout(handleId);
}
return delegate.cancelTask(target, task);
}
}

findMacroTaskOption(task: Task) {
if (!this.macroTaskOptions) {
return null;
}
for (let i = 0; i < this.macroTaskOptions.length; i++) {
const macroTaskOption = this.macroTaskOptions[i];
if (macroTaskOption.source === task.source) {
return macroTaskOption;
}
}
return null;
}

onHandleError(
parentZoneDelegate: ZoneDelegate, currentZone: Zone, targetZone: Zone,
error: any): boolean {
Expand Down
1 change: 1 addition & 0 deletions test/test_fake_polyfill.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,5 @@

global['__Zone_ignore_on_properties'] =
[{target: TestTarget.prototype, ignoreProperties: ['prop1']}];
global['__zone_symbol__FakeAsyncTestMacroTask'] = [{source: 'TestClass.myTimeout'}];
})(typeof window === 'object' && window || typeof self === 'object' && self || global);
92 changes: 91 additions & 1 deletion test/zone-spec/fake-async-test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

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

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

function supportNode() {
Expand Down Expand Up @@ -715,4 +715,94 @@ describe('FakeAsyncTestZoneSpec', () => {

});
}));

describe('should allow user define which macroTask fakeAsyncTest', () => {
let FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec'];
let testZoneSpec: any;
let fakeAsyncTestZone: Zone;
it('should support custom non perodic macroTask', () => {
testZoneSpec = new FakeAsyncTestZoneSpec(
'name', false, [{source: 'TestClass.myTimeout', callbackArgs: ['test']}]);
class TestClass {
myTimeout(callback: Function) {}
}
fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
fakeAsyncTestZone.run(() => {
let ran = false;
patchMacroTask(
TestClass.prototype, 'myTimeout',
(self: any, args: any[]) =>
({name: 'TestClass.myTimeout', target: self, callbackIndex: 0, args: args}));

const testClass = new TestClass();
testClass.myTimeout(function(callbackArgs: any) {
ran = true;
expect(callbackArgs).toEqual('test');
});

expect(ran).toEqual(false);

testZoneSpec.tick();
expect(ran).toEqual(true);
});
});

it('should support custom non perodic macroTask by global flag', () => {
testZoneSpec = new FakeAsyncTestZoneSpec('name');
class TestClass {
myTimeout(callback: Function) {}
}
fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
fakeAsyncTestZone.run(() => {
let ran = false;
patchMacroTask(
TestClass.prototype, 'myTimeout',
(self: any, args: any[]) =>
({name: 'TestClass.myTimeout', target: self, callbackIndex: 0, args: args}));

const testClass = new TestClass();
testClass.myTimeout(() => {
ran = true;
});

expect(ran).toEqual(false);

testZoneSpec.tick();
expect(ran).toEqual(true);
});
});


it('should support custom perodic macroTask', () => {
testZoneSpec = new FakeAsyncTestZoneSpec(
'name', false, [{source: 'TestClass.myInterval', isPeriodic: true}]);
fakeAsyncTestZone = Zone.current.fork(testZoneSpec);
fakeAsyncTestZone.run(() => {
let cycle = 0;
class TestClass {
myInterval(callback: Function, interval: number): any {
return null;
}
}
patchMacroTask(
TestClass.prototype, 'myInterval',
(self: any, args: any[]) =>
({name: 'TestClass.myInterval', target: self, callbackIndex: 0, args: args}));

const testClass = new TestClass();
const id = testClass.myInterval(() => {
cycle++;
}, 10);

expect(cycle).toEqual(0);

testZoneSpec.tick(10);
expect(cycle).toEqual(1);

testZoneSpec.tick(10);
expect(cycle).toEqual(2);
clearInterval(id);
});
});
});
});