Optimise progress timer callback.
This commit is contained in:
parent
1345357a5e
commit
f43a9a7afa
@ -34,8 +34,8 @@
|
|||||||
this.positionNode = document.createTextNode('')
|
this.positionNode = document.createTextNode('')
|
||||||
this.durationNode = document.createTextNode('')
|
this.durationNode = document.createTextNode('')
|
||||||
|
|
||||||
$('#songelapsed').append(this.positionNode)
|
$('#songelapsed').empty().append(this.positionNode)
|
||||||
$('#songlength').append(this.durationNode)
|
$('#songlength').empty().append(this.durationNode)
|
||||||
|
|
||||||
this._progressTimer = new ProgressTimer({
|
this._progressTimer = new ProgressTimer({
|
||||||
// Make sure that the timer object's context is available.
|
// Make sure that the timer object's context is available.
|
||||||
@ -47,7 +47,12 @@
|
|||||||
this._isConnected = false
|
this._isConnected = false
|
||||||
this._mopidy.on('state:online', $.proxy(function () { this._isConnected = true }), this)
|
this._mopidy.on('state:online', $.proxy(function () { this._isConnected = true }), this)
|
||||||
this._mopidy.on('state:offline', $.proxy(function () { this._isConnected = false }), this)
|
this._mopidy.on('state:offline', $.proxy(function () { this._isConnected = false }), this)
|
||||||
this.init()
|
this.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
||||||
|
this._isSyncScheduled = false
|
||||||
|
this._scheduleID = null
|
||||||
|
this._syncAttemptsRemaining = this._maxAttempts
|
||||||
|
this._previousSyncPosition = null
|
||||||
|
this._duration = null
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.SYNC_STATE = {
|
SyncedProgressTimer.SYNC_STATE = {
|
||||||
@ -58,7 +63,7 @@
|
|||||||
|
|
||||||
SyncedProgressTimer.format = function (milliseconds) {
|
SyncedProgressTimer.format = function (milliseconds) {
|
||||||
if (milliseconds === Infinity) {
|
if (milliseconds === Infinity) {
|
||||||
return '(n/a)'
|
return ''
|
||||||
} else if (milliseconds === 0) {
|
} else if (milliseconds === 0) {
|
||||||
return '0:00'
|
return '0:00'
|
||||||
}
|
}
|
||||||
@ -71,57 +76,37 @@
|
|||||||
return minutes + ':' + seconds
|
return minutes + ':' + seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype.init = function () {
|
|
||||||
this.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
|
||||||
this._syncAttemptsRemaining = this._maxAttempts
|
|
||||||
|
|
||||||
this.positionNode.nodeValue = ''
|
|
||||||
this.durationNode.nodeValue = ''
|
|
||||||
|
|
||||||
this._scheduledSyncTime = null
|
|
||||||
this._previousSyncPosition = null
|
|
||||||
this._duration = null
|
|
||||||
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
SyncedProgressTimer.prototype.timerCallback = function (position, duration) {
|
SyncedProgressTimer.prototype.timerCallback = function (position, duration) {
|
||||||
this._update(position, duration)
|
this._update(position, duration)
|
||||||
|
if (this._isSyncScheduled && this._isConnected) {
|
||||||
if (this._isConnected && this._isSyncScheduled()) {
|
|
||||||
this._doSync(position, duration)
|
this._doSync(position, duration)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype._update = function (position, duration) {
|
SyncedProgressTimer.prototype._update = function (position, duration) {
|
||||||
if (!(duration === Infinity && position === 0)) {
|
switch (this.syncState) {
|
||||||
// Timer has been properly initialized.
|
case SyncedProgressTimer.SYNC_STATE.NOT_SYNCED:
|
||||||
this.durationNode.nodeValue = SyncedProgressTimer.format(duration || Infinity)
|
// Waiting for Mopidy to provide a target position.
|
||||||
switch (this.syncState) {
|
this.positionNode.nodeValue = '(wait)'
|
||||||
case SyncedProgressTimer.SYNC_STATE.NOT_SYNCED:
|
break
|
||||||
// Waiting for Mopidy to provide a target position.
|
case SyncedProgressTimer.SYNC_STATE.SYNCING:
|
||||||
this.positionNode.nodeValue = '(wait)'
|
// Busy seeking to new target position.
|
||||||
break
|
this.positionNode.nodeValue = '(sync)'
|
||||||
case SyncedProgressTimer.SYNC_STATE.SYNCING:
|
break
|
||||||
// Busy seeking to new target position.
|
case SyncedProgressTimer.SYNC_STATE.SYNCED:
|
||||||
this.positionNode.nodeValue = '(sync)'
|
this._previousSyncPosition = position
|
||||||
break
|
this.positionNode.nodeValue = SyncedProgressTimer.format(position)
|
||||||
case SyncedProgressTimer.SYNC_STATE.SYNCED:
|
$('#trackslider').val(position).slider('refresh')
|
||||||
this._previousSyncPosition = position
|
break
|
||||||
this.positionNode.nodeValue = SyncedProgressTimer.format(position)
|
|
||||||
$('#trackslider').val(position).slider('refresh')
|
|
||||||
break
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Make sure that default values are displayed while the timer is being initialized.
|
|
||||||
this.positionNode.nodeValue = ''
|
|
||||||
this.durationNode.nodeValue = ''
|
|
||||||
$('#trackslider').val(0).slider('refresh')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype._isSyncScheduled = function () {
|
SyncedProgressTimer.prototype._scheduleSync = function (milliseconds) {
|
||||||
return this._scheduledSyncTime !== null && this._scheduledSyncTime <= new Date().getTime()
|
// Use an anonymous callback to set a boolean value, which should be faster to
|
||||||
|
// check in the timeout callback than doing another function call.
|
||||||
|
clearTimeout(this._scheduleID)
|
||||||
|
this._isSyncScheduled = false
|
||||||
|
this._scheduleID = setTimeout($.proxy(function () { this._isSyncScheduled = true }, this), milliseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype._doSync = function (position, duration) {
|
SyncedProgressTimer.prototype._doSync = function (position, duration) {
|
||||||
@ -130,11 +115,14 @@
|
|||||||
// Don't try to sync if progress timer has not been initialized yet.
|
// Don't try to sync if progress timer has not been initialized yet.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var _this = this
|
var _this = this
|
||||||
_this._mopidy.playback.getTimePosition().then(function (targetPosition) {
|
_this._mopidy.playback.getTimePosition().then(function (targetPosition) {
|
||||||
|
if (_this.syncState === SyncedProgressTimer.SYNC_STATE.NOT_SYNCED) {
|
||||||
|
_this.syncState = SyncedProgressTimer.SYNC_STATE.SYNCING
|
||||||
|
}
|
||||||
if (Math.abs(targetPosition - position) <= 500) {
|
if (Math.abs(targetPosition - position) <= 500) {
|
||||||
// Less than 500ms == in sync.
|
// Less than 500ms == in sync.
|
||||||
_this.syncState = SyncedProgressTimer.SYNC_STATE.SYNCING
|
|
||||||
_this._syncAttemptsRemaining = Math.max(_this._syncAttemptsRemaining - 1, 0)
|
_this._syncAttemptsRemaining = Math.max(_this._syncAttemptsRemaining - 1, 0)
|
||||||
if (_this._syncAttemptsRemaining < _this._maxAttempts - 1 && _this._previousSyncPosition !== targetPosition) {
|
if (_this._syncAttemptsRemaining < _this._maxAttempts - 1 && _this._previousSyncPosition !== targetPosition) {
|
||||||
// Need at least two consecutive syncs to know that Mopidy
|
// Need at least two consecutive syncs to know that Mopidy
|
||||||
@ -143,14 +131,13 @@
|
|||||||
}
|
}
|
||||||
_this._previousSyncPosition = targetPosition
|
_this._previousSyncPosition = targetPosition
|
||||||
// Step back exponentially while increasing number of callbacks.
|
// Step back exponentially while increasing number of callbacks.
|
||||||
_this._scheduledSyncTime = new Date().getTime() +
|
_this._scheduleSync(delay_exponential(0.25, 2, _this._maxAttempts - _this._syncAttemptsRemaining) * 1000)
|
||||||
delay_exponential(0.25, 2, _this._maxAttempts - _this._syncAttemptsRemaining) * 1000
|
|
||||||
} else {
|
} else {
|
||||||
// Drift is too large, re-sync with Mopidy.
|
// Drift is too large, re-sync with Mopidy.
|
||||||
_this._syncAttemptsRemaining = _this._maxAttempts
|
|
||||||
_this._scheduledSyncTime = new Date().getTime() + 100
|
|
||||||
_this._previousSyncPosition = null
|
|
||||||
_this.syncState = SyncedProgressTimer.SYNC_STATE.SYNCING
|
_this.syncState = SyncedProgressTimer.SYNC_STATE.SYNCING
|
||||||
|
_this._syncAttemptsRemaining = _this._maxAttempts
|
||||||
|
_this._previousSyncPosition = null
|
||||||
|
_this._scheduleSync(1000)
|
||||||
_this._progressTimer.set(targetPosition)
|
_this._progressTimer.set(targetPosition)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -160,58 +147,61 @@
|
|||||||
if (arguments.length === 0) {
|
if (arguments.length === 0) {
|
||||||
throw new Error('"SyncedProgressTimer.set" requires the "position" argument.')
|
throw new Error('"SyncedProgressTimer.set" requires the "position" argument.')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
||||||
|
this._syncAttemptsRemaining = this._maxAttempts
|
||||||
// Workaround for https://github.com/adamcik/media-progress-timer/issues/3
|
// Workaround for https://github.com/adamcik/media-progress-timer/issues/3
|
||||||
// This causes the timer to die unexpectedly if the position exceeds
|
// This causes the timer to die unexpectedly if the position exceeds
|
||||||
// the duration slightly.
|
// the duration slightly.
|
||||||
if (this._duration && this._duration < position) {
|
if (this._duration && this._duration < position) {
|
||||||
position = this._duration - 1
|
position = this._duration - 1
|
||||||
}
|
}
|
||||||
this.init()
|
|
||||||
if (arguments.length === 1) {
|
if (arguments.length === 1) {
|
||||||
this._progressTimer.set(position)
|
this._progressTimer.set(position)
|
||||||
} else {
|
} else {
|
||||||
this._duration = duration
|
this._duration = duration
|
||||||
this._progressTimer.set(position, duration)
|
this._progressTimer.set(position, duration)
|
||||||
|
this.durationNode.nodeValue = SyncedProgressTimer.format(duration)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._isSyncScheduled()) {
|
this.updatePosition(position, duration)
|
||||||
// Set lapsed time and slider position directly as timer callback is not currently
|
$('#trackslider').val(position).slider('refresh')
|
||||||
// running.
|
|
||||||
this.positionNode.nodeValue = SyncedProgressTimer.format(position)
|
|
||||||
if (arguments.length === 2) {
|
|
||||||
this.durationNode.nodeValue = SyncedProgressTimer.format(duration)
|
|
||||||
}
|
|
||||||
$('#trackslider').val(position).slider('refresh')
|
|
||||||
}
|
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype.start = function () {
|
SyncedProgressTimer.prototype.start = function () {
|
||||||
this.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
this.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
||||||
this._scheduledSyncTime = new Date().getTime()
|
this._scheduleSync(0)
|
||||||
this._progressTimer.start()
|
this._progressTimer.start()
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype.stop = function () {
|
SyncedProgressTimer.prototype.stop = function () {
|
||||||
this._progressTimer.stop()
|
this._progressTimer.stop()
|
||||||
this._scheduledSyncTime = null
|
clearTimeout(this._scheduleID)
|
||||||
this.updatePosition(this._previousSyncPosition)
|
this._isSyncScheduled = false
|
||||||
|
if (this.syncState !== SyncedProgressTimer.SYNC_STATE.SYNCED && this._previousSyncPosition) {
|
||||||
|
// Timer was busy trying to sync when it was stopped, fallback to displaying the last synced position on screen.
|
||||||
|
this.positionNode.nodeValue = SyncedProgressTimer.format(this._previousSyncPosition)
|
||||||
|
}
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype.reset = function () {
|
SyncedProgressTimer.prototype.reset = function () {
|
||||||
this._progressTimer.reset()
|
// this._progressTimer.reset()
|
||||||
this.stop()
|
this.stop()
|
||||||
this.init()
|
|
||||||
this.set(0, Infinity)
|
this.set(0, Infinity)
|
||||||
|
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
SyncedProgressTimer.prototype.updatePosition = function (position) {
|
SyncedProgressTimer.prototype.updatePosition = function (position) {
|
||||||
this.positionNode.nodeValue = SyncedProgressTimer.format(position)
|
if (!(this._duration === Infinity && position === 0)) {
|
||||||
|
this.positionNode.nodeValue = SyncedProgressTimer.format(position)
|
||||||
|
} else {
|
||||||
|
this.positionNode.nodeValue = ''
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return SyncedProgressTimer
|
return SyncedProgressTimer
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
CACHE MANIFEST
|
CACHE MANIFEST
|
||||||
|
|
||||||
# 2016-04-03:v2
|
# 2016-04-06:v1
|
||||||
|
|
||||||
NETWORK:
|
NETWORK:
|
||||||
*
|
*
|
||||||
|
|||||||
@ -15,9 +15,15 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
// Helper function to provide a reference time in milliseconds.
|
// Helper function to provide a reference time in milliseconds.
|
||||||
var now = typeof window.performance !== 'undefined' &&
|
var now = /* Sinon does not currently support faking `window.performance`
|
||||||
typeof window.performance.now !== 'undefined' &&
|
(see https://github.com/sinonjs/sinon/issues/803).
|
||||||
window.performance.now.bind(window.performance) || Date.now ||
|
Changing this to only rely on `new Date().getTime()
|
||||||
|
in the interim in order to allow testing of the
|
||||||
|
progress timer from MMW.
|
||||||
|
|
||||||
|
typeof window.performance !== 'undefined' &&
|
||||||
|
typeof window.performance.now !== 'undefined' &&
|
||||||
|
window.performance.now.bind(window.performance) || Date.now ||*/
|
||||||
function() { return new Date().getTime(); };
|
function() { return new Date().getTime(); };
|
||||||
|
|
||||||
// Helper to warn library users about deprecated features etc.
|
// Helper to warn library users about deprecated features etc.
|
||||||
|
|||||||
@ -14,6 +14,20 @@ describe('SyncedTimer', function () {
|
|||||||
var mopidy
|
var mopidy
|
||||||
var playback
|
var playback
|
||||||
var getTimePositionStub
|
var getTimePositionStub
|
||||||
|
var clock
|
||||||
|
|
||||||
|
function setFakeTimers () {
|
||||||
|
clock = sinon.useFakeTimers()
|
||||||
|
syncedProgressTimer._progressTimer = new ProgressTimer({
|
||||||
|
callback: $.proxy(syncedProgressTimer.timerCallback, syncedProgressTimer),
|
||||||
|
disableRequestAnimationFrame: true // No window available during testing - use fallback mechanism to schedule updates
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreFakeTimers () {
|
||||||
|
clock.restore()
|
||||||
|
}
|
||||||
|
|
||||||
before(function () {
|
before(function () {
|
||||||
$(document.body).append(
|
$(document.body).append(
|
||||||
'<div id="slidercontainer"><!-- slider for track position -->' +
|
'<div id="slidercontainer"><!-- slider for track position -->' +
|
||||||
@ -24,26 +38,40 @@ describe('SyncedTimer', function () {
|
|||||||
'</div>'
|
'</div>'
|
||||||
)
|
)
|
||||||
$('#trackslider').slider() // Initialize slider
|
$('#trackslider').slider() // Initialize slider
|
||||||
})
|
$('#trackslider').on('slidestart', function () {
|
||||||
beforeEach(function () {
|
syncedProgressTimer.stop()
|
||||||
|
$('#trackslider').on('change', function () { syncedProgressTimer.updatePosition($(this).val()) })
|
||||||
|
})
|
||||||
|
|
||||||
|
$('#trackslider').on('slidestop', function () {
|
||||||
|
$('#trackslider').off('change')
|
||||||
|
syncedProgressTimer.updatePosition($(this).val())
|
||||||
|
// Simulate doSeekPos($(this).val())
|
||||||
|
syncedProgressTimer.set($(this).val())
|
||||||
|
})
|
||||||
|
|
||||||
playback = {
|
playback = {
|
||||||
getTimePosition: function () { return $.when(1000) },
|
getTimePosition: function () { return $.when(1000) },
|
||||||
getState: function () { return $.when('stopped') }
|
getState: function () { return $.when('stopped') }
|
||||||
}
|
}
|
||||||
mopidy = new Mopidy({callingConvention: 'by-position-or-by-name'})
|
|
||||||
mopidy.playback = playback
|
|
||||||
getTimePositionStub = sinon.stub(playback, 'getTimePosition')
|
getTimePositionStub = sinon.stub(playback, 'getTimePosition')
|
||||||
// Simulate Mopidy's track position advancing 10ms between each call.
|
// Simulate Mopidy's track position advancing 250ms between each call for 0:01 to 0:10
|
||||||
for (var i = 0; i < MAX_ATTEMPTS; i++) {
|
for (var i = 0; i < 10000 / 250; i++) {
|
||||||
getTimePositionStub.onCall(i).returns($.when(1000 + i * 10))
|
getTimePositionStub.onCall(i).returns($.when((i + 1) * 250))
|
||||||
}
|
}
|
||||||
mopidy = sinon.stub(mopidy)
|
mopidy = sinon.stub(new Mopidy({callingConvention: 'by-position-or-by-name'}))
|
||||||
|
mopidy.playback = playback
|
||||||
|
})
|
||||||
|
|
||||||
|
beforeEach(function () {
|
||||||
syncedProgressTimer = new SyncedProgressTimer(MAX_ATTEMPTS, mopidy)
|
syncedProgressTimer = new SyncedProgressTimer(MAX_ATTEMPTS, mopidy)
|
||||||
syncedProgressTimer._isConnected = true
|
syncedProgressTimer._isConnected = true
|
||||||
})
|
})
|
||||||
|
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
getTimePositionStub.restore()
|
getTimePositionStub.reset()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#SyncedTimer()', function () {
|
describe('#SyncedTimer()', function () {
|
||||||
it('should add text nodes to DOM for position and duration indicators', function () {
|
it('should add text nodes to DOM for position and duration indicators', function () {
|
||||||
expect($('#songelapsed')).to.have.text('')
|
expect($('#songelapsed')).to.have.text('')
|
||||||
@ -61,7 +89,7 @@ describe('SyncedTimer', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should handle Infinity', function () {
|
it('should handle Infinity', function () {
|
||||||
assert.equal(SyncedProgressTimer.format(Infinity), '(n/a)')
|
assert.equal(SyncedProgressTimer.format(Infinity), '')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle zero', function () {
|
it('should handle zero', function () {
|
||||||
@ -70,44 +98,57 @@ describe('SyncedTimer', function () {
|
|||||||
})
|
})
|
||||||
|
|
||||||
describe('#timerCallback()', function () {
|
describe('#timerCallback()', function () {
|
||||||
var clock
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
clock = sinon.useFakeTimers()
|
setFakeTimers()
|
||||||
syncedProgressTimer._progressTimer = new ProgressTimer({
|
|
||||||
callback: $.proxy(syncedProgressTimer.timerCallback, syncedProgressTimer),
|
|
||||||
disableRequestAnimationFrame: true // No window available during testing - use fallback mechanism to schedule updates
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
clock.restore()
|
restoreFakeTimers()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not try to sync unless connected to mopidy', function () {
|
it('should not try to sync unless connected to mopidy', function () {
|
||||||
|
var _doSyncStub = sinon.stub(syncedProgressTimer, '_doSync')
|
||||||
|
|
||||||
syncedProgressTimer._isConnected = false
|
syncedProgressTimer._isConnected = false
|
||||||
var _syncScheduledStub = sinon.stub(syncedProgressTimer, '_isSyncScheduled')
|
syncedProgressTimer.set(0, 1000).start()
|
||||||
assert.isFalse(_syncScheduledStub.called, '_syncScheduledStub called')
|
clock.tick(1000)
|
||||||
_syncScheduledStub.restore()
|
|
||||||
|
assert.isFalse(_doSyncStub.called, '_doSync called')
|
||||||
|
syncedProgressTimer.stop()
|
||||||
|
_doSyncStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update text nodes', function () {
|
it('should update text nodes', function () {
|
||||||
var updateStub = sinon.stub(syncedProgressTimer, '_update')
|
var updateStub = sinon.stub(syncedProgressTimer, '_update')
|
||||||
|
|
||||||
syncedProgressTimer.set(0, 1000).start()
|
syncedProgressTimer.set(0, 1000).start()
|
||||||
assert.isTrue(updateStub.called, '_update not called')
|
assert.isTrue(updateStub.called, '_update not called')
|
||||||
|
syncedProgressTimer.stop()
|
||||||
updateStub.restore()
|
updateStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should check if a sync is scheduled', function () {
|
it('should attempt to perform a sync as soon as timer is started', function () {
|
||||||
var scheduleStub = sinon.stub(syncedProgressTimer, '_isSyncScheduled').returns(true)
|
var syncStub = sinon.stub(syncedProgressTimer, '_doSync')
|
||||||
syncedProgressTimer.set(0, 1000).start()
|
|
||||||
assert.isTrue(scheduleStub.called, '_isSyncScheduled not called')
|
syncedProgressTimer.set(0, 1000).start() // 'start' will immediately schedule a sync.
|
||||||
scheduleStub.restore()
|
clock.tick(250)
|
||||||
|
|
||||||
|
assert.isTrue(syncStub.called, '_doSync not called')
|
||||||
|
syncedProgressTimer.stop()
|
||||||
|
syncStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should attempt to perform a sync when scheduled', function () {
|
it('should not attempt to perform a sync untill scheduled', function () {
|
||||||
var syncStub = sinon.stub(syncedProgressTimer, '_doSync')
|
var syncStub = sinon.stub(syncedProgressTimer, '_doSync')
|
||||||
syncedProgressTimer.set(0, 1000).start()
|
|
||||||
|
syncedProgressTimer.set(0, 5000).start()
|
||||||
|
syncedProgressTimer._scheduleSync(500)
|
||||||
clock.tick(250)
|
clock.tick(250)
|
||||||
assert.isTrue(syncStub.called, '_doSync not called')
|
assert.isFalse(syncStub.called, 'next _doSync should only have been called after 500ms')
|
||||||
|
|
||||||
|
syncStub.reset()
|
||||||
|
clock.tick(500)
|
||||||
|
assert.isTrue(syncStub.called, 'next _doSync not called after 500ms')
|
||||||
|
syncedProgressTimer.stop()
|
||||||
syncStub.restore()
|
syncStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -115,34 +156,27 @@ describe('SyncedTimer', function () {
|
|||||||
// Simulate runtime on a 5-second track
|
// Simulate runtime on a 5-second track
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED, 'Timer was initialized in incorrect state')
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED, 'Timer was initialized in incorrect state')
|
||||||
syncedProgressTimer.set(0, 5000).start()
|
syncedProgressTimer.set(0, 5000).start()
|
||||||
|
|
||||||
var wasSyncing = false
|
var wasSyncing = false
|
||||||
for (var i = 0; i < MAX_ATTEMPTS; i++) {
|
for (var i = 0; i < 4; i++) {
|
||||||
clock.tick(250) // 250ms * MAX_ATTEMPTS is only 2 seconds, but we'll be synced after only two attempts
|
clock.tick(250)
|
||||||
wasSyncing = wasSyncing || syncedProgressTimer.syncState === SyncedProgressTimer.SYNC_STATE.SYNCING
|
wasSyncing = wasSyncing || syncedProgressTimer.syncState === SyncedProgressTimer.SYNC_STATE.SYNCING
|
||||||
}
|
}
|
||||||
|
syncedProgressTimer.stop()
|
||||||
assert.isTrue(wasSyncing, 'Timer never entered the "syncing" state')
|
assert.isTrue(wasSyncing, 'Timer never entered the "syncing" state')
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCED, 'Timer failed to sync')
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCED, 'Timer failed to sync')
|
||||||
|
syncedProgressTimer.stop()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#_update()', function () {
|
describe('#_update()', function () {
|
||||||
it('should clear timers and reset slider to zero while not ready', function () {
|
it('should set duration to "" for tracks with infinite duration (e.g. streams)', function () {
|
||||||
syncedProgressTimer.positionNode.nodeValue = '1:00'
|
|
||||||
syncedProgressTimer.durationNode.nodeValue = '2:00'
|
|
||||||
$('#trackslider').val(100).slider('refresh')
|
|
||||||
syncedProgressTimer._update(0, Infinity)
|
|
||||||
|
|
||||||
assert.equal(syncedProgressTimer.positionNode.nodeValue, '')
|
|
||||||
assert.equal(syncedProgressTimer.durationNode.nodeValue, '')
|
|
||||||
assert.equal($('#trackslider').val(), 0)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should set duration to "(n/a)" for tracks with infinite duration (e.g. streams)', function () {
|
|
||||||
syncedProgressTimer._update(1000, Infinity)
|
syncedProgressTimer._update(1000, Infinity)
|
||||||
assert.equal(syncedProgressTimer.durationNode.nodeValue, '(n/a)')
|
assert.equal(syncedProgressTimer.durationNode.nodeValue, '')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should show "(wait)" while waiting for Mopidy to supply a position', function () {
|
it('should show "(wait)" while untill syncing starts', function () {
|
||||||
|
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
||||||
syncedProgressTimer._update(1000, 2000)
|
syncedProgressTimer._update(1000, 2000)
|
||||||
assert.equal(syncedProgressTimer.positionNode.nodeValue, '(wait)')
|
assert.equal(syncedProgressTimer.positionNode.nodeValue, '(wait)')
|
||||||
})
|
})
|
||||||
@ -161,84 +195,122 @@ describe('SyncedTimer', function () {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#_isSyncScheduled()', function () {
|
describe('#scheduleSync', function () {
|
||||||
var scheduleSpy
|
beforeEach(function () {
|
||||||
var clock
|
setFakeTimers()
|
||||||
before(function () {
|
|
||||||
scheduleSpy = sinon.spy(syncedProgressTimer, '_isSyncScheduled')
|
|
||||||
clock = sinon.useFakeTimers()
|
|
||||||
})
|
})
|
||||||
after(function () {
|
afterEach(function () {
|
||||||
scheduleSpy.restore()
|
restoreFakeTimers()
|
||||||
clock.restore()
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should schedule sync when scheduled time arrives', function () {
|
it('should schedule sync when scheduled time arrives', function () {
|
||||||
syncedProgressTimer._scheduledSyncTime = new Date().getTime() + 1000
|
clock.tick(0)
|
||||||
assert.isFalse(syncedProgressTimer._isSyncScheduled())
|
syncedProgressTimer._scheduleSync(1000)
|
||||||
clock.tick(1000)
|
assert.isFalse(syncedProgressTimer._isSyncScheduled)
|
||||||
assert.isTrue(syncedProgressTimer._isSyncScheduled())
|
clock.tick(1001)
|
||||||
|
assert.isTrue(syncedProgressTimer._isSyncScheduled)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should clear schedule on each call', function () {
|
||||||
|
var clearSpy = sinon.spy(window, 'clearTimeout')
|
||||||
|
|
||||||
|
clock.tick(0)
|
||||||
|
syncedProgressTimer._isSyncScheduled = true
|
||||||
|
syncedProgressTimer._scheduleSync(1000)
|
||||||
|
assert.isFalse(syncedProgressTimer._isSyncScheduled)
|
||||||
|
|
||||||
|
var scheduleID = syncedProgressTimer._scheduleID
|
||||||
|
clock.tick(1001)
|
||||||
|
syncedProgressTimer._scheduleSync(1000)
|
||||||
|
assert(clearSpy.calledWith(scheduleID))
|
||||||
|
clearSpy.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#_doSync', function () {
|
describe('#_doSync', function () {
|
||||||
var clock
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
clock = sinon.useFakeTimers()
|
setFakeTimers()
|
||||||
})
|
})
|
||||||
afterEach(function () {
|
afterEach(function () {
|
||||||
clock.restore()
|
restoreFakeTimers()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not try to sync until timer has been set', function () {
|
it('should not try to sync until timer has been set', function () {
|
||||||
syncedProgressTimer._doSync(0, Infinity)
|
syncedProgressTimer._doSync(0, Infinity)
|
||||||
assert.isFalse(getTimePositionStub.called, 'getTimePosition called even though timer has not been set')
|
assert.isFalse(getTimePositionStub.called, 'tried to do sync even though the timer has not been set')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should request position from Mopidy', function () {
|
it('should request position from Mopidy', function () {
|
||||||
syncedProgressTimer._doSync(1000, 2000)
|
syncedProgressTimer._doSync(1000, 2000)
|
||||||
assert.isTrue(getTimePositionStub.called, 'getTimePosition not called')
|
assert.isTrue(getTimePositionStub.called, 'getTimePosition not called')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should set state to "SYNCING" as soon as the first sync attempt is made', function () {
|
||||||
|
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.NOT_SYNCED
|
||||||
|
syncedProgressTimer._doSync(100, 2000)
|
||||||
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
||||||
|
})
|
||||||
|
|
||||||
it('should set state to synced after two consecutive successful syncs (i.e. time drift < 500ms)', function () {
|
it('should set state to synced after two consecutive successful syncs (i.e. time drift < 500ms)', function () {
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED)
|
||||||
clock.tick(10)
|
clock.tick(250)
|
||||||
syncedProgressTimer._doSync(1010, 2000)
|
syncedProgressTimer._doSync(250, 2000)
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
||||||
clock.tick(10)
|
clock.tick(250)
|
||||||
syncedProgressTimer._doSync(1020, 2000)
|
syncedProgressTimer._doSync(500, 2000)
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCED)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCED)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should re-initialize and set state to syncing if time drift is more than 500ms', function () {
|
it('should re-initialize and set state to syncing if time drift is more than 500ms', function () {
|
||||||
syncedProgressTimer._doSync(1, 2000)
|
var scheduleStub = sinon.stub(syncedProgressTimer, '_scheduleSync')
|
||||||
|
|
||||||
|
syncedProgressTimer._doSync(1000, 2000)
|
||||||
|
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
||||||
assert.equal(syncedProgressTimer._syncAttemptsRemaining, syncedProgressTimer._maxAttempts)
|
assert.equal(syncedProgressTimer._syncAttemptsRemaining, syncedProgressTimer._maxAttempts)
|
||||||
|
assert.isNull(syncedProgressTimer._previousSyncPosition)
|
||||||
|
assert(scheduleStub.calledWith(1000), 'Expected next sync to be scheduled 1s from now')
|
||||||
|
scheduleStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should step back exponentially while syncing', function () {
|
it('should step back exponentially while syncing', function () {
|
||||||
|
var scheduleStub = sinon.stub(syncedProgressTimer, '_scheduleSync')
|
||||||
|
|
||||||
for (var i = 0; i < syncedProgressTimer._maxAttempts; i++) {
|
for (var i = 0; i < syncedProgressTimer._maxAttempts; i++) {
|
||||||
syncedProgressTimer._doSync(1000 + (i + 1) * 10, 2000)
|
syncedProgressTimer._doSync(i * 250, 2000)
|
||||||
// If we don't advance the clock then '_syncAttemptsRemaining' should just contain the step-back in seconds
|
assert.equal(syncedProgressTimer._syncAttemptsRemaining, syncedProgressTimer._maxAttempts - i - 1, 'Incorrect number of sync attempts remaining')
|
||||||
assert.equal(syncedProgressTimer._syncAttemptsRemaining, syncedProgressTimer._maxAttempts - i - 1, 'Incorrect sync attempts remaining')
|
assert(scheduleStub.calledWith(0.25 * (Math.pow(2, i)) * 1000), 'Incorrect sync time scheduled: ' + scheduleStub.getCall(i))
|
||||||
assert.equal(syncedProgressTimer._scheduledSyncTime, (0.25 * (Math.pow(2, i)) * 1000), 'Incorrect sync time scheduled')
|
scheduleStub.reset()
|
||||||
}
|
}
|
||||||
|
scheduleStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should check sync every 32s once synced', function () {
|
it('should check sync every 32s once synced', function () {
|
||||||
|
var scheduleStub = sinon.stub(syncedProgressTimer, '_scheduleSync')
|
||||||
|
|
||||||
syncedProgressTimer._syncAttemptsRemaining = 0
|
syncedProgressTimer._syncAttemptsRemaining = 0
|
||||||
syncedProgressTimer._doSync(1000, 2000)
|
syncedProgressTimer._doSync(250, 2000)
|
||||||
assert.equal(syncedProgressTimer._scheduledSyncTime, 32000)
|
assert(scheduleStub.calledWith(32000))
|
||||||
|
scheduleStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not sync unless track playback is progressing', function () {
|
it('should not sync unless track playback is progressing', function () {
|
||||||
getTimePositionStub.restore()
|
getTimePositionStub.restore()
|
||||||
getTimePositionStub = sinon.stub(playback, 'getTimePosition')
|
|
||||||
getTimePositionStub.returns($.when(1000)) // Simulate playback 'stuck' at 1000ms.
|
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED)
|
||||||
clock.tick(10)
|
clock.tick(250)
|
||||||
syncedProgressTimer._doSync(1010, 2000)
|
syncedProgressTimer._doSync(250, 2000)
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
||||||
clock.tick(10)
|
clock.tick(250)
|
||||||
syncedProgressTimer._doSync(1010, 2000)
|
syncedProgressTimer._doSync(250, 2000)
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.SYNCING)
|
||||||
|
|
||||||
|
// Restore getTimePositionStub to previous state
|
||||||
|
getTimePositionStub = sinon.stub(playback, 'getTimePosition')
|
||||||
|
// Simulate Mopidy's track position advancing 250ms between each call for 0:01 to 0:10
|
||||||
|
for (var i = 0; i < 10000 / 250; i++) {
|
||||||
|
getTimePositionStub.onCall(i).returns($.when((i + 1) * 250))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -252,18 +324,21 @@ describe('SyncedTimer', function () {
|
|||||||
assert.equal(syncedProgressTimer._progressTimer._state.position, 1000)
|
assert.equal(syncedProgressTimer._progressTimer._state.position, 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should update track slider if no sync is scheduled', function () {
|
it('should update position and track slider immediately', function () {
|
||||||
syncedProgressTimer.stop()
|
syncedProgressTimer.stop()
|
||||||
syncedProgressTimer.set(1000, 2000)
|
syncedProgressTimer.set(1000, 2000)
|
||||||
expect($('#songelapsed').text()).to.endWith('0:01')
|
|
||||||
|
expect($('#songelapsed').text()).to.equal('0:01')
|
||||||
assert.equal($('#trackslider').val(), 1000)
|
assert.equal($('#trackslider').val(), 1000)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should implement workaround for https://github.com/adamcik/media-progress-timer/issues/3', function () {
|
it('should implement workaround for https://github.com/adamcik/media-progress-timer/issues/3', function () {
|
||||||
syncedProgressTimer.set(1000, 2000).start()
|
syncedProgressTimer.set(1000, 2000).start()
|
||||||
|
|
||||||
assert.equal(syncedProgressTimer._duration, 2000)
|
assert.equal(syncedProgressTimer._duration, 2000)
|
||||||
syncedProgressTimer.set(3000)
|
syncedProgressTimer.set(3000)
|
||||||
assert.equal(syncedProgressTimer._progressTimer._state.position, 1999)
|
assert.equal(syncedProgressTimer._progressTimer._state.position, 1999, 'Expected position to be less than duration')
|
||||||
|
syncedProgressTimer.stop()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -272,6 +347,7 @@ describe('SyncedTimer', function () {
|
|||||||
var startStub = sinon.stub(syncedProgressTimer._progressTimer, 'start')
|
var startStub = sinon.stub(syncedProgressTimer._progressTimer, 'start')
|
||||||
syncedProgressTimer.start()
|
syncedProgressTimer.start()
|
||||||
assert(startStub.called)
|
assert(startStub.called)
|
||||||
|
syncedProgressTimer.stop()
|
||||||
startStub.restore()
|
startStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -279,16 +355,19 @@ describe('SyncedTimer', function () {
|
|||||||
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.SYNCED
|
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.SYNCED
|
||||||
syncedProgressTimer.start()
|
syncedProgressTimer.start()
|
||||||
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED)
|
assert.equal(syncedProgressTimer.syncState, SyncedProgressTimer.SYNC_STATE.NOT_SYNCED)
|
||||||
|
syncedProgressTimer.stop()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should schedule a sync immediately', function () {
|
it('should schedule a sync immediately', function () {
|
||||||
var clock = sinon.useFakeTimers()
|
var scheduleSpy = sinon.spy(syncedProgressTimer, '_scheduleSync')
|
||||||
syncedProgressTimer._scheduledSyncTime = new Date().getTime() + 5000
|
|
||||||
expect(syncedProgressTimer._scheduledSyncTime).to.be.above(new Date().getTime())
|
syncedProgressTimer.set(0, 1000)
|
||||||
|
syncedProgressTimer._isSyncScheduled = false
|
||||||
syncedProgressTimer.start()
|
syncedProgressTimer.start()
|
||||||
clock.tick(1000)
|
|
||||||
expect(syncedProgressTimer._scheduledSyncTime).to.be.below(new Date().getTime())
|
assert(scheduleSpy.calledWith(0))
|
||||||
clock.restore()
|
syncedProgressTimer.stop()
|
||||||
|
scheduleSpy.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -296,45 +375,51 @@ describe('SyncedTimer', function () {
|
|||||||
it('should stop timer', function () {
|
it('should stop timer', function () {
|
||||||
var stopStub = sinon.stub(syncedProgressTimer._progressTimer, 'stop')
|
var stopStub = sinon.stub(syncedProgressTimer._progressTimer, 'stop')
|
||||||
syncedProgressTimer.stop()
|
syncedProgressTimer.stop()
|
||||||
|
|
||||||
assert(stopStub.called)
|
assert(stopStub.called)
|
||||||
|
syncedProgressTimer.stop()
|
||||||
stopStub.restore()
|
stopStub.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should show position when stopped', function () {
|
it('should show last synced position if stopped while busy syncing', function () {
|
||||||
|
syncedProgressTimer.set(1000, 5000)
|
||||||
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.SYNCED
|
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.SYNCED
|
||||||
syncedProgressTimer._update(1000, 5000)
|
syncedProgressTimer._previousSyncPosition = 1000
|
||||||
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.SYNCING
|
syncedProgressTimer.syncState = SyncedProgressTimer.SYNC_STATE.SYNCING
|
||||||
syncedProgressTimer._update(2000, 5000)
|
syncedProgressTimer._update(2000, 5000)
|
||||||
assert.equal(syncedProgressTimer.positionNode.nodeValue, '(sync)')
|
assert.equal(syncedProgressTimer.positionNode.nodeValue, '(sync)')
|
||||||
syncedProgressTimer.stop()
|
syncedProgressTimer.stop()
|
||||||
assert.equal(syncedProgressTimer.positionNode.nodeValue, '0:01')
|
assert.equal(syncedProgressTimer.positionNode.nodeValue, '0:01')
|
||||||
|
expect($('#songelapsed').text()).to.equal('0:01')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should cancel any scheduled syncs', function () {
|
it('should cancel any scheduled syncs', function () {
|
||||||
syncedProgressTimer._scheduledSyncTime = 5000
|
var cancelSpy = sinon.spy(window, 'clearTimeout')
|
||||||
|
|
||||||
|
syncedProgressTimer._isSyncScheduled = true
|
||||||
syncedProgressTimer.stop()
|
syncedProgressTimer.stop()
|
||||||
expect(syncedProgressTimer._scheduledSyncTime).to.be.null
|
|
||||||
|
assert.isFalse(syncedProgressTimer._isSyncScheduled)
|
||||||
|
assert(cancelSpy.calledWith(syncedProgressTimer._scheduleID))
|
||||||
|
cancelSpy.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('#reset()', function () {
|
describe('#reset()', function () {
|
||||||
it('should reset timer to 0:00 - (n/a) ', function () {
|
it('should reset timer to "" - "" ', function () {
|
||||||
var resetStub = sinon.stub(syncedProgressTimer._progressTimer, 'reset')
|
|
||||||
var initStub = sinon.stub(syncedProgressTimer, 'init')
|
|
||||||
var stopStub = sinon.stub(syncedProgressTimer, 'stop')
|
var stopStub = sinon.stub(syncedProgressTimer, 'stop')
|
||||||
|
var setStub = sinon.stub(syncedProgressTimer, 'set')
|
||||||
|
|
||||||
syncedProgressTimer.reset()
|
syncedProgressTimer.reset()
|
||||||
|
|
||||||
assert(resetStub.called)
|
|
||||||
assert(initStub.called)
|
|
||||||
assert(stopStub.called)
|
assert(stopStub.called)
|
||||||
|
assert(setStub.called)
|
||||||
|
|
||||||
assert.equal(syncedProgressTimer.positionNode.nodeValue, '0:00')
|
assert.equal(syncedProgressTimer.positionNode.nodeValue, '', 'Position node was not reset')
|
||||||
assert.equal(syncedProgressTimer.durationNode.nodeValue, '(n/a)')
|
assert.equal(syncedProgressTimer.durationNode.nodeValue, '', 'Duration node was not reset')
|
||||||
|
|
||||||
resetStub.restore()
|
|
||||||
initStub.restore()
|
|
||||||
stopStub.restore()
|
stopStub.restore()
|
||||||
|
setStub.restore()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -345,8 +430,84 @@ describe('SyncedTimer', function () {
|
|||||||
syncedProgressTimer.updatePosition(1000)
|
syncedProgressTimer.updatePosition(1000)
|
||||||
|
|
||||||
assert.isTrue(formatSpy.called)
|
assert.isTrue(formatSpy.called)
|
||||||
expect(syncedProgressTimer.positionNode.nodeValue).to.endWith('0:01')
|
expect(syncedProgressTimer.positionNode.nodeValue).to.equal('0:01')
|
||||||
formatSpy.restore()
|
formatSpy.restore()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('should set position to "" if timer has not been initialized', function () {
|
||||||
|
syncedProgressTimer.set(1000, 2000)
|
||||||
|
expect(syncedProgressTimer.positionNode.nodeValue).to.equal('0:01')
|
||||||
|
|
||||||
|
syncedProgressTimer.updatePosition(0)
|
||||||
|
assert.equal(syncedProgressTimer.positionNode.nodeValue, '0:00', 'Position node was not reset')
|
||||||
|
|
||||||
|
syncedProgressTimer.reset()
|
||||||
|
syncedProgressTimer.updatePosition(0)
|
||||||
|
assert.equal(syncedProgressTimer.positionNode.nodeValue, '', 'Position node was not reset')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('integration tests', function () {
|
||||||
|
beforeEach(function () {
|
||||||
|
setFakeTimers()
|
||||||
|
})
|
||||||
|
afterEach(function () {
|
||||||
|
restoreFakeTimers()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('simulate 30-second test run, ', function () {
|
||||||
|
// Initialize
|
||||||
|
syncedProgressTimer.reset()
|
||||||
|
expect($('#songelapsed').text()).to.equal('')
|
||||||
|
expect($('#songlength').text()).to.equal('')
|
||||||
|
assert.equal($('#trackslider').val(), 0)
|
||||||
|
|
||||||
|
// Set song info
|
||||||
|
syncedProgressTimer.set(0, 30000)
|
||||||
|
expect($('#songelapsed').text()).to.equal('0:00')
|
||||||
|
expect($('#songlength').text()).to.equal('0:30')
|
||||||
|
assert.equal($('#trackslider').val(), 0)
|
||||||
|
|
||||||
|
// Start
|
||||||
|
syncedProgressTimer.start()
|
||||||
|
clock.tick(40)
|
||||||
|
expect($('#songelapsed').text()).to.equal('(wait)')
|
||||||
|
expect($('#songlength').text()).to.equal('0:30')
|
||||||
|
assert.equal($('#trackslider').val(), 0)
|
||||||
|
|
||||||
|
// Syncing
|
||||||
|
clock.tick(250)
|
||||||
|
expect($('#songelapsed').text()).to.equal('(sync)')
|
||||||
|
expect($('#songlength').text()).to.equal('0:30')
|
||||||
|
assert.equal($('#trackslider').val(), 0)
|
||||||
|
|
||||||
|
// Synced
|
||||||
|
clock.tick(1000)
|
||||||
|
expect($('#songelapsed').text()).to.equal('0:01')
|
||||||
|
expect($('#songlength').text()).to.equal('0:30')
|
||||||
|
assert.isAtLeast($('#trackslider').val(), 1000)
|
||||||
|
|
||||||
|
// Move slider
|
||||||
|
$('#trackslider').trigger('slidestart')
|
||||||
|
clock.tick(250)
|
||||||
|
$('#trackslider').val(5000).slider('refresh')
|
||||||
|
$('#trackslider').trigger('change')
|
||||||
|
clock.tick(250)
|
||||||
|
$('#trackslider').trigger('slidestop')
|
||||||
|
|
||||||
|
clock.tick(1000) // Position should remain '0:05' as the timer should not be running after a slider change
|
||||||
|
expect($('#songelapsed').text()).to.equal('0:05')
|
||||||
|
|
||||||
|
// Start -> Sync -> Stop
|
||||||
|
syncedProgressTimer.start()
|
||||||
|
clock.tick(40)
|
||||||
|
expect($('#songelapsed').text()).to.equal('(sync)')
|
||||||
|
syncedProgressTimer._previousSyncPosition = 1000
|
||||||
|
syncedProgressTimer.stop()
|
||||||
|
expect($('#songelapsed').text()).to.equal('0:01')
|
||||||
|
expect($('#songlength').text()).to.equal('0:30')
|
||||||
|
|
||||||
|
syncedProgressTimer.stop()
|
||||||
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user