From fb8bede8ea9b2d64e83333cea4722906837ffd73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20L=C3=B3pez=20Pardo?= Date: Fri, 18 Oct 2013 15:47:34 +0200 Subject: [PATCH 1/3] Add FakeDate object --- src/core/Clock.js | 26 ++++++++++++++++-- src/core/Env.js | 2 +- src/core/MockDate.js | 59 +++++++++++++++++++++++++++++++++++++++++ src/core/requireCore.js | 1 + 4 files changed, 85 insertions(+), 3 deletions(-) create mode 100644 src/core/MockDate.js diff --git a/src/core/Clock.js b/src/core/Clock.js index 188a45e3..96304ed2 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, @@ -15,15 +15,34 @@ getJasmineRequireObj().Clock = function() { }, installed = false; - self.install = function() { + if (date) { + var realDate = { + Date: global.Date + }, + fakeDate = { + Date: date.Date + }; + } + + self.install = function(mockDate) { replace(global, fakeTimingFunctions); timer = fakeTimingFunctions; installed = true; + + if (date && mockDate) { + date.install(mockDate); + replace(global, fakeDate); + } }; self.uninstall = function() { delayedFunctionScheduler.reset(); + if (date) { + date.reset(); + replace(global, realDate); + } replace(global, realTimingFunctions); + timer = realTimingFunctions; installed = false; }; @@ -58,6 +77,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.getEnv().clock.install()"); diff --git a/src/core/Env.js b/src/core/Env.js index 5c1fa813..58de0609 100644 --- a/src/core/Env.js +++ b/src/core/Env.js @@ -9,7 +9,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..db17e006 --- /dev/null +++ b/src/core/MockDate.js @@ -0,0 +1,59 @@ +getJasmineRequireObj().MockDate = function() { + function MockDate(global) { + var self = this; + var currentTime = 0; + + if (!global || !global.Date) { + self.install = function() {}; + self.tick = function() {}; + self.reset = function() {}; + return self; + } + + self.install = function(mockDate) { + if (mockDate instanceof Date) { + currentTime = mockDate.getTime(); + } else { + currentTime = global.Date.now(); + } + }; + + self.tick = function(millis) { + millis = millis || 0; + currentTime = currentTime + millis; + }; + + self.reset = function() { + currentTime = 0; + }; + + self.Date = FakeDate; + + createDateProperties(); + + return self; + + function FakeDate() { + if (arguments.length === 0) { + return new global.Date(currentTime); + } else { + return global.Date.apply(this, arguments); + } + } + + function createDateProperties() { + FakeDate.prototype = global.Date.prototype; + + FakeDate.now = function() { + return currentTime; + } + + FakeDate.toSource = global.Date.toSource; + FakeDate.toString = global.Date.toString; + FakeDate.parse = global.Date.parse; + FakeDate.UTC = global.Date.UTC; + } + } + + return MockDate; +}; diff --git a/src/core/requireCore.js b/src/core/requireCore.js index 15b949ea..b33f7296 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$); From 81b822fea9ec14d345ee0165759d37c0c2eea8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20L=C3=B3pez=20Pardo?= Date: Fri, 25 Oct 2013 14:41:32 +0200 Subject: [PATCH 2/3] Add specs for mock date --- spec/core/MockDateSpec.js | 139 ++++++++++++++++++++++++++++++++++++++ src/core/Clock.js | 12 +--- src/core/MockDate.js | 31 +++++---- 3 files changed, 157 insertions(+), 25 deletions(-) create mode 100644 spec/core/MockDateSpec.js 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 96304ed2..7c8472d4 100644 --- a/src/core/Clock.js +++ b/src/core/Clock.js @@ -15,14 +15,6 @@ getJasmineRequireObj().Clock = function() { }, installed = false; - if (date) { - var realDate = { - Date: global.Date - }, - fakeDate = { - Date: date.Date - }; - } self.install = function(mockDate) { replace(global, fakeTimingFunctions); @@ -31,15 +23,13 @@ getJasmineRequireObj().Clock = function() { if (date && mockDate) { date.install(mockDate); - replace(global, fakeDate); } }; self.uninstall = function() { delayedFunctionScheduler.reset(); if (date) { - date.reset(); - replace(global, realDate); + date.uninstall(); } replace(global, realTimingFunctions); diff --git a/src/core/MockDate.js b/src/core/MockDate.js index db17e006..294579bb 100644 --- a/src/core/MockDate.js +++ b/src/core/MockDate.js @@ -6,16 +6,20 @@ getJasmineRequireObj().MockDate = function() { if (!global || !global.Date) { self.install = function() {}; self.tick = function() {}; - self.reset = function() {}; + self.uninstall = function() {}; return self; } + var GlobalDate = global.Date; + self.install = function(mockDate) { if (mockDate instanceof Date) { currentTime = mockDate.getTime(); } else { - currentTime = global.Date.now(); + currentTime = GlobalDate.now(); } + + global.Date = FakeDate; }; self.tick = function(millis) { @@ -23,35 +27,34 @@ getJasmineRequireObj().MockDate = function() { currentTime = currentTime + millis; }; - self.reset = function() { + self.uninstall = function() { currentTime = 0; + global.Date = GlobalDate; }; - self.Date = FakeDate; - createDateProperties(); return self; function FakeDate() { if (arguments.length === 0) { - return new global.Date(currentTime); + return new GlobalDate(currentTime); } else { - return global.Date.apply(this, arguments); + return new GlobalDate(arguments[0], arguments[1], arguments[2], + arguments[3], arguments[4], arguments[5], arguments[6]); } - } + } function createDateProperties() { - FakeDate.prototype = global.Date.prototype; FakeDate.now = function() { return currentTime; - } + }; - FakeDate.toSource = global.Date.toSource; - FakeDate.toString = global.Date.toString; - FakeDate.parse = global.Date.parse; - FakeDate.UTC = global.Date.UTC; + FakeDate.toSource = GlobalDate.toSource; + FakeDate.toString = GlobalDate.toString; + FakeDate.parse = GlobalDate.parse; + FakeDate.UTC = GlobalDate.UTC; } } From 3186b24a664eecfd1ed75c11f2ec5b2991314af5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20L=C3=B3pez=20Pardo?= Date: Fri, 25 Oct 2013 14:59:07 +0200 Subject: [PATCH 3/3] add acceptance tests for mock clock with date --- spec/core/ClockSpec.js | 69 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/spec/core/ClockSpec.js b/spec/core/ClockSpec.js index c49a25b7..a742044f 100644 --- a/spec/core/ClockSpec.js +++ b/spec/core/ClockSpec.js @@ -361,4 +361,73 @@ describe("Clock (acceptance)", function() { clock.clearInterval(123) }).not.toThrow(); }); + + 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); + }); });