Re-add Mock Clock behavior as global 'clock'

- Use clock.install, clock.tick...
- Add unit coverage.
- Fixes old bug in function scheduler
This commit is contained in:
Davis W. Frank & Rajan Agaskar
2012-12-06 18:22:46 -08:00
parent a1011e7748
commit 74f928fd54
19 changed files with 1074 additions and 464 deletions

296
spec/core/ClockSpec.js Normal file
View File

@@ -0,0 +1,296 @@
describe("Clock", function() {
// TODO: fullName/SpecFilter is broken, so don't nest describes you want to filter
it("calls the global setTimeout directly if Clock is not installed", function() {
var setTimeout = jasmine.createSpy('setTimeout'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction']),
global = { setTimeout: setTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.setTimeout(delayedFn, 0);
expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(delayedFn, 0);
});
it("schedules the delayed function with the fake timer", function() {
var setTimeout = jasmine.createSpy('setTimeout'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = {scheduleFunction: scheduleFunction},
global = { setTimeout: setTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.install();
clock.setTimeout(delayedFn, 0, 'a', 'b');
expect(setTimeout).not.toHaveBeenCalled();
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b']);
});
it("returns an id for the delayed function", function() {
var setTimeout = jasmine.createSpy('setTimeout'),
scheduleId = 123,
scheduleFunction = jasmine.createSpy('scheduleFunction').andReturn(scheduleId),
delayedFunctionScheduler = {scheduleFunction: scheduleFunction},
global = { setTimeout: setTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler),
timeoutId;
clock.install();
timeoutId = clock.setTimeout(delayedFn, 0);
expect(timeoutId).toEqual(123);
});
it("calls the global clearTimeout directly if Clock is not installed", function() {
var clearTimeout = jasmine.createSpy('clearTimeout'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['clearTimeout']),
global = { clearTimeout: clearTimeout },
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.clearTimeout(123);
expect(clearTimeout).toHaveBeenCalledWith(123);
});
it("clears the scheduled function with the scheduler", function() {
var clearTimeout = jasmine.createSpy('clearTimeout'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['removeFunctionWithId']),
global = { setTimeout: clearTimeout },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.install();
clock.clearTimeout(123);
expect(clearTimeout).not.toHaveBeenCalled();
expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(123);
});
it("calls the global setInterval directly if Clock is not installed", function() {
var setInterval = jasmine.createSpy('setInterval'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction']),
global = { setInterval: setInterval },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.setInterval(delayedFn, 0);
expect(delayedFunctionScheduler.scheduleFunction).not.toHaveBeenCalled();
expect(setInterval).toHaveBeenCalledWith(delayedFn, 0);
});
it("schedules the delayed function with the fake timer", function() {
var setInterval = jasmine.createSpy('setInterval'),
scheduleFunction = jasmine.createSpy('scheduleFunction'),
delayedFunctionScheduler = {scheduleFunction: scheduleFunction},
global = { setInterval: setInterval },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.install();
clock.setInterval(delayedFn, 0, 'a', 'b');
expect(setInterval).not.toHaveBeenCalled();
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(delayedFn, 0, ['a', 'b'], true);
});
it("returns an id for the delayed function", function() {
var setInterval = jasmine.createSpy('setInterval'),
scheduleId = 123,
scheduleFunction = jasmine.createSpy('scheduleFunction').andReturn(scheduleId),
delayedFunctionScheduler = {scheduleFunction: scheduleFunction},
global = { setInterval: setInterval },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler),
intervalId;
clock.install();
intervalId = clock.setInterval(delayedFn, 0);
expect(intervalId).toEqual(123);
});
it("calls the global clearInterval directly if Clock is not installed", function() {
var clearInterval = jasmine.createSpy('clearInterval'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['clearInterval']),
global = { clearInterval: clearInterval },
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.clearInterval(123);
expect(clearInterval).toHaveBeenCalledWith(123);
});
it("clears the scheduled function with the scheduler", function() {
var clearInterval = jasmine.createSpy('clearInterval'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['removeFunctionWithId']),
global = { setInterval: clearInterval },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.install();
clock.clearInterval(123);
expect(clearInterval).not.toHaveBeenCalled();
expect(delayedFunctionScheduler.removeFunctionWithId).toHaveBeenCalledWith(123);
});
it("gives you a friendly reminder if the Clock is not installed and you tick", function() {
var clock = new jasmine.Clock({}, jasmine.createSpyObj('delayedFunctionScheduler', ['tick']));
expect(function() {
clock.tick(50);
}).toThrow();
});
it("can be uninstalled", function() {
var setTimeout = jasmine.createSpy('setTimeout'),
setInterval = jasmine.createSpy('setInterval'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction', 'tick', 'reset']),
global = { setTimeout: setTimeout, setInterval: setInterval },
delayedFn = jasmine.createSpy('delayedFn'),
clock = new jasmine.Clock(global, delayedFunctionScheduler);
clock.install();
clock.setTimeout(delayedFn, 0);
expect(setTimeout).not.toHaveBeenCalled();
clock.setInterval(delayedFn, 0);
expect(setInterval).not.toHaveBeenCalled();
expect(function() {
clock.tick(0);
}).not.toThrow();
clock.uninstall();
expect(delayedFunctionScheduler.reset).toHaveBeenCalled();
clock.setTimeout(delayedFn, 0);
expect(setTimeout).toHaveBeenCalled();
clock.setInterval(delayedFn, 0);
expect(setInterval).toHaveBeenCalled();
expect(function() {
clock.tick(0);
}).toThrow();
});
it("on IE < 9, fails if extra args are passed to fake clock", function() {
//fail, because this would break in IE9.
var setTimeout = jasmine.createSpy('setTimeout'),
setInterval = jasmine.createSpy('setInterval'),
delayedFunctionScheduler = jasmine.createSpyObj('delayedFunctionScheduler', ['scheduleFunction']),
fn = jasmine.createSpy('fn'),
global = { setTimeout: setTimeout, setInterval: setInterval },
clock = new jasmine.Clock(global, delayedFunctionScheduler);
setTimeout.apply = null;
setInterval.apply = null;
clock.install();
clock.setTimeout(fn, 0);
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, []);
expect(function() {
clock.setTimeout(fn, 0, 'extra');
}).toThrow();
clock.setInterval(fn, 0);
expect(delayedFunctionScheduler.scheduleFunction).toHaveBeenCalledWith(fn, 0, [], true);
expect(function() {
clock.setInterval(fn, 0, 'extra');
}).toThrow();
});
});
describe("Clock (acceptance)", function() {
it("can run setTimeouts/setIntervals synchronously", function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFn2 = jasmine.createSpy('delayedFn2'),
delayedFn3 = jasmine.createSpy('delayedFn3'),
recurring1 = jasmine.createSpy('recurring1'),
delayedFunctionScheduler = new jasmine.DelayedFunctionScheduler(),
clock = new jasmine.Clock({setTimeout: setTimeout}, delayedFunctionScheduler);
clock.install();
clock.setTimeout(delayedFn1, 0, 'some', 'arg');
var intervalId = clock.setInterval(recurring1, 50, 'some', 'other', 'args');
clock.setTimeout(delayedFn2, 100);
clock.setTimeout(delayedFn3, 200);
expect(delayedFn1).not.toHaveBeenCalled();
expect(delayedFn2).not.toHaveBeenCalled();
expect(delayedFn3).not.toHaveBeenCalled();
clock.tick(0);
expect(delayedFn1).toHaveBeenCalledWith('some', 'arg');
expect(delayedFn2).not.toHaveBeenCalled();
expect(delayedFn3).not.toHaveBeenCalled();
clock.tick(50);
expect(recurring1).toHaveBeenCalledWith('some', 'other', 'args');
expect(recurring1.callCount).toBe(1);
expect(delayedFn2).not.toHaveBeenCalled();
expect(delayedFn3).not.toHaveBeenCalled();
clock.tick(50);
expect(recurring1.callCount).toBe(2);
expect(delayedFn2).toHaveBeenCalled();
expect(delayedFn3).not.toHaveBeenCalled();
clock.tick(100);
expect(recurring1.callCount).toBe(4);
expect(delayedFn3).toHaveBeenCalled();
clock.clearInterval(intervalId);
clock.tick(50);
expect(recurring1.callCount).toBe(4);
});
it("can clear a previously set timeout", function() {
var clearedFn = jasmine.createSpy('clearedFn'),
delayedFunctionScheduler = new jasmine.DelayedFunctionScheduler(),
clock = new jasmine.Clock({setTimeout: function() {}}, delayedFunctionScheduler),
timeoutId;
clock.install();
timeoutId = clock.setTimeout(clearedFn, 100);
expect(clearedFn).not.toHaveBeenCalled();
clock.clearTimeout(timeoutId);
clock.tick(100);
expect(clearedFn).not.toHaveBeenCalled();
});
it("correctly schedules functions after the Clock has advanced", function() {
var delayedFn1 = jasmine.createSpy('delayedFn1'),
delayedFunctionScheduler = new jasmine.DelayedFunctionScheduler(),
clock = new jasmine.Clock({setTimeout: function(){}}, delayedFunctionScheduler);
clock.install();
clock.tick(100);
clock.setTimeout(delayedFn1, 10, ['some', 'arg']);
clock.tick(5);
expect(delayedFn1).not.toHaveBeenCalled();
clock.tick(5);
expect(delayedFn1).toHaveBeenCalled();
});
});

View File

@@ -0,0 +1,142 @@
describe("DelayedFunctionScheduler", function() {
it("schedules a function for later execution", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0);
expect(fn).not.toHaveBeenCalled();
scheduler.tick(0);
expect(fn).toHaveBeenCalled();
});
it("optionally passes params to scheduled functions", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0, ['foo', 'bar']);
expect(fn).not.toHaveBeenCalled();
scheduler.tick(0);
expect(fn).toHaveBeenCalledWith('foo', 'bar');
});
it("scheduled fns can optionally reoccur", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 20, [], true);
expect(fn).not.toHaveBeenCalled();
scheduler.tick(20);
expect(fn.callCount).toBe(1);
scheduler.tick(40);
expect(fn.callCount).toBe(3);
scheduler.tick(21);
expect(fn.callCount).toBe(4);
});
it("increments scheduled fns ids unless one is passed", function() {
var scheduler = new jasmine.DelayedFunctionScheduler();
expect(scheduler.scheduleFunction(function() {
}, 0)).toBe(1);
expect(scheduler.scheduleFunction(function() {
}, 0)).toBe(2);
expect(scheduler.scheduleFunction(function() {
}, 0, [], false, 123)).toBe(123);
expect(scheduler.scheduleFunction(function() {
}, 0)).toBe(3);
});
it("#removeFunctionWithId removes a previously scheduled function with a given id", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
timeoutKey;
timeoutKey = scheduler.scheduleFunction(fn, 0);
expect(fn).not.toHaveBeenCalled();
scheduler.removeFunctionWithId(timeoutKey);
scheduler.tick(0);
expect(fn).not.toHaveBeenCalled();
});
it("reset removes scheduled functions", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
scheduler.scheduleFunction(fn, 0);
expect(fn).not.toHaveBeenCalled();
scheduler.reset();
scheduler.tick(0);
expect(fn).not.toHaveBeenCalled();
});
it("reset resets the returned ids", function() {
var scheduler = new jasmine.DelayedFunctionScheduler();
expect(scheduler.scheduleFunction(function() { }, 0)).toBe(1);
expect(scheduler.scheduleFunction(function() { }, 0, [], false, 123)).toBe(123);
scheduler.reset();
expect(scheduler.scheduleFunction(function() { }, 0)).toBe(1);
expect(scheduler.scheduleFunction(function() { }, 0, [], false, 123)).toBe(123);
});
it("reset resets the current tick time", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn');
expect(fn).not.toHaveBeenCalled();
scheduler.tick(15);
scheduler.reset();
scheduler.scheduleFunction(fn, 20, [], false, 1, 20);
scheduler.tick(5);
expect(fn).not.toHaveBeenCalled();
});
it("executes recurring functions interleaved with regular functions in the correct order", function() {
var scheduler = new jasmine.DelayedFunctionScheduler(),
fn = jasmine.createSpy('fn'),
recurringCallCount = 0,
recurring = jasmine.createSpy('recurring').andCallFake(function() {
recurringCallCount++;
if (recurringCallCount < 5) {
expect(fn).not.toHaveBeenCalled();
}
});
scheduler.scheduleFunction(recurring, 10, [], true);
scheduler.scheduleFunction(fn, 50);
scheduler.tick(60);
expect(recurring).toHaveBeenCalled();
expect(recurring.callCount).toBe(6);
expect(fn).toHaveBeenCalled();
});
});

View File

@@ -157,3 +157,33 @@ describe("jasmine.Env", function() {
});
});
});
describe("jasmine Env", function() {
it("Mock clock can be installed and used in tests", function() {
var setTimeout = jasmine.createSpy('setTimeout'),
globalTimeoutFn = jasmine.createSpy('globalTimeoutFn'),
fakeTimeoutFn = jasmine.createSpy('fakeTimeoutFn'),
env = new jasmine.Env({global: { setTimeout: setTimeout }});
env.describe("tests", function() {
env.it("test with mock clock", function() {
env.clock.install();
env.clock.setTimeout(fakeTimeoutFn, 100);
env.clock.tick(100);
});
env.it("test without mock clock", function() {
env.clock.setTimeout(globalTimeoutFn, 100);
});
});
expect(setTimeout).not.toHaveBeenCalled();
expect(fakeTimeoutFn).not.toHaveBeenCalled();
env.execute();
expect(fakeTimeoutFn).toHaveBeenCalled();
expect(setTimeout).toHaveBeenCalledWith(globalTimeoutFn, 100);
});
});

View File

@@ -1,40 +0,0 @@
//// TODO: Disabling b/c this spec isn't testing what it thinks it is.
//// Make a proper unit and intergration tests for this object
//describe("MockClock", function () {
//
// beforeEach(function() {
// jasmine.Clock.useMock();
// });
//
// describe("setTimeout", function () {
// it("should mock the clock when useMock is in a beforeEach", function() {
// var expected = false;
// setTimeout(function() {
// expected = true;
// }, 30000);
// expect(expected).toBe(false);
// jasmine.Clock.tick(30001);
// expect(expected).toBe(true);
// });
// });
//
// describe("setInterval", function () {
// it("should mock the clock when useMock is in a beforeEach", function() {
// var interval = 0;
// setInterval(function() {
// interval++;
// }, 30000);
// expect(interval).toEqual(0);
// jasmine.Clock.tick(30001);
// expect(interval).toEqual(1);
// jasmine.Clock.tick(30001);
// expect(interval).toEqual(2);
// jasmine.Clock.tick(1);
// expect(interval).toEqual(2);
// });
// });
//
// it("shouldn't complain if you call jasmine.Clock.useMock() more than once", function() {
// jasmine.Clock.useMock();
// });
//});

View File

@@ -5,12 +5,6 @@ describe('RunnerTest', function() {
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
fakeTimer = new jasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
});
describe('beforeEach', function() {

View File

@@ -5,12 +5,6 @@ describe("jasmine spec running", function () {
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
fakeTimer = new originalJasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
});
it('should assign spec ids sequentially', function() {

View File

@@ -1,16 +1,9 @@
describe('Suite', function() {
var fakeTimer;
var env;
beforeEach(function() {
env = new jasmine.Env();
env.updateInterval = 0;
fakeTimer = new originalJasmine.FakeTimer();
env.setTimeout = fakeTimer.setTimeout;
env.clearTimeout = fakeTimer.clearTimeout;
env.setInterval = fakeTimer.setInterval;
env.clearInterval = fakeTimer.clearInterval;
});
describe('Specs', function () {
@@ -42,7 +35,7 @@ describe('Suite', function() {
});
});
});
it('#specs should return all immediate children that are specs.', function () {
var suiteSpecs = suite.specs();
expect(suiteSpecs.length).toEqual(3);