diff --git a/lib/jasmine/jasmine.ts b/lib/jasmine/jasmine.ts index 4ff7762f7..5d279ab36 100644 --- a/lib/jasmine/jasmine.ts +++ b/lib/jasmine/jasmine.ts @@ -67,10 +67,14 @@ return originalJasmineFn.apply(this, arguments); }; }); - if (enableClockPatch) { - const originalClockFn: Function = ((jasmine as any)[symbol('clock')] = jasmine['clock']); - (jasmine as any)['clock'] = function() { - const clock = originalClockFn.apply(this, arguments); + + // need to patch jasmine.clock().mockDate and jasmine.clock().tick() so + // they can work properly in FakeAsyncTest + const originalClockFn: Function = ((jasmine as any)[symbol('clock')] = jasmine['clock']); + (jasmine as any)['clock'] = function() { + const clock = originalClockFn.apply(this, arguments); + if (!clock[symbol('patched')]) { + clock[symbol('patched')] = symbol('patched'); const originalTick = (clock[symbol('tick')] = clock.tick); clock.tick = function() { const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); @@ -83,7 +87,7 @@ clock.mockDate = function() { const fakeAsyncZoneSpec = Zone.current.get('FakeAsyncTestZoneSpec'); if (fakeAsyncZoneSpec) { - const dateTime = arguments[0]; + const dateTime = arguments.length > 0 ? arguments[0] : new Date(); return fakeAsyncZoneSpec.setCurrentRealTime.apply( fakeAsyncZoneSpec, dateTime && typeof dateTime.getTime === 'function' ? [dateTime.getTime()] : @@ -91,20 +95,23 @@ } return originalMockDate.apply(this, arguments); }; - ['install', 'uninstall'].forEach(methodName => { - const originalClockFn: Function = (clock[symbol(methodName)] = clock[methodName]); - clock[methodName] = function() { - const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec']; - if (FakeAsyncTestZoneSpec) { - (jasmine as any)[symbol('clockInstalled')] = 'install' === methodName; - return; - } - return originalClockFn.apply(this, arguments); - }; - }); - return clock; - }; - } + // for auto go into fakeAsync feature, we need the flag to enable it + if (enableClockPatch) { + ['install', 'uninstall'].forEach(methodName => { + const originalClockFn: Function = (clock[symbol(methodName)] = clock[methodName]); + clock[methodName] = function() { + const FakeAsyncTestZoneSpec = (Zone as any)['FakeAsyncTestZoneSpec']; + if (FakeAsyncTestZoneSpec) { + (jasmine as any)[symbol('clockInstalled')] = 'install' === methodName; + return; + } + return originalClockFn.apply(this, arguments); + }; + }); + } + } + return clock; + }; /** * Gets a function wrapping the body of a Jasmine `describe` block to execute in a diff --git a/lib/zone-spec/fake-async-test.ts b/lib/zone-spec/fake-async-test.ts index c85205047..278cc1f52 100644 --- a/lib/zone-spec/fake-async-test.ts +++ b/lib/zone-spec/fake-async-test.ts @@ -32,13 +32,14 @@ const OriginalDate = global.Date; class FakeDate { constructor() { - const d = new OriginalDate(); - d.setTime(global.Date.now()); - return d; - } - - static UTC() { - return OriginalDate.UTC(); + if (arguments.length === 0) { + const d = new OriginalDate(); + d.setTime(FakeDate.now()); + return d; + } else { + const args = Array.prototype.slice.call(arguments); + return new OriginalDate(...args); + } } static now() { @@ -48,12 +49,19 @@ } return OriginalDate.now.apply(this, arguments); } - - static parse() { - return OriginalDate.parse(); - } } + (FakeDate as any).UTC = OriginalDate.UTC; + (FakeDate as any).parse = OriginalDate.parse; + + // keep a reference for zone patched timer function + const timers = { + setTimeout: global.setTimeout, + setInterval: global.setInterval, + clearTimeout: global.clearTimeout, + clearInterval: global.clearInterval + }; + class Scheduler { // Next scheduler id. public nextId: number = 1; @@ -63,7 +71,7 @@ // Current simulated time in millis. private _currentTime: number = 0; // Current real time in millis. - private _currentRealTime: number = Date.now(); + private _currentRealTime: number = OriginalDate.now(); constructor() {} @@ -341,6 +349,11 @@ } global['Date'] = FakeDate; FakeDate.prototype = OriginalDate.prototype; + + // try check and reset timers + // because jasmine.clock().install() may + // have replaced the global timer + FakeAsyncTestZoneSpec.checkTimerPatch(); } static resetDate() { @@ -349,6 +362,17 @@ } } + static checkTimerPatch() { + if (global.setTimeout !== timers.setTimeout) { + global.setTimeout = timers.setTimeout; + global.clearTimeout = timers.clearTimeout; + } + if (global.setInterval !== timers.setInterval) { + global.setInterval = timers.setInterval; + global.clearInterval = timers.clearInterval; + } + } + lockDatePatch() { this.patchDateLocked = true; FakeAsyncTestZoneSpec.patchDate(); diff --git a/test/test-env-setup-mocha.ts b/test/test-env-setup-mocha.ts index bf2129452..96058a0c4 100644 --- a/test/test-env-setup-mocha.ts +++ b/test/test-env-setup-mocha.ts @@ -81,6 +81,11 @@ declare const global: any; throw new Error(`Expected ${expected} to be greater than ${actual}`); } }, + toBeLessThan: function(actual: number) { + if (expected >= actual) { + throw new Error(`Expected ${expected} to be lesser than ${actual}`); + } + }, toBeDefined: function() { if (!expected) { throw new Error(`Expected ${expected} to be defined`); @@ -109,6 +114,11 @@ declare const global: any; throw new Error(`Expected ${expected} to be truthy`); } }, + toBeFalsy: function(actual: any) { + if (!!actual) { + throw new Error(`Expected ${actual} to be falsy`); + } + }, toContain: function(actual: any) { if (expected.indexOf(actual) === -1) { throw new Error(`Expected ${expected} to contain ${actual}`); @@ -159,7 +169,11 @@ declare const global: any; if (expected > actual) { throw new Error(`Expected ${expected} not to be greater than ${actual}`); } - + }, + toBeLessThan: function(actual: number) { + if (expected < actual) { + throw new Error(`Expected ${expected} not to be lesser than ${actual}`); + } }, toHaveBeenCalledWith: function(params: any[]) { if (!eq(expected.callArgs, params)) { diff --git a/test/zone-spec/fake-async-test.spec.ts b/test/zone-spec/fake-async-test.spec.ts index b64bda8fa..f92f1b96b 100644 --- a/test/zone-spec/fake-async-test.spec.ts +++ b/test/zone-spec/fake-async-test.spec.ts @@ -913,17 +913,51 @@ describe('FakeAsyncTestZoneSpec', () => { expect(d instanceof Date).toBe(true); }); }); + + it('should new Date with parameter correctly', () => { + fakeAsyncTestZone.run(() => { + const d: Date = new Date(0); + expect(d.getFullYear()).toBeLessThan(1971); + const d1: Date = new Date('December 17, 1995 03:24:00'); + expect(d1.getFullYear()).toEqual(1995); + const d2: Date = new Date(1995, 11, 17, 3, 24, 0); + expect(d2.getFullYear()).toEqual(1995); + + d2.setFullYear(1985); + expect(isNaN(d2.getTime())).toBeFalsy(); + expect(d2.getFullYear()).toBe(1985); + expect(d2.getMonth()).toBe(11); + expect(d2.getDate()).toBe(17); + }); + }); + + it('should get Date.UTC() correctly', () => { + fakeAsyncTestZone.run(() => { + const utcDate = new Date(Date.UTC(96, 11, 1, 0, 0, 0)); + expect(utcDate.getFullYear()).toBe(1996); + }); + }); + + it('should call Date.parse() correctly', () => { + fakeAsyncTestZone.run(() => { + const unixTimeZero = Date.parse('01 Jan 1970 00:00:00 GMT'); + expect(unixTimeZero).toBe(0); + }); + }); + }); describe( - 'fakeAsyncTest should work without jasmine.clock', + 'fakeAsyncTest should work without patch jasmine.clock', ifEnvSupports( () => { return !supportClock() && supportNode(); }, () => { const fakeAsync = (Zone as any)[Zone.__symbol__('fakeAsyncTest')].fakeAsync; + let spy: any; beforeEach(() => { + spy = jasmine.createSpy('timer'); jasmine.clock().install(); }); @@ -932,11 +966,44 @@ describe('FakeAsyncTestZoneSpec', () => { }); it('should check date type correctly', fakeAsync(() => { + const d: any = new Date(); + expect(d instanceof Date).toBe(true); + })); + + it('should check date type correctly without fakeAsync', () => { const d: any = new Date(); expect(d instanceof Date).toBe(true); - })); + }); + + it('should tick correctly', fakeAsync(() => { + jasmine.clock().mockDate(); + const start = Date.now(); + jasmine.clock().tick(100); + const end = Date.now(); + expect(end - start).toBe(100); + })); + + it('should tick correctly without fakeAsync', () => { + jasmine.clock().mockDate(); + const start = Date.now(); + jasmine.clock().tick(100); + const end = Date.now(); + expect(end - start).toBe(100); + }); it('should mock date correctly', fakeAsync(() => { + const baseTime = new Date(2013, 9, 23); + jasmine.clock().mockDate(baseTime); + const start = Date.now(); + expect(start).toBe(baseTime.getTime()); + jasmine.clock().tick(100); + const end = Date.now(); + expect(end - start).toBe(100); + expect(end).toBe(baseTime.getTime() + 100); + expect(new Date().getFullYear()).toEqual(2013); + })); + + it('should mock date correctly without fakeAsync', () => { const baseTime = new Date(2013, 9, 23); jasmine.clock().mockDate(baseTime); const start = Date.now(); @@ -945,9 +1012,21 @@ describe('FakeAsyncTestZoneSpec', () => { const end = Date.now(); expect(end - start).toBe(100); expect(end).toBe(baseTime.getTime() + 100); - })); + expect(new Date().getFullYear()).toEqual(2013); + }); it('should handle new Date correctly', fakeAsync(() => { + const baseTime = new Date(2013, 9, 23); + jasmine.clock().mockDate(baseTime); + const start = new Date(); + expect(start.getTime()).toBe(baseTime.getTime()); + jasmine.clock().tick(100); + const end = new Date(); + expect(end.getTime() - start.getTime()).toBe(100); + expect(end.getTime()).toBe(baseTime.getTime() + 100); + })); + + it('should handle new Date correctly without fakeAsync', () => { const baseTime = new Date(2013, 9, 23); jasmine.clock().mockDate(baseTime); const start = new Date(); @@ -956,11 +1035,27 @@ describe('FakeAsyncTestZoneSpec', () => { const end = new Date(); expect(end.getTime() - start.getTime()).toBe(100); expect(end.getTime()).toBe(baseTime.getTime() + 100); - })); + }); + + it('should handle setTimeout correctly', fakeAsync(() => { + setTimeout(spy, 100); + expect(spy).not.toHaveBeenCalled(); + jasmine.clock().tick(100); + expect(spy).toHaveBeenCalled(); + })); + + it('should handle setTimeout correctly without fakeAsync', () => { + setTimeout(spy, 100); + expect(spy).not.toHaveBeenCalled(); + jasmine.clock().tick(100); + expect(spy).toHaveBeenCalled(); + }); })); describe('fakeAsyncTest should patch jasmine.clock', ifEnvSupports(supportClock, () => { + let spy: any; beforeEach(() => { + spy = jasmine.createSpy('timer'); jasmine.clock().install(); }); @@ -980,6 +1075,13 @@ describe('FakeAsyncTestZoneSpec', () => { expect(end - start).toBe(100); }); + it('should tick correctly', () => { + const start = Date.now(); + jasmine.clock().tick(100); + const end = Date.now(); + expect(end - start).toBe(100); + }); + it('should mock date correctly', () => { const baseTime = new Date(2013, 9, 23); jasmine.clock().mockDate(baseTime); @@ -1001,6 +1103,13 @@ describe('FakeAsyncTestZoneSpec', () => { expect(end.getTime() - start.getTime()).toBe(100); expect(end.getTime()).toBe(baseTime.getTime() + 100); }); + + it('should handle setTimeout correctly', () => { + setTimeout(spy, 100); + expect(spy).not.toHaveBeenCalled(); + jasmine.clock().tick(100); + expect(spy).toHaveBeenCalled(); + }); })); describe('fakeAsyncTest should patch rxjs scheduler', ifEnvSupports(supportClock, () => { @@ -1427,6 +1536,44 @@ const {fakeAsync, tick, discardPeriodicTasks, flush, flushMicrotasks} = fakeAsyn expect(zoneInTest1).toBe(zoneInBeforeEach); })); }); + + describe('fakeAsync should work with Date', () => { + it('should get date diff correctly', fakeAsync(() => { + const start = Date.now(); + tick(100); + const end = Date.now(); + expect(end - start).toBe(100); + })); + + it('should check date type correctly', fakeAsync(() => { + const d: any = new Date(); + expect(d instanceof Date).toBe(true); + })); + + it('should new Date with parameter correctly', fakeAsync(() => { + const d: Date = new Date(0); + expect(d.getFullYear()).toBeLessThan(1971); + const d1: Date = new Date('December 17, 1995 03:24:00'); + expect(d1.getFullYear()).toEqual(1995); + const d2: Date = new Date(1995, 11, 17, 3, 24, 0); + expect(isNaN(d2.getTime())).toBeFalsy(); + expect(d2.getFullYear()).toEqual(1995); + d2.setFullYear(1985); + expect(d2.getFullYear()).toBe(1985); + expect(d2.getMonth()).toBe(11); + expect(d2.getDate()).toBe(17); + })); + + it('should get Date.UTC() correctly', fakeAsync(() => { + const utcDate = new Date(Date.UTC(96, 11, 1, 0, 0, 0)); + expect(utcDate.getFullYear()).toBe(1996); + })); + + it('should call Date.parse() correctly', fakeAsync(() => { + const unixTimeZero = Date.parse('01 Jan 1970 00:00:00 GMT'); + expect(unixTimeZero).toBe(0); + })); + }); }); describe('ProxyZone', () => {