hyper-spec 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +49 -0
- data/.rubocop.yml +107 -0
- data/CODE_OF_CONDUCT.md +49 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +311 -0
- data/LICENSE.txt +21 -0
- data/README.md +378 -0
- data/Rakefile +5 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/hyper-spec.gemspec +72 -0
- data/lib/bin/firebug-2.0.13-fx.xpi +0 -0
- data/lib/hyper-spec.rb +88 -0
- data/lib/hyper-spec/component_test_helpers.rb +306 -0
- data/lib/hyper-spec/lolex.rb +66 -0
- data/lib/hyper-spec/rails/engine.rb +8 -0
- data/lib/hyper-spec/time_cop.rb +44 -0
- data/lib/hyper-spec/version.rb +3 -0
- data/lib/hyper-spec/wait_for_ajax.rb +30 -0
- data/lib/react/isomorphic_helpers.rb +7 -0
- data/lib/react/top_level_rails_component.rb +104 -0
- data/lib/selenium/web_driver/firefox/profile.rb +55 -0
- data/vendor/assets/javascripts/lolex.js +658 -0
- data/vendor/assets/javascripts/time_cop.rb +190 -0
- metadata +478 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'timecop'
|
2
|
+
|
3
|
+
module HyperSpec
|
4
|
+
class Timecop
|
5
|
+
private
|
6
|
+
|
7
|
+
def travel(mock_type, *args, &block)
|
8
|
+
raise SafeModeException if Timecop.safe_mode? && !block_given?
|
9
|
+
|
10
|
+
stack_item = TimeStackItem.new(mock_type, *args)
|
11
|
+
|
12
|
+
stack_backup = @_stack.dup
|
13
|
+
@_stack << stack_item
|
14
|
+
|
15
|
+
Lolex.push(mock_type, *args)
|
16
|
+
|
17
|
+
if block_given?
|
18
|
+
begin
|
19
|
+
yield stack_item.time
|
20
|
+
ensure
|
21
|
+
Lolex.pop
|
22
|
+
@_stack.replace stack_backup
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def return(&block)
|
28
|
+
current_stack = @_stack
|
29
|
+
current_baseline = @baseline
|
30
|
+
unmock!
|
31
|
+
yield
|
32
|
+
ensure
|
33
|
+
Lolex.restore
|
34
|
+
@_stack = current_stack
|
35
|
+
@baseline = current_baseline
|
36
|
+
end
|
37
|
+
|
38
|
+
def unmock! #:nodoc:
|
39
|
+
@baseline = nil
|
40
|
+
@_stack = []
|
41
|
+
Lolex.unmock
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module HyperSpec
|
2
|
+
module WaitForAjax
|
3
|
+
def wait_for_ajax
|
4
|
+
Timeout.timeout(Capybara.default_max_wait_time) do
|
5
|
+
loop do
|
6
|
+
sleep 0.25
|
7
|
+
break if finished_all_ajax_requests?
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def running?
|
13
|
+
result = page.evaluate_script('(function(active) { return active; })(jQuery.active)')
|
14
|
+
result && !result.zero?
|
15
|
+
rescue Exception => e
|
16
|
+
puts "wait_for_ajax failed while testing state of jQuery.active: #{e}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def finished_all_ajax_requests?
|
20
|
+
unless running?
|
21
|
+
sleep 0.25 # this was 1 second, not sure if its necessary to be so long...
|
22
|
+
!running?
|
23
|
+
end
|
24
|
+
rescue Capybara::NotSupportedByDriverError
|
25
|
+
true
|
26
|
+
rescue Exception => e
|
27
|
+
e.message == 'jQuery is not defined'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module React
|
2
|
+
class TopLevelRailsComponent
|
3
|
+
class << self
|
4
|
+
attr_accessor :event_history
|
5
|
+
|
6
|
+
def callback_history_for(proc_name)
|
7
|
+
event_history[proc_name]
|
8
|
+
end
|
9
|
+
|
10
|
+
def last_callback_for(proc_name)
|
11
|
+
event_history[proc_name].last
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear_callback_history_for(proc_name)
|
15
|
+
event_history[proc_name] = []
|
16
|
+
end
|
17
|
+
|
18
|
+
def event_history_for(event_name)
|
19
|
+
event_history["_on#{event_name.event_camelize}"]
|
20
|
+
end
|
21
|
+
|
22
|
+
def last_event_for(event_name)
|
23
|
+
event_history["_on#{event_name.event_camelize}"].last
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear_event_history_for(event_name)
|
27
|
+
event_history["_on#{event_name.event_camelize}"] = []
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def component
|
32
|
+
return @component if @component
|
33
|
+
paths_searched = []
|
34
|
+
|
35
|
+
if params.component_name.start_with?('::')
|
36
|
+
paths_searched << params.component_name.gsub(/^\:\:/, '')
|
37
|
+
|
38
|
+
@component =
|
39
|
+
begin
|
40
|
+
params.component_name.gsub(/^\:\:/, '').split('::')
|
41
|
+
.inject(Module) { |scope, next_const| scope.const_get(next_const, false) }
|
42
|
+
rescue
|
43
|
+
nil
|
44
|
+
end
|
45
|
+
|
46
|
+
return @component if @component && @component.method_defined?(:render)
|
47
|
+
else
|
48
|
+
self.class.search_path.each do |path|
|
49
|
+
# try each path + params.controller + params.component_name
|
50
|
+
paths_searched << "#{path.name + '::' unless path == Module}"\
|
51
|
+
"#{params.controller}::#{params.component_name}"
|
52
|
+
|
53
|
+
@component =
|
54
|
+
begin
|
55
|
+
[params.controller, params.component_name]
|
56
|
+
.join('::').split('::')
|
57
|
+
.inject(path) { |scope, next_const| scope.const_get(next_const, false) }
|
58
|
+
rescue
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
|
62
|
+
return @component if @component && @component.method_defined?(:render)
|
63
|
+
end
|
64
|
+
|
65
|
+
self.class.search_path.each do |path|
|
66
|
+
# then try each path + params.component_name
|
67
|
+
paths_searched << "#{path.name + '::' unless path == Module}#{params.component_name}"
|
68
|
+
@component =
|
69
|
+
begin
|
70
|
+
params.component_name.to_s.split('::')
|
71
|
+
.inject(path) { |scope, next_const| scope.const_get(next_const, false) }
|
72
|
+
rescue
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
return @component if @component && @component.method_defined?(:render)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
@component = nil
|
81
|
+
|
82
|
+
raise "Could not find component class '#{params.component_name}' "\
|
83
|
+
"for params.controller '#{params.controller}' in any component directory. "\
|
84
|
+
"Tried [#{paths_searched.join(', ')}]"
|
85
|
+
end
|
86
|
+
|
87
|
+
before_mount do
|
88
|
+
TopLevelRailsComponent.event_history = Hash.new { |h, k| h[k] = [] }
|
89
|
+
@render_params = params.render_params
|
90
|
+
component.validator.rules.each do |name, rules|
|
91
|
+
next unless rules[:type] == Proc
|
92
|
+
|
93
|
+
TopLevelRailsComponent.event_history[name] = []
|
94
|
+
@render_params[name] = lambda do |*args|
|
95
|
+
TopLevelRailsComponent.event_history[name] << args.collect { |arg| Native(arg).to_n }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def render
|
101
|
+
present component, @render_params
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Selenium
|
2
|
+
module WebDriver
|
3
|
+
module Firefox
|
4
|
+
class Profile
|
5
|
+
class << self
|
6
|
+
attr_accessor :firebug_version
|
7
|
+
|
8
|
+
def firebug_version
|
9
|
+
@firebug_version ||= '2.0.13-fx'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def frame_position
|
14
|
+
@frame_position ||= 'detached'
|
15
|
+
end
|
16
|
+
|
17
|
+
def frame_position=(position)
|
18
|
+
@frame_position = %w(left right top detached).detect do |side|
|
19
|
+
position && position[0].downcase == side[0]
|
20
|
+
end || 'detached'
|
21
|
+
end
|
22
|
+
|
23
|
+
def enable_firebug(version = nil)
|
24
|
+
version ||= Selenium::WebDriver::Firefox::Profile.firebug_version
|
25
|
+
add_extension(File.expand_path("../../../../bin/firebug-#{version}.xpi", __FILE__))
|
26
|
+
|
27
|
+
# For some reason, Firebug seems to trigger the Firefox plugin check
|
28
|
+
# (navigating to https://www.mozilla.org/en-US/plugincheck/ at startup).
|
29
|
+
# This prevents it. See http://code.google.com/p/selenium/issues/detail?id=4619.
|
30
|
+
self['extensions.blocklist.enabled'] = false
|
31
|
+
|
32
|
+
# Prevent "Welcome!" tab
|
33
|
+
self['extensions.firebug.showFirstRunPage'] = false
|
34
|
+
|
35
|
+
# Enable for all sites.
|
36
|
+
self['extensions.firebug.allPagesActivation'] = 'on'
|
37
|
+
|
38
|
+
# Enable all features.
|
39
|
+
%w(console net script).each do |feature|
|
40
|
+
self["extensions.firebug.#{feature}.enableSites"] = true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Closed by default, will open detached.
|
44
|
+
self['extensions.firebug.framePosition'] = frame_position
|
45
|
+
self['extensions.firebug.previousPlacement'] = 3
|
46
|
+
self['extensions.firebug.defaultPanelName'] = 'console'
|
47
|
+
|
48
|
+
# Disable native "Inspect Element" menu item.
|
49
|
+
self['devtools.inspector.enabled'] = false
|
50
|
+
self['extensions.firebug.hideDefaultInspector'] = true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,658 @@
|
|
1
|
+
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.lolex = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
|
2
|
+
(function (global){
|
3
|
+
(function (global) {
|
4
|
+
"use strict";
|
5
|
+
|
6
|
+
var userAgent = global.navigator && global.navigator.userAgent;
|
7
|
+
var isRunningInIE = userAgent && userAgent.indexOf("MSIE ") > -1;
|
8
|
+
|
9
|
+
// Make properties writable in IE, as per
|
10
|
+
// http://www.adequatelygood.com/Replacing-setTimeout-Globally.html
|
11
|
+
if (isRunningInIE) {
|
12
|
+
global.setTimeout = global.setTimeout;
|
13
|
+
global.clearTimeout = global.clearTimeout;
|
14
|
+
global.setInterval = global.setInterval;
|
15
|
+
global.clearInterval = global.clearInterval;
|
16
|
+
global.Date = global.Date;
|
17
|
+
}
|
18
|
+
|
19
|
+
// setImmediate is not a standard function
|
20
|
+
// avoid adding the prop to the window object if not present
|
21
|
+
if (global.setImmediate !== undefined) {
|
22
|
+
global.setImmediate = global.setImmediate;
|
23
|
+
global.clearImmediate = global.clearImmediate;
|
24
|
+
}
|
25
|
+
|
26
|
+
// node expects setTimeout/setInterval to return a fn object w/ .ref()/.unref()
|
27
|
+
// browsers, a number.
|
28
|
+
// see https://github.com/cjohansen/Sinon.JS/pull/436
|
29
|
+
|
30
|
+
var NOOP = function () { return undefined; };
|
31
|
+
var timeoutResult = setTimeout(NOOP, 0);
|
32
|
+
var addTimerReturnsObject = typeof timeoutResult === "object";
|
33
|
+
var hrtimePresent = (global.process && typeof global.process.hrtime === "function");
|
34
|
+
clearTimeout(timeoutResult);
|
35
|
+
|
36
|
+
var NativeDate = Date;
|
37
|
+
var uniqueTimerId = 1;
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
|
41
|
+
* number of milliseconds. This is used to support human-readable strings passed
|
42
|
+
* to clock.tick()
|
43
|
+
*/
|
44
|
+
function parseTime(str) {
|
45
|
+
if (!str) {
|
46
|
+
return 0;
|
47
|
+
}
|
48
|
+
|
49
|
+
var strings = str.split(":");
|
50
|
+
var l = strings.length, i = l;
|
51
|
+
var ms = 0, parsed;
|
52
|
+
|
53
|
+
if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
|
54
|
+
throw new Error("tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits");
|
55
|
+
}
|
56
|
+
|
57
|
+
while (i--) {
|
58
|
+
parsed = parseInt(strings[i], 10);
|
59
|
+
|
60
|
+
if (parsed >= 60) {
|
61
|
+
throw new Error("Invalid time " + str);
|
62
|
+
}
|
63
|
+
|
64
|
+
ms += parsed * Math.pow(60, (l - i - 1));
|
65
|
+
}
|
66
|
+
|
67
|
+
return ms * 1000;
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Floor function that also works for negative numbers
|
72
|
+
*/
|
73
|
+
function fixedFloor(n) {
|
74
|
+
return (n >= 0 ? Math.floor(n) : Math.ceil(n));
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* % operator that also works for negative numbers
|
79
|
+
*/
|
80
|
+
function fixedModulo(n, m) {
|
81
|
+
return ((n % m) + m) % m;
|
82
|
+
}
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Used to grok the `now` parameter to createClock.
|
86
|
+
*/
|
87
|
+
function getEpoch(epoch) {
|
88
|
+
if (!epoch) { return 0; }
|
89
|
+
if (typeof epoch.getTime === "function") { return epoch.getTime(); }
|
90
|
+
if (typeof epoch === "number") { return epoch; }
|
91
|
+
throw new TypeError("now should be milliseconds since UNIX epoch");
|
92
|
+
}
|
93
|
+
|
94
|
+
function inRange(from, to, timer) {
|
95
|
+
return timer && timer.callAt >= from && timer.callAt <= to;
|
96
|
+
}
|
97
|
+
|
98
|
+
function mirrorDateProperties(target, source) {
|
99
|
+
var prop;
|
100
|
+
for (prop in source) {
|
101
|
+
if (source.hasOwnProperty(prop)) {
|
102
|
+
target[prop] = source[prop];
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
// set special now implementation
|
107
|
+
if (source.now) {
|
108
|
+
target.now = function now() {
|
109
|
+
return target.clock.now;
|
110
|
+
};
|
111
|
+
} else {
|
112
|
+
delete target.now;
|
113
|
+
}
|
114
|
+
|
115
|
+
// set special toSource implementation
|
116
|
+
if (source.toSource) {
|
117
|
+
target.toSource = function toSource() {
|
118
|
+
return source.toSource();
|
119
|
+
};
|
120
|
+
} else {
|
121
|
+
delete target.toSource;
|
122
|
+
}
|
123
|
+
|
124
|
+
// set special toString implementation
|
125
|
+
target.toString = function toString() {
|
126
|
+
return source.toString();
|
127
|
+
};
|
128
|
+
|
129
|
+
target.prototype = source.prototype;
|
130
|
+
target.parse = source.parse;
|
131
|
+
target.UTC = source.UTC;
|
132
|
+
target.prototype.toUTCString = source.prototype.toUTCString;
|
133
|
+
|
134
|
+
return target;
|
135
|
+
}
|
136
|
+
|
137
|
+
function createDate() {
|
138
|
+
function ClockDate(year, month, date, hour, minute, second, ms) {
|
139
|
+
// Defensive and verbose to avoid potential harm in passing
|
140
|
+
// explicit undefined when user does not pass argument
|
141
|
+
switch (arguments.length) {
|
142
|
+
case 0:
|
143
|
+
return new NativeDate(ClockDate.clock.now);
|
144
|
+
case 1:
|
145
|
+
return new NativeDate(year);
|
146
|
+
case 2:
|
147
|
+
return new NativeDate(year, month);
|
148
|
+
case 3:
|
149
|
+
return new NativeDate(year, month, date);
|
150
|
+
case 4:
|
151
|
+
return new NativeDate(year, month, date, hour);
|
152
|
+
case 5:
|
153
|
+
return new NativeDate(year, month, date, hour, minute);
|
154
|
+
case 6:
|
155
|
+
return new NativeDate(year, month, date, hour, minute, second);
|
156
|
+
default:
|
157
|
+
return new NativeDate(year, month, date, hour, minute, second, ms);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
161
|
+
return mirrorDateProperties(ClockDate, NativeDate);
|
162
|
+
}
|
163
|
+
|
164
|
+
function addTimer(clock, timer) {
|
165
|
+
if (timer.func === undefined) {
|
166
|
+
throw new Error("Callback must be provided to timer calls");
|
167
|
+
}
|
168
|
+
|
169
|
+
if (!clock.timers) {
|
170
|
+
clock.timers = {};
|
171
|
+
}
|
172
|
+
|
173
|
+
timer.id = uniqueTimerId++;
|
174
|
+
timer.createdAt = clock.now;
|
175
|
+
timer.callAt = clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
|
176
|
+
|
177
|
+
clock.timers[timer.id] = timer;
|
178
|
+
|
179
|
+
if (addTimerReturnsObject) {
|
180
|
+
return {
|
181
|
+
id: timer.id,
|
182
|
+
ref: NOOP,
|
183
|
+
unref: NOOP
|
184
|
+
};
|
185
|
+
}
|
186
|
+
|
187
|
+
return timer.id;
|
188
|
+
}
|
189
|
+
|
190
|
+
|
191
|
+
/* eslint consistent-return: "off" */
|
192
|
+
function compareTimers(a, b) {
|
193
|
+
// Sort first by absolute timing
|
194
|
+
if (a.callAt < b.callAt) {
|
195
|
+
return -1;
|
196
|
+
}
|
197
|
+
if (a.callAt > b.callAt) {
|
198
|
+
return 1;
|
199
|
+
}
|
200
|
+
|
201
|
+
// Sort next by immediate, immediate timers take precedence
|
202
|
+
if (a.immediate && !b.immediate) {
|
203
|
+
return -1;
|
204
|
+
}
|
205
|
+
if (!a.immediate && b.immediate) {
|
206
|
+
return 1;
|
207
|
+
}
|
208
|
+
|
209
|
+
// Sort next by creation time, earlier-created timers take precedence
|
210
|
+
if (a.createdAt < b.createdAt) {
|
211
|
+
return -1;
|
212
|
+
}
|
213
|
+
if (a.createdAt > b.createdAt) {
|
214
|
+
return 1;
|
215
|
+
}
|
216
|
+
|
217
|
+
// Sort next by id, lower-id timers take precedence
|
218
|
+
if (a.id < b.id) {
|
219
|
+
return -1;
|
220
|
+
}
|
221
|
+
if (a.id > b.id) {
|
222
|
+
return 1;
|
223
|
+
}
|
224
|
+
|
225
|
+
// As timer ids are unique, no fallback `0` is necessary
|
226
|
+
}
|
227
|
+
|
228
|
+
function firstTimerInRange(clock, from, to) {
|
229
|
+
var timers = clock.timers,
|
230
|
+
timer = null,
|
231
|
+
id,
|
232
|
+
isInRange;
|
233
|
+
|
234
|
+
for (id in timers) {
|
235
|
+
if (timers.hasOwnProperty(id)) {
|
236
|
+
isInRange = inRange(from, to, timers[id]);
|
237
|
+
|
238
|
+
if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) {
|
239
|
+
timer = timers[id];
|
240
|
+
}
|
241
|
+
}
|
242
|
+
}
|
243
|
+
|
244
|
+
return timer;
|
245
|
+
}
|
246
|
+
|
247
|
+
function firstTimer(clock) {
|
248
|
+
var timers = clock.timers,
|
249
|
+
timer = null,
|
250
|
+
id;
|
251
|
+
|
252
|
+
for (id in timers) {
|
253
|
+
if (timers.hasOwnProperty(id)) {
|
254
|
+
if (!timer || compareTimers(timer, timers[id]) === 1) {
|
255
|
+
timer = timers[id];
|
256
|
+
}
|
257
|
+
}
|
258
|
+
}
|
259
|
+
|
260
|
+
return timer;
|
261
|
+
}
|
262
|
+
|
263
|
+
function lastTimer(clock) {
|
264
|
+
var timers = clock.timers,
|
265
|
+
timer = null,
|
266
|
+
id;
|
267
|
+
|
268
|
+
for (id in timers) {
|
269
|
+
if (timers.hasOwnProperty(id)) {
|
270
|
+
if (!timer || compareTimers(timer, timers[id]) === -1) {
|
271
|
+
timer = timers[id];
|
272
|
+
}
|
273
|
+
}
|
274
|
+
}
|
275
|
+
|
276
|
+
return timer;
|
277
|
+
}
|
278
|
+
|
279
|
+
function callTimer(clock, timer) {
|
280
|
+
var exception;
|
281
|
+
|
282
|
+
if (typeof timer.interval === "number") {
|
283
|
+
clock.timers[timer.id].callAt += timer.interval;
|
284
|
+
} else {
|
285
|
+
delete clock.timers[timer.id];
|
286
|
+
}
|
287
|
+
|
288
|
+
try {
|
289
|
+
if (typeof timer.func === "function") {
|
290
|
+
timer.func.apply(null, timer.args);
|
291
|
+
} else {
|
292
|
+
/* eslint no-eval: "off" */
|
293
|
+
eval(timer.func);
|
294
|
+
}
|
295
|
+
} catch (e) {
|
296
|
+
exception = e;
|
297
|
+
}
|
298
|
+
|
299
|
+
if (!clock.timers[timer.id]) {
|
300
|
+
if (exception) {
|
301
|
+
throw exception;
|
302
|
+
}
|
303
|
+
return;
|
304
|
+
}
|
305
|
+
|
306
|
+
if (exception) {
|
307
|
+
throw exception;
|
308
|
+
}
|
309
|
+
}
|
310
|
+
|
311
|
+
function timerType(timer) {
|
312
|
+
if (timer.immediate) {
|
313
|
+
return "Immediate";
|
314
|
+
}
|
315
|
+
if (timer.interval !== undefined) {
|
316
|
+
return "Interval";
|
317
|
+
}
|
318
|
+
return "Timeout";
|
319
|
+
}
|
320
|
+
|
321
|
+
function clearTimer(clock, timerId, ttype) {
|
322
|
+
if (!timerId) {
|
323
|
+
// null appears to be allowed in most browsers, and appears to be
|
324
|
+
// relied upon by some libraries, like Bootstrap carousel
|
325
|
+
return;
|
326
|
+
}
|
327
|
+
|
328
|
+
if (!clock.timers) {
|
329
|
+
clock.timers = [];
|
330
|
+
}
|
331
|
+
|
332
|
+
// in Node, timerId is an object with .ref()/.unref(), and
|
333
|
+
// its .id field is the actual timer id.
|
334
|
+
if (typeof timerId === "object") {
|
335
|
+
timerId = timerId.id;
|
336
|
+
}
|
337
|
+
|
338
|
+
if (clock.timers.hasOwnProperty(timerId)) {
|
339
|
+
// check that the ID matches a timer of the correct type
|
340
|
+
var timer = clock.timers[timerId];
|
341
|
+
if (timerType(timer) === ttype) {
|
342
|
+
delete clock.timers[timerId];
|
343
|
+
} else {
|
344
|
+
throw new Error("Cannot clear timer: timer created with set" + timerType(timer)
|
345
|
+
+ "() but cleared with clear" + ttype + "()");
|
346
|
+
}
|
347
|
+
}
|
348
|
+
}
|
349
|
+
|
350
|
+
function uninstall(clock, target) {
|
351
|
+
var method,
|
352
|
+
i,
|
353
|
+
l;
|
354
|
+
var installedHrTime = "_hrtime";
|
355
|
+
|
356
|
+
for (i = 0, l = clock.methods.length; i < l; i++) {
|
357
|
+
method = clock.methods[i];
|
358
|
+
if (method === "hrtime" && target.process) {
|
359
|
+
target.process.hrtime = clock[installedHrTime];
|
360
|
+
} else {
|
361
|
+
if (target[method] && target[method].hadOwnProperty) {
|
362
|
+
target[method] = clock["_" + method];
|
363
|
+
} else {
|
364
|
+
try {
|
365
|
+
delete target[method];
|
366
|
+
} catch (ignore) { /* eslint empty-block: "off" */ }
|
367
|
+
}
|
368
|
+
}
|
369
|
+
}
|
370
|
+
|
371
|
+
// Prevent multiple executions which will completely remove these props
|
372
|
+
clock.methods = [];
|
373
|
+
}
|
374
|
+
|
375
|
+
function hijackMethod(target, method, clock) {
|
376
|
+
var prop;
|
377
|
+
|
378
|
+
clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
|
379
|
+
clock["_" + method] = target[method];
|
380
|
+
|
381
|
+
if (method === "Date") {
|
382
|
+
var date = mirrorDateProperties(clock[method], target[method]);
|
383
|
+
target[method] = date;
|
384
|
+
} else {
|
385
|
+
target[method] = function () {
|
386
|
+
return clock[method].apply(clock, arguments);
|
387
|
+
};
|
388
|
+
|
389
|
+
for (prop in clock[method]) {
|
390
|
+
if (clock[method].hasOwnProperty(prop)) {
|
391
|
+
target[method][prop] = clock[method][prop];
|
392
|
+
}
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
target[method].clock = clock;
|
397
|
+
}
|
398
|
+
|
399
|
+
var timers = {
|
400
|
+
setTimeout: setTimeout,
|
401
|
+
clearTimeout: clearTimeout,
|
402
|
+
setImmediate: global.setImmediate,
|
403
|
+
clearImmediate: global.clearImmediate,
|
404
|
+
setInterval: setInterval,
|
405
|
+
clearInterval: clearInterval,
|
406
|
+
Date: Date
|
407
|
+
};
|
408
|
+
|
409
|
+
if (hrtimePresent) {
|
410
|
+
timers.hrtime = global.process.hrtime;
|
411
|
+
}
|
412
|
+
|
413
|
+
var keys = Object.keys || function (obj) {
|
414
|
+
var ks = [],
|
415
|
+
key;
|
416
|
+
|
417
|
+
for (key in obj) {
|
418
|
+
if (obj.hasOwnProperty(key)) {
|
419
|
+
ks.push(key);
|
420
|
+
}
|
421
|
+
}
|
422
|
+
|
423
|
+
return ks;
|
424
|
+
};
|
425
|
+
|
426
|
+
exports.timers = timers;
|
427
|
+
|
428
|
+
function createClock(now, loopLimit) {
|
429
|
+
loopLimit = loopLimit || 1000;
|
430
|
+
|
431
|
+
var clock = {
|
432
|
+
now: getEpoch(now),
|
433
|
+
hrNow: 0,
|
434
|
+
timeouts: {},
|
435
|
+
Date: createDate(),
|
436
|
+
loopLimit: loopLimit
|
437
|
+
};
|
438
|
+
|
439
|
+
clock.Date.clock = clock;
|
440
|
+
|
441
|
+
clock.setTimeout = function setTimeout(func, timeout) {
|
442
|
+
return addTimer(clock, {
|
443
|
+
func: func,
|
444
|
+
args: Array.prototype.slice.call(arguments, 2),
|
445
|
+
delay: timeout
|
446
|
+
});
|
447
|
+
};
|
448
|
+
|
449
|
+
clock.clearTimeout = function clearTimeout(timerId) {
|
450
|
+
return clearTimer(clock, timerId, "Timeout");
|
451
|
+
};
|
452
|
+
|
453
|
+
clock.setInterval = function setInterval(func, timeout) {
|
454
|
+
return addTimer(clock, {
|
455
|
+
func: func,
|
456
|
+
args: Array.prototype.slice.call(arguments, 2),
|
457
|
+
delay: timeout,
|
458
|
+
interval: timeout
|
459
|
+
});
|
460
|
+
};
|
461
|
+
|
462
|
+
clock.clearInterval = function clearInterval(timerId) {
|
463
|
+
return clearTimer(clock, timerId, "Interval");
|
464
|
+
};
|
465
|
+
|
466
|
+
clock.setImmediate = function setImmediate(func) {
|
467
|
+
return addTimer(clock, {
|
468
|
+
func: func,
|
469
|
+
args: Array.prototype.slice.call(arguments, 1),
|
470
|
+
immediate: true
|
471
|
+
});
|
472
|
+
};
|
473
|
+
|
474
|
+
clock.clearImmediate = function clearImmediate(timerId) {
|
475
|
+
return clearTimer(clock, timerId, "Immediate");
|
476
|
+
};
|
477
|
+
|
478
|
+
clock.tick = function tick(ms) {
|
479
|
+
ms = typeof ms === "number" ? ms : parseTime(ms);
|
480
|
+
var tickFrom = clock.now, tickTo = clock.now + ms, previous = clock.now;
|
481
|
+
var timer = firstTimerInRange(clock, tickFrom, tickTo);
|
482
|
+
var oldNow;
|
483
|
+
|
484
|
+
clock.duringTick = true;
|
485
|
+
|
486
|
+
function updateHrTime(newNow) {
|
487
|
+
clock.hrNow += (newNow - clock.now);
|
488
|
+
}
|
489
|
+
|
490
|
+
var firstException;
|
491
|
+
while (timer && tickFrom <= tickTo) {
|
492
|
+
if (clock.timers[timer.id]) {
|
493
|
+
updateHrTime(timer.callAt);
|
494
|
+
tickFrom = timer.callAt;
|
495
|
+
clock.now = timer.callAt;
|
496
|
+
try {
|
497
|
+
oldNow = clock.now;
|
498
|
+
callTimer(clock, timer);
|
499
|
+
// compensate for any setSystemTime() call during timer callback
|
500
|
+
if (oldNow !== clock.now) {
|
501
|
+
tickFrom += clock.now - oldNow;
|
502
|
+
tickTo += clock.now - oldNow;
|
503
|
+
previous += clock.now - oldNow;
|
504
|
+
}
|
505
|
+
} catch (e) {
|
506
|
+
firstException = firstException || e;
|
507
|
+
}
|
508
|
+
}
|
509
|
+
|
510
|
+
timer = firstTimerInRange(clock, previous, tickTo);
|
511
|
+
previous = tickFrom;
|
512
|
+
}
|
513
|
+
|
514
|
+
clock.duringTick = false;
|
515
|
+
updateHrTime(tickTo);
|
516
|
+
clock.now = tickTo;
|
517
|
+
|
518
|
+
if (firstException) {
|
519
|
+
throw firstException;
|
520
|
+
}
|
521
|
+
|
522
|
+
return clock.now;
|
523
|
+
};
|
524
|
+
|
525
|
+
clock.next = function next() {
|
526
|
+
var timer = firstTimer(clock);
|
527
|
+
if (!timer) {
|
528
|
+
return clock.now;
|
529
|
+
}
|
530
|
+
|
531
|
+
clock.duringTick = true;
|
532
|
+
try {
|
533
|
+
clock.now = timer.callAt;
|
534
|
+
callTimer(clock, timer);
|
535
|
+
return clock.now;
|
536
|
+
} finally {
|
537
|
+
clock.duringTick = false;
|
538
|
+
}
|
539
|
+
};
|
540
|
+
|
541
|
+
clock.runAll = function runAll() {
|
542
|
+
var numTimers, i;
|
543
|
+
for (i = 0; i < clock.loopLimit; i++) {
|
544
|
+
if (!clock.timers) {
|
545
|
+
return clock.now;
|
546
|
+
}
|
547
|
+
|
548
|
+
numTimers = Object.keys(clock.timers).length;
|
549
|
+
if (numTimers === 0) {
|
550
|
+
return clock.now;
|
551
|
+
}
|
552
|
+
|
553
|
+
clock.next();
|
554
|
+
}
|
555
|
+
|
556
|
+
throw new Error("Aborting after running " + clock.loopLimit + "timers, assuming an infinite loop!");
|
557
|
+
};
|
558
|
+
|
559
|
+
clock.runToLast = function runToLast() {
|
560
|
+
var timer = lastTimer(clock);
|
561
|
+
if (!timer) {
|
562
|
+
return clock.now;
|
563
|
+
}
|
564
|
+
|
565
|
+
return clock.tick(timer.callAt);
|
566
|
+
};
|
567
|
+
|
568
|
+
clock.reset = function reset() {
|
569
|
+
clock.timers = {};
|
570
|
+
};
|
571
|
+
|
572
|
+
clock.setSystemTime = function setSystemTime(now) {
|
573
|
+
// determine time difference
|
574
|
+
var newNow = getEpoch(now);
|
575
|
+
var difference = newNow - clock.now;
|
576
|
+
var id, timer;
|
577
|
+
|
578
|
+
// update 'system clock'
|
579
|
+
clock.now = newNow;
|
580
|
+
|
581
|
+
// update timers and intervals to keep them stable
|
582
|
+
for (id in clock.timers) {
|
583
|
+
if (clock.timers.hasOwnProperty(id)) {
|
584
|
+
timer = clock.timers[id];
|
585
|
+
timer.createdAt += difference;
|
586
|
+
timer.callAt += difference;
|
587
|
+
}
|
588
|
+
}
|
589
|
+
};
|
590
|
+
|
591
|
+
if (hrtimePresent) {
|
592
|
+
clock.hrtime = function (prev) {
|
593
|
+
if (Array.isArray(prev)) {
|
594
|
+
var oldSecs = (prev[0] + prev[1] / 1e9);
|
595
|
+
var newSecs = (clock.hrNow / 1000);
|
596
|
+
var difference = (newSecs - oldSecs);
|
597
|
+
var secs = fixedFloor(difference);
|
598
|
+
var nanosecs = fixedModulo(difference * 1e9, 1e9);
|
599
|
+
return [
|
600
|
+
secs,
|
601
|
+
nanosecs
|
602
|
+
];
|
603
|
+
}
|
604
|
+
return [
|
605
|
+
fixedFloor(clock.hrNow / 1000),
|
606
|
+
fixedModulo(clock.hrNow * 1e6, 1e9)
|
607
|
+
];
|
608
|
+
};
|
609
|
+
}
|
610
|
+
|
611
|
+
return clock;
|
612
|
+
}
|
613
|
+
exports.createClock = createClock;
|
614
|
+
|
615
|
+
exports.install = function install(target, now, toFake, loopLimit) {
|
616
|
+
var i,
|
617
|
+
l;
|
618
|
+
|
619
|
+
if (typeof target === "number") {
|
620
|
+
toFake = now;
|
621
|
+
now = target;
|
622
|
+
target = null;
|
623
|
+
}
|
624
|
+
|
625
|
+
if (!target) {
|
626
|
+
target = global;
|
627
|
+
}
|
628
|
+
|
629
|
+
var clock = createClock(now, loopLimit);
|
630
|
+
|
631
|
+
clock.uninstall = function () {
|
632
|
+
uninstall(clock, target);
|
633
|
+
};
|
634
|
+
|
635
|
+
clock.methods = toFake || [];
|
636
|
+
|
637
|
+
if (clock.methods.length === 0) {
|
638
|
+
clock.methods = keys(timers);
|
639
|
+
}
|
640
|
+
|
641
|
+
for (i = 0, l = clock.methods.length; i < l; i++) {
|
642
|
+
if (clock.methods[i] === "hrtime") {
|
643
|
+
if (target.process && typeof target.process.hrtime === "function") {
|
644
|
+
hijackMethod(target.process, clock.methods[i], clock);
|
645
|
+
}
|
646
|
+
} else {
|
647
|
+
hijackMethod(target, clock.methods[i], clock);
|
648
|
+
}
|
649
|
+
}
|
650
|
+
|
651
|
+
return clock;
|
652
|
+
};
|
653
|
+
|
654
|
+
}(global || this));
|
655
|
+
|
656
|
+
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
|
657
|
+
},{}]},{},[1])(1)
|
658
|
+
});
|