jspec.timers.js
3.55 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Mock Timers - Copyright TJ Holowaychuk <tj@vision-media.ca> (MIT Licensed)
;(function(){
/**
* Localized timer stack.
*/
var timers = [];
// nodejs, rhino don't have a window object
var global = this;
// if they where mocked before this library is loaded - bad luck
var savedGlobals = {
setTimeout: global.setTimeout,
setInterval: global.setInterval,
clearInterval: global.clearInterval,
clearTimeout: global.clearTimeout,
// those should not be globals, but are mocked none the less, so we save them
resetTimers: global.resetTimers,
tick: global.tick
};
var hadResetTimers = 'resetTimers' in global;
var hadTick = 'tick' in global;
function forEachProperty(anObject, aClosure) {
for (var key in anObject) {
if ( ! anObject.hasOwnProperty(key))
continue;
aClosure(key, anObject[key]);
}
}
global.MockTimers = {
mockTimersVersion: '2.0.0',
mockGlobalTimerFunctions: function() {
forEachProperty(this.mocks, function(aName, aFunction) {
global[aName] = aFunction;
});
},
unmockGlobalTimerFunctions: function() {
forEachProperty(this.savedGlobals, function(aName, aFunction) {
global[aName] = aFunction;
});
if ( ! hadResetTimers)
delete global['resetTimers'];
if ( ! hadTick)
delete global['tick'];
}
};
function clearTimer(id) {
return delete timers[--id];
}
var mocks = {
/**
* Set mock timeout with _callback_ and timeout of _ms_.
*
* @param {function} callback
* @param {int} ms
* @return {int}
* @api public
*/
setTimeout: function(callback, ms) {
var id;
return id = setInterval(function(){
callback();
clearInterval(id);
}, ms);
},
/**
* Set mock interval with _callback_ and interval of _ms_.
*
* @param {function} callback
* @param {int} ms
* @return {int}
* @api public
*/
setInterval: function(callback, ms) {
// REFACT: use wrapper object so callback is not changed -> state leak
callback.step = ms;
callback.current = callback.last = 0;
timers[timers.length] = callback;
return timers.length;
},
/**
* Destroy timer with _id_.
*
* @param {int} id
* @return {bool}
* @api public
*/
clearInterval: clearTimer,
clearTimeout: clearTimer
};
// additional functions that are not originally in the global namespace
/**
* Reset timers.
*
* @return {array}
* @api public
*/
mocks.resetTimers = function() {
return timers = [];
};
/**
* Increment each timers internal clock by _ms_.
*
* @param {int} ms
* @api public
*/
mocks.tick = function(ms) {
for (var i = 0, len = timers.length; i < len; ++i) {
if ( ! timers[i] || ! (timers[i].current += ms))
continue;
if (timers[i].current - timers[i].last < timers[i].step)
continue;
var times = Math.floor((timers[i].current - timers[i].last) / timers[i].step);
var remainder = (timers[i].current - timers[i].last) % timers[i].step;
timers[i].last = timers[i].current - remainder;
while (times-- && timers[i])
timers[i]();
}
};
// make them available publicly
MockTimers.mocks = mocks;
JSpec.include({
beforeSpec: function(){
MockTimers.mockGlobalTimerFunctions();
},
afterSpec : function() {
MockTimers.unmockGlobalTimerFunctions();
}
});
})();