2015-11-20 07:29:49 +08:00
|
|
|
_ = require 'underscore'
|
|
|
|
|
|
|
|
# Public: To make specs easier to test, we make all asynchronous behavior
|
|
|
|
# actually synchronous. We do this by overriding all global timeout and
|
|
|
|
# Promise functions.
|
|
|
|
#
|
|
|
|
# You must now manually call `advanceClock()` in order to move the "clock"
|
|
|
|
# forward.
|
|
|
|
class TimeOverride
|
|
|
|
|
|
|
|
@advanceClock = (delta=1) =>
|
|
|
|
@now += delta
|
|
|
|
callbacks = []
|
|
|
|
|
|
|
|
@timeouts ?= []
|
|
|
|
@timeouts = @timeouts.filter ([id, strikeTime, callback]) =>
|
|
|
|
if strikeTime <= @now
|
|
|
|
callbacks.push(callback)
|
|
|
|
false
|
|
|
|
else
|
|
|
|
true
|
|
|
|
|
|
|
|
callback() for callback in callbacks
|
|
|
|
|
|
|
|
@resetTime = =>
|
|
|
|
@now = 0
|
|
|
|
@timeoutCount = 0
|
|
|
|
@intervalCount = 0
|
|
|
|
@timeouts = []
|
|
|
|
@intervalTimeouts = {}
|
|
|
|
@originalPromiseScheduler = null
|
|
|
|
|
|
|
|
@enableSpies = =>
|
|
|
|
window.advanceClock = @advanceClock
|
|
|
|
|
fix(specs): change spec scheduler back to setTimeout
Summary:
Adds a new `npm run test-window` that will launch specs in a window so you
can use the debugger
The spec window wouldn't close because `onbeforeunload` was unnecessarily
preventing close. This circumvents this in spec mode.
Most significantly I discovered we can't use the synchronous timer for the
promise scheduler anymore. Suppose you do:
```
it('should error', async () => {
try {
await doSomething()
throw new Error("doSomething should have thrown!")
} catch (err) {
expect(err.message).toMatch(/my message/)
}
})
```
The way async/await is transpiled, when `doSomething` throws, the error
will propagate all the way back up to the uncaughtPromiseException handler
before the `catch` gets called and registered. The transpilation method
assumes that when the function gets executed it can synchrously advance
beyond the call before the `then` or `catch` resolve. When the promise
scheduler is synchronous this doesn't happen.
I chose to use `setTimeout` instead of `process.nextTick` or
`setImmediate` as the promise scheduler. `setTimeout` seems to work better
with Chrome's async function call stacks. `nextTick` and `setImmediate`,
being Node methods, skip Chrome's async watchers.
Test Plan:
I talked with Juan about these changes, in an upcoming diff he will be
testing these in the context of our broader test suite.
Reviewers: mark, khamidou, halla, spang, juan
Reviewed By: juan
Differential Revision: https://phab.nylas.com/D3779
2017-01-26 06:43:11 +08:00
|
|
|
window.originalSetTimeout = window.setTimeout
|
2015-11-20 07:29:49 +08:00
|
|
|
window.originalSetInterval = window.setInterval
|
|
|
|
spyOn(window, "setTimeout").andCallFake @_fakeSetTimeout
|
|
|
|
spyOn(window, "clearTimeout").andCallFake @_fakeClearTimeout
|
|
|
|
spyOn(window, "setInterval").andCallFake @_fakeSetInterval
|
|
|
|
spyOn(window, "clearInterval").andCallFake @_fakeClearInterval
|
|
|
|
spyOn(_._, "now").andCallFake => @now
|
|
|
|
|
|
|
|
# spyOn(Date, "now").andCallFake => @now
|
|
|
|
# spyOn(Date.prototype, "getTime").andCallFake => @now
|
|
|
|
|
|
|
|
@_setPromiseScheduler()
|
|
|
|
|
|
|
|
@_setPromiseScheduler: =>
|
|
|
|
@originalPromiseScheduler ?= Promise.setScheduler (fn) =>
|
fix(specs): change spec scheduler back to setTimeout
Summary:
Adds a new `npm run test-window` that will launch specs in a window so you
can use the debugger
The spec window wouldn't close because `onbeforeunload` was unnecessarily
preventing close. This circumvents this in spec mode.
Most significantly I discovered we can't use the synchronous timer for the
promise scheduler anymore. Suppose you do:
```
it('should error', async () => {
try {
await doSomething()
throw new Error("doSomething should have thrown!")
} catch (err) {
expect(err.message).toMatch(/my message/)
}
})
```
The way async/await is transpiled, when `doSomething` throws, the error
will propagate all the way back up to the uncaughtPromiseException handler
before the `catch` gets called and registered. The transpilation method
assumes that when the function gets executed it can synchrously advance
beyond the call before the `then` or `catch` resolve. When the promise
scheduler is synchronous this doesn't happen.
I chose to use `setTimeout` instead of `process.nextTick` or
`setImmediate` as the promise scheduler. `setTimeout` seems to work better
with Chrome's async function call stacks. `nextTick` and `setImmediate`,
being Node methods, skip Chrome's async watchers.
Test Plan:
I talked with Juan about these changes, in an upcoming diff he will be
testing these in the context of our broader test suite.
Reviewers: mark, khamidou, halla, spang, juan
Reviewed By: juan
Differential Revision: https://phab.nylas.com/D3779
2017-01-26 06:43:11 +08:00
|
|
|
window.originalSetTimeout(fn, 0)
|
2015-11-20 07:29:49 +08:00
|
|
|
|
|
|
|
@disableSpies = =>
|
|
|
|
window.advanceClock = null
|
|
|
|
|
|
|
|
jasmine.unspy(window, 'setTimeout')
|
|
|
|
jasmine.unspy(window, 'clearTimeout')
|
|
|
|
jasmine.unspy(window, 'setInterval')
|
|
|
|
jasmine.unspy(window, 'clearInterval')
|
|
|
|
|
|
|
|
jasmine.unspy(_._, "now")
|
|
|
|
|
|
|
|
Promise.setScheduler(@originalPromiseScheduler) if @originalPromiseScheduler
|
|
|
|
@originalPromiseScheduler = null
|
|
|
|
|
|
|
|
@resetSpyData = ->
|
|
|
|
window.setTimeout.reset?()
|
|
|
|
window.clearTimeout.reset?()
|
|
|
|
window.setInterval.reset?()
|
|
|
|
window.clearInterval.reset?()
|
|
|
|
Date.now.reset?()
|
|
|
|
Date.prototype.getTime.reset?()
|
|
|
|
|
|
|
|
@_fakeSetTimeout = (callback, ms) =>
|
|
|
|
id = ++@timeoutCount
|
|
|
|
@timeouts.push([id, @now + ms, callback])
|
|
|
|
id
|
|
|
|
|
|
|
|
@_fakeClearTimeout = (idToClear) =>
|
|
|
|
@timeouts ?= []
|
|
|
|
@timeouts = @timeouts.filter ([id]) -> id != idToClear
|
|
|
|
|
|
|
|
@_fakeSetInterval = (callback, ms) =>
|
|
|
|
id = ++@intervalCount
|
2016-10-14 04:31:40 +08:00
|
|
|
action = =>
|
2015-11-20 07:29:49 +08:00
|
|
|
callback()
|
|
|
|
@intervalTimeouts[id] = @_fakeSetTimeout(action, ms)
|
|
|
|
@intervalTimeouts[id] = @_fakeSetTimeout(action, ms)
|
|
|
|
id
|
|
|
|
|
|
|
|
@_fakeClearInterval = (idToClear) =>
|
|
|
|
@_fakeClearTimeout(@intervalTimeouts[idToClear])
|
|
|
|
|
|
|
|
module.exports = TimeOverride
|