diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index b6ccec5d..0595ab05 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -356,4 +356,73 @@ describe("Clock (acceptance)", function() { expect(delayedFn1).toHaveBeenCalled(); expect(delayedFn2).toHaveBeenCalled(); }); + + it("does not mock the Date object when installing without parameters", function() { + var delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), + global = {Date: Date}, + mockDate = new j$.MockDate(global), + clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate); + + clock.install(); + + expect(global.Date).toEqual(Date); + + var now = global.Date.now(); + + clock.tick(50); + + expect(global.Date.now() - now).not.toEqual(50); + }); + + it("mocks the Date object and sets it to current time when installing with true parameter", function() { + var delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), + global = {Date: Date}, + mockDate = new j$.MockDate(global), + clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate); + + clock.install(true); + + var now = global.Date.now(); + + clock.tick(50); + + expect(global.Date.now() - now).toEqual(50); + + var timeoutDate = 0; + clock.setTimeout(function() { + timeoutDate = global.Date.now(); + }, 100); + + clock.tick(100); + + expect(timeoutDate - now).toEqual(150); + }); + + it("mocks the Date object and sets it to a given time when installing with a Date parameter", function() { + var delayedFunctionScheduler = new j$.DelayedFunctionScheduler(), + global = {Date: Date}, + mockDate = new j$.MockDate(global), + clock = new j$.Clock({setTimeout: setTimeout}, delayedFunctionScheduler, mockDate), + baseTime = new Date(2013, 9, 23); + + + clock.install(baseTime); + + var now = global.Date.now(); + + expect(now).toEqual(baseTime.getTime()); + + clock.tick(50); + + expect(global.Date.now()).toEqual(baseTime.getTime() + 50); + + var timeoutDate = 0; + clock.setTimeout(function() { + timeoutDate = global.Date.now(); + }, 100); + + clock.tick(100); + + expect(timeoutDate).toEqual(baseTime.getTime() + 150); + }); }); diff --git a/spec/core/MockDateSpec.js b/spec/core/MockDateSpec.js new file mode 100644 index 00000000..05bf86f9 --- /dev/null +++ b/spec/core/MockDateSpec.js @@ -0,0 +1,139 @@ +describe("FakeDate", function() { + it("does not fail if no global date is found", function() { + var fakeGlobal = {}, + mockDate = new j$.MockDate(fakeGlobal); + + mockDate.install(); + + fakeGlobal.Date = jasmine.createSpy("Date"); + + mockDate.tick(0); + + expect(fakeGlobal.Date).not.toHaveBeenCalled(); + }); + + it("does not replace global Date if it is not installed", function() { + var fakeDate = jasmine.createSpy("global Date"), + fakeGlobal = { Date: fakeDate }, + mockDate = new j$.MockDate(fakeGlobal); + + fakeDate.now = function(){}; + + expect(fakeDate).toEqual(fakeGlobal.Date); + mockDate.install(); + + expect(fakeDate).not.toEqual(fakeGlobal.Date); + }); + + it("replaces the global Date on uninstall", function() { + var fakeDate = jasmine.createSpy("global Date"), + fakeGlobal = { Date: fakeDate }, + mockDate = new j$.MockDate(fakeGlobal); + + fakeDate.now = function(){}; + + mockDate.install(); + mockDate.uninstall(); + + expect(fakeDate).toEqual(fakeGlobal.Date); + + }); + + it("takes the current time as the base when installing without parameters", function() { + var fakeDate = jasmine.createSpy("global Date"), + fakeGlobal = { Date: fakeDate }, + mockDate = new j$.MockDate(fakeGlobal); + + fakeGlobal.Date.prototype.getTime = function() { + return 1000; + }; + fakeDate.now = function(){ return 1000; }; + + mockDate.install(); + + expect(new fakeGlobal.Date().getTime()).toEqual(1000); + }); + + it("can accept a date as time base when installing", function() { + var fakeGlobal = { Date: Date }, + mockDate = new j$.MockDate(fakeGlobal), + baseDate = new Date(2013, 9, 23); + + mockDate.install(baseDate); + + expect(new fakeGlobal.Date().getTime()).toEqual(baseDate.getTime()); + }); + + + it("fakes current time when using Date.now()", function() { + var fakeGlobal = { Date: Date }, + mockDate = new j$.MockDate(fakeGlobal), + baseDate = new Date(2013, 9, 23); + + mockDate.install(baseDate); + + expect(fakeGlobal.Date.now()).toEqual(baseDate.getTime()); + }); + + it("makes time passes using tick", function() { + var fakeDate = jasmine.createSpy("global Date"), + fakeGlobal = { Date: fakeDate }, + mockDate = new j$.MockDate(fakeGlobal); + + fakeDate.now = function(){ return 1000; }; + + mockDate.install(); + + mockDate.tick(100); + + expect(fakeGlobal.Date.now()).toEqual(1100); + + mockDate.tick(1000); + + expect(fakeGlobal.Date.now()).toEqual(2100); + }); + + it("allows to increase 0 milliseconds using tick", function() { + var fakeDate = jasmine.createSpy("global Date"), + fakeGlobal = { Date: fakeDate }, + mockDate = new j$.MockDate(fakeGlobal); + + fakeDate.now = function(){ return 1000; }; + + mockDate.install(); + + mockDate.tick(0); + expect(fakeGlobal.Date.now()).toEqual(1000); + + mockDate.tick(); + expect(fakeGlobal.Date.now()).toEqual(1000); + }); + + it("allows to create a Date in a different time than now", function() { + var fakeGlobal = { Date: Date }, + mockDate = new j$.MockDate(fakeGlobal), + baseDate = new Date(2013, 9, 23, 0, 0, 0, 0); + + mockDate.install(baseDate); + + var otherDate = new fakeGlobal.Date(2013, 9, 23, 0, 0, 1, 0); + + mockDate.tick(1000); + + expect(fakeGlobal.Date.now()).toEqual(otherDate.getTime()); + }); + + it("copies all Date properties to the mocked date", function() { + var fakeGlobal = { Date: Date }, + mockDate = new j$.MockDate(fakeGlobal), + baseDate = new Date(2013, 9, 23, 0, 0, 0, 0); + + mockDate.install(baseDate); + + var otherDate = new fakeGlobal.Date(); + + expect(otherDate).toEqual(jasmine.any(Date)); + + expect(fakeGlobal.Date.UTC(2013, 9, 23)).toEqual(Date.UTC(2013, 9, 23)); + }); +}); diff --git a/src/core/Clock.js b/src/core/Clock.js index 69832506..c05f2a97 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -1,5 +1,5 @@ getJasmineRequireObj().Clock = function() { - function Clock(global, delayedFunctionScheduler) { + function Clock(global, delayedFunctionScheduler, date) { var self = this, realTimingFunctions = { setTimeout: global.setTimeout, @@ -16,15 +16,24 @@ getJasmineRequireObj().Clock = function() { installed = false, timer; - self.install = function() { + + self.install = function(mockDate) { replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; installed = true; + + if (date && mockDate) { + date.install(mockDate); + } }; self.uninstall = function() { delayedFunctionScheduler.reset(); + if (date) { + date.uninstall(); + } replace(global, realTimingFunctions); + timer = realTimingFunctions; installed = false; }; @@ -59,6 +68,9 @@ getJasmineRequireObj().Clock = function() { self.tick = function(millis) { if (installed) { + if (date) { + date.tick(millis); + } delayedFunctionScheduler.tick(millis); } else { throw new Error('Mock clock is not installed, use jasmine.clock().install()'); diff --git a/src/core/Env.js b/src/core/Env.js index 1312cb2c..fc2fd27f 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -11,7 +11,7 @@ getJasmineRequireObj().Env = function(j$) { var realSetTimeout = j$.getGlobal().setTimeout; var realClearTimeout = j$.getGlobal().clearTimeout; - this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler()); + this.clock = new j$.Clock(global, new j$.DelayedFunctionScheduler(), new j$.MockDate(global)); var runnableLookupTable = {}; diff --git a/src/core/MockDate.js b/src/core/MockDate.js new file mode 100644 index 00000000..294579bb --- /dev/null +++ b/src/core/MockDate.js @@ -0,0 +1,62 @@ +getJasmineRequireObj().MockDate = function() { + function MockDate(global) { + var self = this; + var currentTime = 0; + + if (!global || !global.Date) { + self.install = function() {}; + self.tick = function() {}; + self.uninstall = function() {}; + return self; + } + + var GlobalDate = global.Date; + + self.install = function(mockDate) { + if (mockDate instanceof Date) { + currentTime = mockDate.getTime(); + } else { + currentTime = GlobalDate.now(); + } + + global.Date = FakeDate; + }; + + self.tick = function(millis) { + millis = millis || 0; + currentTime = currentTime + millis; + }; + + self.uninstall = function() { + currentTime = 0; + global.Date = GlobalDate; + }; + + createDateProperties(); + + return self; + + function FakeDate() { + if (arguments.length === 0) { + return new GlobalDate(currentTime); + } else { + return new GlobalDate(arguments[0], arguments[1], arguments[2], + arguments[3], arguments[4], arguments[5], arguments[6]); + } + } + + function createDateProperties() { + + FakeDate.now = function() { + return currentTime; + }; + + FakeDate.toSource = GlobalDate.toSource; + FakeDate.toString = GlobalDate.toString; + FakeDate.parse = GlobalDate.parse; + FakeDate.UTC = GlobalDate.UTC; + } + } + + return MockDate; +}; diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 5803a5bc..528489b6 100644 --- a/src/core/requireCore.js +++ b/src/core/requireCore.js @@ -14,6 +14,7 @@ getJasmineRequireObj().core = function(jRequire) { j$.util = jRequire.util(); j$.Any = jRequire.Any(); j$.CallTracker = jRequire.CallTracker(); + j$.MockDate = jRequire.MockDate(); j$.Clock = jRequire.Clock(); j$.DelayedFunctionScheduler = jRequire.DelayedFunctionScheduler(); j$.Env = jRequire.Env(j$);