hyper-spec 0.1.2 → 0.99.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.
@@ -1,6 +1,156 @@
1
- require 'timecop'
1
+ if RUBY_ENGINE == 'opal'
2
+ # Wrap the Lolex js package
3
+ class Lolex
4
+ class << self
2
5
 
3
- module HyperSpec
6
+ # to avoid forcing us to require time we make our own Time.parse method
7
+ # copied from opal.rb master branch
8
+
9
+ def parse_time(str)
10
+ `new Date(Date.parse(str))`
11
+ end
12
+
13
+ def stack
14
+ @stack ||= []
15
+ end
16
+
17
+ def push(time, scale = 1, resolution = 10)
18
+ time = parse_time(time) if time.is_a? String
19
+ stack << [Time.now, @scale, @resolution]
20
+ update_lolex(time, scale, resolution)
21
+ end
22
+
23
+ def pop
24
+ update_lolex(*stack.pop) unless stack.empty?
25
+ end
26
+
27
+ def unmock(time, resolution)
28
+ push(time, 1, resolution)
29
+ @backup_stack = stack
30
+ @stack = []
31
+ end
32
+
33
+ def restore
34
+ @stack = @backup_stack
35
+ pop
36
+ end
37
+
38
+ def tick
39
+ real_clock = `(new #{@lolex}['_Date']).getTime()`
40
+ mock_clock = Time.now.to_f * 1000
41
+ real_elapsed_time = real_clock - @real_start_time
42
+ mock_elapsed_time = mock_clock - @mock_start_time
43
+
44
+ ticks = real_elapsed_time * @scale - mock_elapsed_time
45
+
46
+ `#{@lolex}.tick(#{ticks.to_i})`
47
+ nil
48
+ end
49
+
50
+ def create_ticker
51
+ return unless @scale && @scale > 0
52
+ ticker = %x{
53
+ #{@lolex}['_setInterval'].call(
54
+ window,
55
+ function() { #{tick} },
56
+ #{@resolution}
57
+ )
58
+ }
59
+ ticker
60
+ end
61
+
62
+ def update_lolex(time, scale, resolution)
63
+ `#{@lolex}.uninstall()` && return if scale.nil?
64
+ @mock_start_time = time.to_f * 1000
65
+
66
+ if @lolex
67
+ `#{@lolex}['_clearInterval'].call(window, #{@ticker})` if @ticker
68
+ @real_start_time = `(new #{@lolex}['_Date']).getTime()`
69
+ `#{@lolex}.tick(#{@mock_start_time - Time.now.to_f * 1000})`
70
+ else
71
+ @real_start_time = Time.now.to_f * 1000
72
+ @lolex = `lolex.install({ now: #{@mock_start_time} })`
73
+ end
74
+
75
+ @scale = scale
76
+ @resolution = resolution
77
+ @ticker = create_ticker
78
+ nil # must return nil otherwise we try to return a timer to server!
79
+ end
80
+ end
81
+ end
82
+
83
+ else
84
+ require 'timecop'
85
+
86
+ # Interface to the Lolex package running on the client side
87
+ # Below we will monkey patch Timecop to call these methods
88
+ class Lolex
89
+ class << self
90
+ def init(page, client_time_zone, resolution)
91
+ @capybara_page = page
92
+ @resolution = resolution || 10
93
+ @client_time_zone = client_time_zone
94
+ run_pending_evaluations
95
+ @initialized = true
96
+ end
97
+
98
+ def initialized?
99
+ @initialized
100
+ end
101
+
102
+ def push(mock_type, *args)
103
+ scale = if mock_type == :freeze
104
+ 0
105
+ elsif mock_type == :scale
106
+ args[0]
107
+ else
108
+ 1
109
+ end
110
+ evaluate_ruby do
111
+ "Lolex.push('#{time_string_in_zone}', #{scale}, #{@resolution})"
112
+ end
113
+ end
114
+
115
+ def pop
116
+ evaluate_ruby { 'Lolex.pop' }
117
+ end
118
+
119
+ def unmock
120
+ evaluate_ruby { "Lolex.unmock('#{time_string_in_zone}', #{@resolution})" }
121
+ end
122
+
123
+ def restore
124
+ evaluate_ruby { 'Lolex.restore' }
125
+ end
126
+
127
+ private
128
+
129
+ def time_string_in_zone
130
+ Time.now.in_time_zone(@client_time_zone).strftime('%Y/%m/%d %H:%M:%S %z')
131
+ end
132
+
133
+ def pending_evaluations
134
+ @pending_evaluations ||= []
135
+ end
136
+
137
+ def evaluate_ruby(&block)
138
+ if @capybara_page
139
+ @capybara_page.evaluate_ruby(yield)
140
+ else
141
+ pending_evaluations << block
142
+ end
143
+ end
144
+
145
+ def run_pending_evaluations
146
+ return if pending_evaluations.empty?
147
+ @capybara_page.evaluate_ruby(pending_evaluations.collect(&:call).join("\n"))
148
+ @pending_evaluations ||= []
149
+ end
150
+ end
151
+ end
152
+
153
+ # Monkey patches to call our Lolex interface
4
154
  class Timecop
5
155
  private
6
156
 
@@ -1,3 +1,3 @@
1
1
  module HyperSpec
2
- VERSION = '0.1.2'
2
+ VERSION = '0.99.0'
3
3
  end
@@ -10,21 +10,36 @@ module HyperSpec
10
10
  end
11
11
 
12
12
  def running?
13
- result = page.evaluate_script('(function(active) { return active; })(jQuery.active)')
14
- result && !result.zero?
13
+ jscode = <<-CODE
14
+ (function() {
15
+ if (typeof Opal !== "undefined" && Opal.Hyperloop !== undefined) {
16
+ try {
17
+ return Opal.Hyperloop.$const_get("HTTP")["$active?"]();
18
+ } catch(err) {
19
+ if (typeof jQuery !== "undefined" && jQuery.active !== undefined) {
20
+ return (jQuery.active > 0);
21
+ } else {
22
+ return false;
23
+ }
24
+ }
25
+ } else if (typeof jQuery !== "undefined" && jQuery.active !== undefined) {
26
+ return (jQuery.active > 0);
27
+ } else {
28
+ return false;
29
+ }
30
+ })();
31
+ CODE
32
+ page.evaluate_script(jscode)
15
33
  rescue Exception => e
16
- puts "wait_for_ajax failed while testing state of jQuery.active: #{e}"
34
+ puts "wait_for_ajax failed while testing state of ajax requests: #{e}"
17
35
  end
18
36
 
19
37
  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
38
+ !running?
24
39
  rescue Capybara::NotSupportedByDriverError
25
40
  true
26
41
  rescue Exception => e
27
- e.message == 'jQuery is not defined'
42
+ e.message == 'either jQuery or Hyperloop::HTTP is not defined'
28
43
  end
29
44
  end
30
45
  end
@@ -31,57 +31,47 @@ module React
31
31
  def component
32
32
  return @component if @component
33
33
  paths_searched = []
34
-
34
+ component = nil
35
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)
36
+ # if absolute path of component is given, look it up and fail if not found
37
+ paths_searched << params.component_name
38
+ component = begin
39
+ Object.const_get(params.component_name)
40
+ rescue NameError
41
+ nil
42
+ end
47
43
  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)
44
+ # if relative path is given, look it up like this
45
+ # 1) we check each path + controller-name + component-name
46
+ # 2) if we can't find it there we check each path + component-name
47
+ # if we can't find it we just try const_get
48
+ # so (assuming controller name is Home)
49
+ # ::Foo::Bar will only resolve to some component named ::Foo::Bar
50
+ # but Foo::Bar will check (in this order) ::Home::Foo::Bar, ::Components::Home::Foo::Bar, ::Foo::Bar, ::Components::Foo::Bar
51
+ self.class.search_path.each do |scope|
52
+ paths_searched << "#{scope.name}::#{params.controller}::#{params.component_name}"
53
+ component = begin
54
+ scope.const_get(params.controller, false).const_get(params.component_name, false)
55
+ rescue NameError
56
+ nil
57
+ end
58
+ break if component != nil
63
59
  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)
60
+ unless component
61
+ self.class.search_path.each do |scope|
62
+ paths_searched << "#{scope.name}::#{params.component_name}"
63
+ component = begin
64
+ scope.const_get(params.component_name, false)
65
+ rescue NameError
66
+ nil
67
+ end
68
+ break if component != nil
69
+ end
77
70
  end
78
71
  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(', ')}]"
72
+ @component = component
73
+ return @component if @component && @component.method_defined?(:render)
74
+ raise "Could not find component class '#{params.component_name}' for params.controller '#{params.controller}' in any component directory. Tried [#{paths_searched.join(", ")}]"
85
75
  end
86
76
 
87
77
  before_mount do
@@ -98,7 +88,7 @@ module React
98
88
  end
99
89
 
100
90
  def render
101
- present component, @render_params
91
+ React::RenderingContext.render(component, @render_params)
102
92
  end
103
93
  end
104
94
  end
@@ -6,7 +6,7 @@ module Selenium
6
6
  attr_accessor :firebug_version
7
7
 
8
8
  def firebug_version
9
- @firebug_version ||= '2.0.13-fx'
9
+ @firebug_version ||= '2.0.19-fx'
10
10
  end
11
11
  end
12
12
 
@@ -15,7 +15,7 @@ module Selenium
15
15
  end
16
16
 
17
17
  def frame_position=(position)
18
- @frame_position = %w(left right top detached).detect do |side|
18
+ @frame_position = %w[left right top detached].detect do |side|
19
19
  position && position[0].downcase == side[0]
20
20
  end || 'detached'
21
21
  end
@@ -36,7 +36,7 @@ module Selenium
36
36
  self['extensions.firebug.allPagesActivation'] = 'on'
37
37
 
38
38
  # Enable all features.
39
- %w(console net script).each do |feature|
39
+ %w[console net script].each do |feature|
40
40
  self["extensions.firebug.#{feature}.enableSites"] = true
41
41
  end
42
42
 
@@ -0,0 +1,733 @@
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
+ "use strict";
4
+
5
+ var userAgent = global.navigator && global.navigator.userAgent;
6
+ var isRunningInIE = userAgent && userAgent.indexOf("MSIE ") > -1;
7
+ var maxTimeout = Math.pow(2, 31) - 1; //see https://heycam.github.io/webidl/#abstract-opdef-converttoint
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
+ var nextTickPresent = (global.process && typeof global.process.nextTick === "function");
35
+ var performancePresent = (global.performance && typeof global.performance.now === "function");
36
+
37
+ clearTimeout(timeoutResult);
38
+
39
+ var NativeDate = Date;
40
+ var uniqueTimerId = 1;
41
+
42
+ /**
43
+ * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into
44
+ * number of milliseconds. This is used to support human-readable strings passed
45
+ * to clock.tick()
46
+ */
47
+ function parseTime(str) {
48
+ if (!str) {
49
+ return 0;
50
+ }
51
+
52
+ var strings = str.split(":");
53
+ var l = strings.length;
54
+ var i = l;
55
+ var ms = 0;
56
+ var parsed;
57
+
58
+ if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) {
59
+ throw new Error("tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits");
60
+ }
61
+
62
+ while (i--) {
63
+ parsed = parseInt(strings[i], 10);
64
+
65
+ if (parsed >= 60) {
66
+ throw new Error("Invalid time " + str);
67
+ }
68
+
69
+ ms += parsed * Math.pow(60, (l - i - 1));
70
+ }
71
+
72
+ return ms * 1000;
73
+ }
74
+
75
+ /**
76
+ * Floor function that also works for negative numbers
77
+ */
78
+ function fixedFloor(n) {
79
+ return (n >= 0 ? Math.floor(n) : Math.ceil(n));
80
+ }
81
+
82
+ /**
83
+ * % operator that also works for negative numbers
84
+ */
85
+ function fixedModulo(n, m) {
86
+ return ((n % m) + m) % m;
87
+ }
88
+
89
+ /**
90
+ * Used to grok the `now` parameter to createClock.
91
+ * @param epoch {Date|number} the system time
92
+ */
93
+ function getEpoch(epoch) {
94
+ if (!epoch) { return 0; }
95
+ if (typeof epoch.getTime === "function") { return epoch.getTime(); }
96
+ if (typeof epoch === "number") { return epoch; }
97
+ throw new TypeError("now should be milliseconds since UNIX epoch");
98
+ }
99
+
100
+ function inRange(from, to, timer) {
101
+ return timer && timer.callAt >= from && timer.callAt <= to;
102
+ }
103
+
104
+ function mirrorDateProperties(target, source) {
105
+ var prop;
106
+ for (prop in source) {
107
+ if (source.hasOwnProperty(prop)) {
108
+ target[prop] = source[prop];
109
+ }
110
+ }
111
+
112
+ // set special now implementation
113
+ if (source.now) {
114
+ target.now = function now() {
115
+ return target.clock.now;
116
+ };
117
+ } else {
118
+ delete target.now;
119
+ }
120
+
121
+ // set special toSource implementation
122
+ if (source.toSource) {
123
+ target.toSource = function toSource() {
124
+ return source.toSource();
125
+ };
126
+ } else {
127
+ delete target.toSource;
128
+ }
129
+
130
+ // set special toString implementation
131
+ target.toString = function toString() {
132
+ return source.toString();
133
+ };
134
+
135
+ target.prototype = source.prototype;
136
+ target.parse = source.parse;
137
+ target.UTC = source.UTC;
138
+ target.prototype.toUTCString = source.prototype.toUTCString;
139
+
140
+ return target;
141
+ }
142
+
143
+ function createDate() {
144
+ function ClockDate(year, month, date, hour, minute, second, ms) {
145
+ // Defensive and verbose to avoid potential harm in passing
146
+ // explicit undefined when user does not pass argument
147
+ switch (arguments.length) {
148
+ case 0:
149
+ return new NativeDate(ClockDate.clock.now);
150
+ case 1:
151
+ return new NativeDate(year);
152
+ case 2:
153
+ return new NativeDate(year, month);
154
+ case 3:
155
+ return new NativeDate(year, month, date);
156
+ case 4:
157
+ return new NativeDate(year, month, date, hour);
158
+ case 5:
159
+ return new NativeDate(year, month, date, hour, minute);
160
+ case 6:
161
+ return new NativeDate(year, month, date, hour, minute, second);
162
+ default:
163
+ return new NativeDate(year, month, date, hour, minute, second, ms);
164
+ }
165
+ }
166
+
167
+ return mirrorDateProperties(ClockDate, NativeDate);
168
+ }
169
+
170
+
171
+ function enqueueJob(clock, job) {
172
+ // enqueues a microtick-deferred task - ecma262/#sec-enqueuejob
173
+ if (!clock.jobs) {
174
+ clock.jobs = [];
175
+ }
176
+ clock.jobs.push(job);
177
+ }
178
+
179
+ function runJobs(clock) {
180
+ // runs all microtick-deferred tasks - ecma262/#sec-runjobs
181
+ if (!clock.jobs) {
182
+ return;
183
+ }
184
+ for (var i = 0; i < clock.jobs.length; i++) {
185
+ var job = clock.jobs[i];
186
+ job.func.apply(null, job.args);
187
+ }
188
+ clock.jobs = [];
189
+ }
190
+
191
+ function addTimer(clock, timer) {
192
+ if (timer.func === undefined) {
193
+ throw new Error("Callback must be provided to timer calls");
194
+ }
195
+
196
+ if (timer.hasOwnProperty("delay")) {
197
+ timer.delay = timer.delay > maxTimeout ? 1 : timer.delay;
198
+ }
199
+
200
+ if (timer.hasOwnProperty("interval")) {
201
+ timer.interval = timer.interval > maxTimeout ? 1 : timer.interval;
202
+ }
203
+
204
+ if (!clock.timers) {
205
+ clock.timers = {};
206
+ }
207
+
208
+ timer.id = uniqueTimerId++;
209
+ timer.createdAt = clock.now;
210
+ timer.callAt = clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0));
211
+
212
+ clock.timers[timer.id] = timer;
213
+
214
+ if (addTimerReturnsObject) {
215
+ return {
216
+ id: timer.id,
217
+ ref: NOOP,
218
+ unref: NOOP
219
+ };
220
+ }
221
+
222
+ return timer.id;
223
+ }
224
+
225
+
226
+ /* eslint consistent-return: "off" */
227
+ function compareTimers(a, b) {
228
+ // Sort first by absolute timing
229
+ if (a.callAt < b.callAt) {
230
+ return -1;
231
+ }
232
+ if (a.callAt > b.callAt) {
233
+ return 1;
234
+ }
235
+
236
+ // Sort next by immediate, immediate timers take precedence
237
+ if (a.immediate && !b.immediate) {
238
+ return -1;
239
+ }
240
+ if (!a.immediate && b.immediate) {
241
+ return 1;
242
+ }
243
+
244
+ // Sort next by creation time, earlier-created timers take precedence
245
+ if (a.createdAt < b.createdAt) {
246
+ return -1;
247
+ }
248
+ if (a.createdAt > b.createdAt) {
249
+ return 1;
250
+ }
251
+
252
+ // Sort next by id, lower-id timers take precedence
253
+ if (a.id < b.id) {
254
+ return -1;
255
+ }
256
+ if (a.id > b.id) {
257
+ return 1;
258
+ }
259
+
260
+ // As timer ids are unique, no fallback `0` is necessary
261
+ }
262
+
263
+ function firstTimerInRange(clock, from, to) {
264
+ var timers = clock.timers;
265
+ var timer = null;
266
+ var id, isInRange;
267
+
268
+ for (id in timers) {
269
+ if (timers.hasOwnProperty(id)) {
270
+ isInRange = inRange(from, to, timers[id]);
271
+
272
+ if (isInRange && (!timer || compareTimers(timer, timers[id]) === 1)) {
273
+ timer = timers[id];
274
+ }
275
+ }
276
+ }
277
+
278
+ return timer;
279
+ }
280
+
281
+ function firstTimer(clock) {
282
+ var timers = clock.timers;
283
+ var timer = null;
284
+ var id;
285
+
286
+ for (id in timers) {
287
+ if (timers.hasOwnProperty(id)) {
288
+ if (!timer || compareTimers(timer, timers[id]) === 1) {
289
+ timer = timers[id];
290
+ }
291
+ }
292
+ }
293
+
294
+ return timer;
295
+ }
296
+
297
+ function lastTimer(clock) {
298
+ var timers = clock.timers;
299
+ var timer = null;
300
+ var id;
301
+
302
+ for (id in timers) {
303
+ if (timers.hasOwnProperty(id)) {
304
+ if (!timer || compareTimers(timer, timers[id]) === -1) {
305
+ timer = timers[id];
306
+ }
307
+ }
308
+ }
309
+
310
+ return timer;
311
+ }
312
+
313
+ function callTimer(clock, timer) {
314
+ if (typeof timer.interval === "number") {
315
+ clock.timers[timer.id].callAt += timer.interval;
316
+ } else {
317
+ delete clock.timers[timer.id];
318
+ }
319
+
320
+ if (typeof timer.func === "function") {
321
+ timer.func.apply(null, timer.args);
322
+ } else {
323
+ /* eslint no-eval: "off" */
324
+ eval(timer.func);
325
+ }
326
+ }
327
+
328
+ function timerType(timer) {
329
+ if (timer.immediate) {
330
+ return "Immediate";
331
+ }
332
+ if (timer.interval !== undefined) {
333
+ return "Interval";
334
+ }
335
+ return "Timeout";
336
+ }
337
+
338
+ function clearTimer(clock, timerId, ttype) {
339
+ if (!timerId) {
340
+ // null appears to be allowed in most browsers, and appears to be
341
+ // relied upon by some libraries, like Bootstrap carousel
342
+ return;
343
+ }
344
+
345
+ if (!clock.timers) {
346
+ clock.timers = [];
347
+ }
348
+
349
+ // in Node, timerId is an object with .ref()/.unref(), and
350
+ // its .id field is the actual timer id.
351
+ if (typeof timerId === "object") {
352
+ timerId = timerId.id;
353
+ }
354
+
355
+ if (clock.timers.hasOwnProperty(timerId)) {
356
+ // check that the ID matches a timer of the correct type
357
+ var timer = clock.timers[timerId];
358
+ if (timerType(timer) === ttype) {
359
+ delete clock.timers[timerId];
360
+ } else {
361
+ throw new Error("Cannot clear timer: timer created with set" + timerType(timer)
362
+ + "() but cleared with clear" + ttype + "()");
363
+ }
364
+ }
365
+ }
366
+
367
+ function uninstall(clock, target, config) {
368
+ var method,
369
+ i,
370
+ l;
371
+ var installedHrTime = "_hrtime";
372
+ var installedNextTick = "_nextTick";
373
+
374
+ for (i = 0, l = clock.methods.length; i < l; i++) {
375
+ method = clock.methods[i];
376
+ if (method === "hrtime" && target.process) {
377
+ target.process.hrtime = clock[installedHrTime];
378
+ } else if (method === "nextTick" && target.process) {
379
+ target.process.nextTick = clock[installedNextTick];
380
+ } else {
381
+ if (target[method] && target[method].hadOwnProperty) {
382
+ target[method] = clock["_" + method];
383
+ if (method === "clearInterval" && config.shouldAdvanceTime === true) {
384
+ target[method](clock.attachedInterval);
385
+ }
386
+ } else {
387
+ try {
388
+ delete target[method];
389
+ } catch (ignore) { /* eslint empty-block: "off" */ }
390
+ }
391
+ }
392
+ }
393
+
394
+ // Prevent multiple executions which will completely remove these props
395
+ clock.methods = [];
396
+ }
397
+
398
+ function hijackMethod(target, method, clock) {
399
+ var prop;
400
+ clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call(target, method);
401
+ clock["_" + method] = target[method];
402
+
403
+ if (method === "Date") {
404
+ var date = mirrorDateProperties(clock[method], target[method]);
405
+ target[method] = date;
406
+ } else {
407
+ target[method] = function () {
408
+ return clock[method].apply(clock, arguments);
409
+ };
410
+
411
+ for (prop in clock[method]) {
412
+ if (clock[method].hasOwnProperty(prop)) {
413
+ target[method][prop] = clock[method][prop];
414
+ }
415
+ }
416
+ }
417
+
418
+ target[method].clock = clock;
419
+ }
420
+
421
+ function doIntervalTick(clock, advanceTimeDelta) {
422
+ clock.tick(advanceTimeDelta);
423
+ }
424
+
425
+ var timers = {
426
+ setTimeout: setTimeout,
427
+ clearTimeout: clearTimeout,
428
+ setImmediate: global.setImmediate,
429
+ clearImmediate: global.clearImmediate,
430
+ setInterval: setInterval,
431
+ clearInterval: clearInterval,
432
+ Date: Date
433
+ };
434
+
435
+ if (hrtimePresent) {
436
+ timers.hrtime = global.process.hrtime;
437
+ }
438
+
439
+ if (nextTickPresent) {
440
+ timers.nextTick = global.process.nextTick;
441
+ }
442
+
443
+ if (performancePresent) {
444
+ timers.performance = global.performance;
445
+ }
446
+
447
+ var keys = Object.keys || function (obj) {
448
+ var ks = [];
449
+ var key;
450
+
451
+ for (key in obj) {
452
+ if (obj.hasOwnProperty(key)) {
453
+ ks.push(key);
454
+ }
455
+ }
456
+
457
+ return ks;
458
+ };
459
+
460
+ exports.timers = timers;
461
+
462
+ /**
463
+ * @param now {Date|number} the system time
464
+ * @param loopLimit {number} maximum number of timers that will be run when calling runAll()
465
+ */
466
+ function createClock(now, loopLimit) {
467
+ loopLimit = loopLimit || 1000;
468
+
469
+ var clock = {
470
+ now: getEpoch(now),
471
+ hrNow: 0,
472
+ timeouts: {},
473
+ Date: createDate(),
474
+ loopLimit: loopLimit
475
+ };
476
+
477
+ clock.Date.clock = clock;
478
+
479
+ clock.setTimeout = function setTimeout(func, timeout) {
480
+ return addTimer(clock, {
481
+ func: func,
482
+ args: Array.prototype.slice.call(arguments, 2),
483
+ delay: timeout
484
+ });
485
+ };
486
+
487
+ clock.clearTimeout = function clearTimeout(timerId) {
488
+ return clearTimer(clock, timerId, "Timeout");
489
+ };
490
+ clock.nextTick = function nextTick(func) {
491
+ return enqueueJob(clock, {
492
+ func: func,
493
+ args: Array.prototype.slice.call(arguments, 1)
494
+ });
495
+ };
496
+ clock.setInterval = function setInterval(func, timeout) {
497
+ return addTimer(clock, {
498
+ func: func,
499
+ args: Array.prototype.slice.call(arguments, 2),
500
+ delay: timeout,
501
+ interval: timeout
502
+ });
503
+ };
504
+
505
+ clock.clearInterval = function clearInterval(timerId) {
506
+ return clearTimer(clock, timerId, "Interval");
507
+ };
508
+
509
+ clock.setImmediate = function setImmediate(func) {
510
+ return addTimer(clock, {
511
+ func: func,
512
+ args: Array.prototype.slice.call(arguments, 1),
513
+ immediate: true
514
+ });
515
+ };
516
+
517
+ clock.clearImmediate = function clearImmediate(timerId) {
518
+ return clearTimer(clock, timerId, "Immediate");
519
+ };
520
+
521
+ function updateHrTime(newNow) {
522
+ clock.hrNow += (newNow - clock.now);
523
+ }
524
+
525
+ clock.tick = function tick(ms) {
526
+ ms = typeof ms === "number" ? ms : parseTime(ms);
527
+ var tickFrom = clock.now;
528
+ var tickTo = clock.now + ms;
529
+ var previous = clock.now;
530
+ var timer = firstTimerInRange(clock, tickFrom, tickTo);
531
+ var oldNow, firstException;
532
+
533
+ clock.duringTick = true;
534
+ runJobs(clock);
535
+
536
+ while (timer && tickFrom <= tickTo) {
537
+ if (clock.timers[timer.id]) {
538
+ updateHrTime(timer.callAt);
539
+ tickFrom = timer.callAt;
540
+ clock.now = timer.callAt;
541
+ try {
542
+ runJobs(clock);
543
+ oldNow = clock.now;
544
+ callTimer(clock, timer);
545
+ } catch (e) {
546
+ firstException = firstException || e;
547
+ }
548
+
549
+ // compensate for any setSystemTime() call during timer callback
550
+ if (oldNow !== clock.now) {
551
+ tickFrom += clock.now - oldNow;
552
+ tickTo += clock.now - oldNow;
553
+ previous += clock.now - oldNow;
554
+ }
555
+ }
556
+
557
+ timer = firstTimerInRange(clock, previous, tickTo);
558
+ previous = tickFrom;
559
+ }
560
+
561
+ runJobs(clock);
562
+ clock.duringTick = false;
563
+ updateHrTime(tickTo);
564
+ clock.now = tickTo;
565
+
566
+ if (firstException) {
567
+ throw firstException;
568
+ }
569
+
570
+ return clock.now;
571
+ };
572
+
573
+ clock.next = function next() {
574
+ runJobs(clock);
575
+ var timer = firstTimer(clock);
576
+ if (!timer) {
577
+ return clock.now;
578
+ }
579
+
580
+ clock.duringTick = true;
581
+ try {
582
+ updateHrTime(timer.callAt);
583
+ clock.now = timer.callAt;
584
+ callTimer(clock, timer);
585
+ runJobs(clock);
586
+ return clock.now;
587
+ } finally {
588
+ clock.duringTick = false;
589
+ }
590
+ };
591
+
592
+ clock.runAll = function runAll() {
593
+ var numTimers, i;
594
+ runJobs(clock);
595
+ for (i = 0; i < clock.loopLimit; i++) {
596
+ if (!clock.timers) {
597
+ return clock.now;
598
+ }
599
+
600
+ numTimers = keys(clock.timers).length;
601
+ if (numTimers === 0) {
602
+ return clock.now;
603
+ }
604
+
605
+ clock.next();
606
+ }
607
+
608
+ throw new Error("Aborting after running " + clock.loopLimit + " timers, assuming an infinite loop!");
609
+ };
610
+
611
+ clock.runToLast = function runToLast() {
612
+ var timer = lastTimer(clock);
613
+ if (!timer) {
614
+ runJobs(clock);
615
+ return clock.now;
616
+ }
617
+
618
+ return clock.tick(timer.callAt);
619
+ };
620
+
621
+ clock.reset = function reset() {
622
+ clock.timers = {};
623
+ };
624
+
625
+ clock.setSystemTime = function setSystemTime(systemTime) {
626
+ // determine time difference
627
+ var newNow = getEpoch(systemTime);
628
+ var difference = newNow - clock.now;
629
+ var id, timer;
630
+
631
+ // update 'system clock'
632
+ clock.now = newNow;
633
+
634
+ // update timers and intervals to keep them stable
635
+ for (id in clock.timers) {
636
+ if (clock.timers.hasOwnProperty(id)) {
637
+ timer = clock.timers[id];
638
+ timer.createdAt += difference;
639
+ timer.callAt += difference;
640
+ }
641
+ }
642
+ };
643
+
644
+ if (performancePresent) {
645
+ clock.performance = Object.create(global.performance);
646
+ clock.performance.now = function lolexNow() {
647
+ return clock.hrNow;
648
+ };
649
+ }
650
+ if (hrtimePresent) {
651
+ clock.hrtime = function (prev) {
652
+ if (Array.isArray(prev)) {
653
+ var oldSecs = (prev[0] + prev[1] / 1e9);
654
+ var newSecs = (clock.hrNow / 1000);
655
+ var difference = (newSecs - oldSecs);
656
+ var secs = fixedFloor(difference);
657
+ var nanosecs = fixedModulo(difference * 1e9, 1e9);
658
+ return [
659
+ secs,
660
+ nanosecs
661
+ ];
662
+ }
663
+ return [
664
+ fixedFloor(clock.hrNow / 1000),
665
+ fixedModulo(clock.hrNow * 1e6, 1e9)
666
+ ];
667
+ };
668
+ }
669
+
670
+ return clock;
671
+ }
672
+ exports.createClock = createClock;
673
+
674
+ /**
675
+ * @param config {Object} optional config
676
+ * @param config.target {Object} the target to install timers in (default `window`)
677
+ * @param config.now {number|Date} a number (in milliseconds) or a Date object (default epoch)
678
+ * @param config.toFake {string[]} names of the methods that should be faked.
679
+ * @param config.loopLimit {number} the maximum number of timers that will be run when calling runAll()
680
+ * @param config.shouldAdvanceTime {Boolean} tells lolex to increment mocked time automatically (default false)
681
+ * @param config.advanceTimeDelta {Number} increment mocked time every <<advanceTimeDelta>> ms (default: 20ms)
682
+ */
683
+ exports.install = function install(config) {
684
+ if ( arguments.length > 1 || config instanceof Date || Array.isArray(config) || typeof config === "number") {
685
+ throw new TypeError("lolex.install called with " + String(config) +
686
+ " lolex 2.0+ requires an object parameter - see https://github.com/sinonjs/lolex");
687
+ }
688
+ config = typeof config !== "undefined" ? config : {};
689
+ config.shouldAdvanceTime = config.shouldAdvanceTime || false;
690
+ config.advanceTimeDelta = config.advanceTimeDelta || 20;
691
+
692
+ var i, l;
693
+ var target = config.target || global;
694
+ var clock = createClock(config.now, config.loopLimit);
695
+
696
+ clock.uninstall = function () {
697
+ uninstall(clock, target, config);
698
+ };
699
+
700
+ clock.methods = config.toFake || ["setTimeout", "clearTimeout", "setImmediate", "clearImmediate","setInterval", "clearInterval", "Date"];
701
+
702
+ if (clock.methods.length === 0) {
703
+ // do not fake nextTick by default - GitHub#126
704
+ clock.methods = keys(timers).filter(function (key) {return key !== "nextTick";});
705
+ }
706
+
707
+ for (i = 0, l = clock.methods.length; i < l; i++) {
708
+ if (clock.methods[i] === "hrtime") {
709
+ if (target.process && typeof target.process.hrtime === "function") {
710
+ hijackMethod(target.process, clock.methods[i], clock);
711
+ }
712
+ } else if (clock.methods[i] === "nextTick") {
713
+ if (target.process && typeof target.process.nextTick === "function") {
714
+ hijackMethod(target.process, clock.methods[i], clock);
715
+ }
716
+ } else {
717
+ if (clock.methods[i] === "setInterval" && config.shouldAdvanceTime === true) {
718
+ var intervalTick = doIntervalTick.bind(null, clock, config.advanceTimeDelta);
719
+ var intervalId = target[clock.methods[i]](
720
+ intervalTick,
721
+ config.advanceTimeDelta);
722
+ clock.attachedInterval = intervalId;
723
+ }
724
+ hijackMethod(target, clock.methods[i], clock);
725
+ }
726
+ }
727
+
728
+ return clock;
729
+ };
730
+
731
+ }).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
732
+ },{}]},{},[1])(1)
733
+ });