From 6ef92baaa4eb114de9264f16294e3114b6b74a95 Mon Sep 17 00:00:00 2001 From: "JiaLi.Passion" Date: Wed, 25 Oct 2017 16:27:11 +0900 Subject: [PATCH 1/3] fix(fakeAsyncTest): fix #937, let user be able to customize testable macroTask --- lib/zone-spec/fake-async-test.ts | 48 ++++++++++++++++++- test/zone-spec/fake-async-test.spec.ts | 64 +++++++++++++++++++++++++- 2 files changed, 110 insertions(+), 2 deletions(-) diff --git a/lib/zone-spec/fake-async-test.ts b/lib/zone-spec/fake-async-test.ts index a22134002..94270d4d1 100644 --- a/lib/zone-spec/fake-async-test.ts +++ b/lib/zone-spec/fake-async-test.ts @@ -23,6 +23,11 @@ target: any; } + interface MacroTaskOptions { + source: string; + isPeriodic?: boolean; + } + class Scheduler { // Next scheduler id. public nextId: number = 0; @@ -172,7 +177,9 @@ pendingPeriodicTimers: number[] = []; pendingTimers: number[] = []; - constructor(namePrefix: string, private trackPendingRequestAnimationFrame = false) { + constructor( + namePrefix: string, private trackPendingRequestAnimationFrame = false, + private macroTaskOptions?: MacroTaskOptions[]) { this.name = 'fakeAsyncTestZone for ' + namePrefix; } @@ -352,6 +359,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; + if (!!macroTaskOption.isPeriodic) { + // periodic macroTask, use setInterval to simulate + task.data['handleId'] = + this._setInterval(task.invoke, delay, (task.data as any)['args']); + task.data.isPeriodic = true; + } else { + // not periodic, use setTimout to simulate + task.data['handleId'] = + this._setTimeout(task.invoke, delay, (task.data as any)['args']); + } + break; + } throw new Error('Unknown macroTask scheduled in fake async test: ' + task.source); } break; @@ -372,10 +397,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 { diff --git a/test/zone-spec/fake-async-test.spec.ts b/test/zone-spec/fake-async-test.spec.ts index 13a96afa8..def730566 100644 --- a/test/zone-spec/fake-async-test.spec.ts +++ b/test/zone-spec/fake-async-test.spec.ts @@ -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() { @@ -715,4 +715,66 @@ 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'}]); + 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); + }); + }); + }); }); From 43fc6d9f451e26dd88713f3771086df11a2d4e81 Mon Sep 17 00:00:00 2001 From: "JiaLi.Passion" Date: Wed, 25 Oct 2017 16:41:03 +0900 Subject: [PATCH 2/3] add global flag to define fakeAsyncTest macroTaskOptions --- lib/zone-spec/fake-async-test.ts | 5 +++++ test/test_fake_polyfill.ts | 1 + test/zone-spec/fake-async-test.spec.ts | 26 ++++++++++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/lib/zone-spec/fake-async-test.ts b/lib/zone-spec/fake-async-test.ts index 94270d4d1..9ee337fc6 100644 --- a/lib/zone-spec/fake-async-test.ts +++ b/lib/zone-spec/fake-async-test.ts @@ -181,6 +181,11 @@ 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}): diff --git a/test/test_fake_polyfill.ts b/test/test_fake_polyfill.ts index b6d3e63c3..27488c808 100644 --- a/test/test_fake_polyfill.ts +++ b/test/test_fake_polyfill.ts @@ -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); diff --git a/test/zone-spec/fake-async-test.spec.ts b/test/zone-spec/fake-async-test.spec.ts index def730566..159092e9c 100644 --- a/test/zone-spec/fake-async-test.spec.ts +++ b/test/zone-spec/fake-async-test.spec.ts @@ -745,6 +745,32 @@ describe('FakeAsyncTestZoneSpec', () => { }); }); + 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}]); From c9b5567a178fa77563625ff6a387b39e58cca907 Mon Sep 17 00:00:00 2001 From: "JiaLi.Passion" Date: Wed, 25 Oct 2017 23:10:16 +0900 Subject: [PATCH 3/3] support set callback arguments --- lib/zone-spec/fake-async-test.ts | 9 +++++---- test/zone-spec/fake-async-test.spec.ts | 6 ++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/zone-spec/fake-async-test.ts b/lib/zone-spec/fake-async-test.ts index 9ee337fc6..5123743a5 100644 --- a/lib/zone-spec/fake-async-test.ts +++ b/lib/zone-spec/fake-async-test.ts @@ -26,6 +26,7 @@ interface MacroTaskOptions { source: string; isPeriodic?: boolean; + callbackArgs?: any; } class Scheduler { @@ -370,15 +371,15 @@ 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, (task.data as any)['args']); + 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, (task.data as any)['args']); + task.data['handleId'] = this._setTimeout(task.invoke, delay, callbackArgs); } break; } diff --git a/test/zone-spec/fake-async-test.spec.ts b/test/zone-spec/fake-async-test.spec.ts index 159092e9c..8c53da96b 100644 --- a/test/zone-spec/fake-async-test.spec.ts +++ b/test/zone-spec/fake-async-test.spec.ts @@ -721,7 +721,8 @@ describe('FakeAsyncTestZoneSpec', () => { let testZoneSpec: any; let fakeAsyncTestZone: Zone; it('should support custom non perodic macroTask', () => { - testZoneSpec = new FakeAsyncTestZoneSpec('name', false, [{source: 'TestClass.myTimeout'}]); + testZoneSpec = new FakeAsyncTestZoneSpec( + 'name', false, [{source: 'TestClass.myTimeout', callbackArgs: ['test']}]); class TestClass { myTimeout(callback: Function) {} } @@ -734,8 +735,9 @@ describe('FakeAsyncTestZoneSpec', () => { ({name: 'TestClass.myTimeout', target: self, callbackIndex: 0, args: args})); const testClass = new TestClass(); - testClass.myTimeout(() => { + testClass.myTimeout(function(callbackArgs: any) { ran = true; + expect(callbackArgs).toEqual('test'); }); expect(ran).toEqual(false);