Extracts everything related to localStorage in common/persistence-service.js

That way, whenever we want to use something else (like a JSON file, or a DB) we will only have to change persistence-service.js
- Migrated the unit tests there too.
- Added replay in case of jPlayer error.
This commit is contained in:
Hyzual 2015-01-03 18:17:55 +01:00
parent 3c809d1903
commit 42830bbdbf
8 changed files with 262 additions and 222 deletions

View file

@ -2,7 +2,7 @@
/* Declare app level module */ /* Declare app level module */
angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize', angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize',
'jamstash.subsonic.controller', 'jamstash.archive.controller', 'jamstash.player.controller', 'jamstash.queue.controller', 'angular-locker']) 'jamstash.subsonic.controller', 'jamstash.archive.controller', 'jamstash.player.controller', 'jamstash.queue.controller', 'jamstash.persistence'])
.config(['$routeProvider',function($routeProvider) { .config(['$routeProvider',function($routeProvider) {
$routeProvider $routeProvider
@ -48,10 +48,4 @@ angular.module('JamStash', ['ngCookies', 'ngRoute', 'ngSanitize',
} }
}; };
}]); }]);
}])
.config(['lockerProvider', function (lockerProvider) {
lockerProvider.setDefaultDriver('local')
.setDefaultNamespace('jamstash')
.setEventsEnabled(false);
}]); }]);

View file

@ -1,6 +1,6 @@
angular.module('JamStash') angular.module('JamStash')
.controller('AppController', ['$scope', '$rootScope', '$document', '$window', '$location', '$cookieStore', '$http', 'utils', 'globals', 'model', 'notifications', 'player', 'locker', .controller('AppController', ['$scope', '$rootScope', '$document', '$window', '$location', '$cookieStore', '$http', 'utils', 'globals', 'model', 'notifications', 'player', 'persistence',
function($scope, $rootScope, $document, $window, $location, $cookieStore, $http, utils, globals, model, notifications, player, locker) { function($scope, $rootScope, $document, $window, $location, $cookieStore, $http, utils, globals, model, notifications, player, persistence) {
'use strict'; 'use strict';
$rootScope.settings = globals.settings; $rootScope.settings = globals.settings;
@ -400,27 +400,6 @@ angular.module('JamStash')
return $sce.trustAsHtml(html); return $sce.trustAsHtml(html);
}; };
$scope.loadTrackPosition = function () {
// Load Saved Song
var song = locker.get('CurrentSong');
if (song) {
player.load(song);
}
if (globals.settings.Debug) { console.log('Current Position Loaded from localStorage: ', song); }
};
$scope.loadQueue = function () {
// load Saved queue
var queue = locker.get('CurrentQueue');
if (queue) {
player.addSongs(queue);
if (player.queue.length > 0) {
notifications.updateMessage(player.queue.length + ' Saved Song(s)', true);
}
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + player.queue.length + ' song(s)'); }
}
};
/* Launch on Startup */ /* Launch on Startup */
$scope.loadSettings(); $scope.loadSettings();
utils.switchTheme(globals.settings.Theme); utils.switchTheme(globals.settings.Theme);
@ -432,9 +411,8 @@ angular.module('JamStash')
if ($scope.loggedIn()) { if ($scope.loggedIn()) {
//$scope.ping(); //$scope.ping();
if (globals.settings.SaveTrackPosition) { if (globals.settings.SaveTrackPosition) {
$scope.loadQueue(); persistence.loadQueue();
$scope.loadTrackPosition(); persistence.loadTrackPosition();
//FIXME: HYZ: player.startSaveTrackPosition();
} }
} }
/* End Startup */ /* End Startup */

View file

@ -57,79 +57,4 @@ describe("Main controller", function() {
xdescribe("toggleSetting -", function() { xdescribe("toggleSetting -", function() {
}); });
describe("load from localStorage -", function() {
var fakeStorage;
beforeEach(function() {
fakeStorage = {};
spyOn(locker, "get").and.callFake(function(key) {
return fakeStorage[key];
});
});
describe("loadTrackPosition -", function() {
beforeEach(function() {
spyOn(player, "load");
});
it("Given that we previously saved the current track's position in local Storage, it loads the song we saved into the player", function() {
var song = {
id: 8626,
name: 'Pectinatodenticulate',
artist: 'Isiah Hosfield',
album: 'Tammanyize'
};
fakeStorage = {
'CurrentSong': song
};
scope.loadTrackPosition();
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
expect(player.load).toHaveBeenCalledWith(song);
});
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
scope.loadTrackPosition();
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
expect(player.load).not.toHaveBeenCalled();
});
});
describe("loadQueue -", function() {
beforeEach(function() {
spyOn(notifications, "updateMessage");
spyOn(player, "addSongs").and.callFake(function (songs) {
// Update the queue length so that notifications work
player.queue.length += songs.length;
});
});
it("Given that we previously saved the playing queue in local Storage, it fills the player's queue with what we saved and notifies the user", function() {
var queue = [
{ id: 8705 },
{ id: 1617 },
{ id: 9812 }
];
fakeStorage = {
'CurrentQueue': queue
};
scope.loadQueue();
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
expect(player.addSongs).toHaveBeenCalledWith(queue);
expect(notifications.updateMessage).toHaveBeenCalledWith('3 Saved Song(s)', true);
});
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
scope.loadQueue();
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
expect(player.addSongs).not.toHaveBeenCalled();
expect(notifications.updateMessage).not.toHaveBeenCalled();
});
});
});
}); });

View file

@ -0,0 +1,48 @@
'use strict';
/**
* jamstash.persistence Module
*
* Provides load, save and delete operations for the current song and queue.
* Data storage provided by HTML5 localStorage.
*/
angular.module('jamstash.persistence', ['jamstash.settings', 'jamstash.player.service', 'jamstash.notifications', 'angular-locker'])
.config(['lockerProvider', function (lockerProvider) {
lockerProvider.setDefaultDriver('local')
.setDefaultNamespace('jamstash')
.setEventsEnabled(false);
}])
.service('persistence', ['globals', 'player', 'notifications', 'locker', function(globals, player, notifications, locker){
this.loadTrackPosition = function () {
// Load Saved Song
var song = locker.get('CurrentSong');
if (song) {
player.load(song);
}
if (globals.settings.Debug) { console.log('Current Position Loaded from localStorage: ', song); }
};
this.loadQueue = function () {
// load Saved queue
var queue = locker.get('CurrentQueue');
if (queue) {
player.addSongs(queue);
if (player.queue.length > 0) {
notifications.updateMessage(player.queue.length + ' Saved Song(s)', true);
}
if (globals.settings.Debug) { console.log('Play Queue Loaded From localStorage: ' + player.queue.length + ' song(s)'); }
}
};
this.saveTrackPosition = function (song) {
locker.put('CurrentSong', song);
if (globals.settings.Debug) { console.log('Saving Current Position: ', song); }
};
this.saveQueue = function () {
locker.put('CurrentQueue', player.queue);
if (globals.settings.Debug) { console.log('Saving Queue: ' + player.queue.length + ' songs'); }
};
}]);

View file

@ -0,0 +1,110 @@
describe("Persistence service", function() {
'use strict';
var persistence, player, notifications, locker;
var song;
beforeEach(function() {
module('jamstash.persistence');
inject(function (_persistence_, _player_, _notifications_, _locker_) {
persistence = _persistence_;
player = _player_;
notifications = _notifications_;
locker = _locker_;
});
song = {
id: 8626,
name: 'Pectinatodenticulate',
artist: 'Isiah Hosfield',
album: 'Tammanyize'
};
player.queue = [];
});
describe("load from localStorage -", function() {
var fakeStorage;
beforeEach(function() {
fakeStorage = {};
spyOn(locker, "get").and.callFake(function(key) {
return fakeStorage[key];
});
});
describe("loadTrackPosition -", function() {
beforeEach(function() {
spyOn(player, "load");
});
it("Given that we previously saved the current track's position in local Storage, it loads the song we saved into the player", function() {
fakeStorage = { 'CurrentSong': song };
persistence.loadTrackPosition();
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
expect(player.load).toHaveBeenCalledWith(song);
});
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
persistence.loadTrackPosition();
expect(locker.get).toHaveBeenCalledWith('CurrentSong');
expect(player.load).not.toHaveBeenCalled();
});
});
describe("loadQueue -", function() {
beforeEach(function() {
spyOn(notifications, "updateMessage");
spyOn(player, "addSongs").and.callFake(function (songs) {
// Update the queue length so that notifications work
player.queue.length += songs.length;
});
});
it("Given that we previously saved the playing queue in local Storage, it fills the player's queue with what we saved and notifies the user", function() {
var queue = [
{ id: 8705 },
{ id: 1617 },
{ id: 9812 }
];
fakeStorage = { 'CurrentQueue': queue };
persistence.loadQueue();
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
expect(player.addSongs).toHaveBeenCalledWith(queue);
expect(notifications.updateMessage).toHaveBeenCalledWith('3 Saved Song(s)', true);
});
it("Given that we didn't save anything in local Storage, it doesn't load anything", function() {
persistence.loadQueue();
expect(locker.get).toHaveBeenCalledWith('CurrentQueue');
expect(player.addSongs).not.toHaveBeenCalled();
expect(notifications.updateMessage).not.toHaveBeenCalled();
});
});
});
describe("save from localStorage -", function() {
beforeEach(function() {
spyOn(locker, "put");
});
it("it saves the current track's position in local Storage", function() {
persistence.saveTrackPosition(song);
expect(locker.put).toHaveBeenCalledWith('CurrentSong', song);
});
it("it saves the playing queue in local Storage", function() {
player.queue = [
{ id: 1245 },
{ id: 7465 },
{ id: 948 }
];
persistence.saveQueue();
expect(locker.put).toHaveBeenCalledWith('CurrentQueue', player.queue);
});
});
});

View file

@ -103,6 +103,7 @@
<script src="common/model-service.js"></script> <script src="common/model-service.js"></script>
<script src="common/utils-service.js"></script> <script src="common/utils-service.js"></script>
<script src="common/notification-service.js"></script> <script src="common/notification-service.js"></script>
<script src="common/persistence-service.js"></script>
<script src="common/main-controller.js"></script> <script src="common/main-controller.js"></script>
<script src="subsonic/subsonic.js"></script> <script src="subsonic/subsonic.js"></script>
<script src="subsonic/subsonic-service.js"></script> <script src="subsonic/subsonic-service.js"></script>

View file

@ -4,10 +4,10 @@
* Encapsulates the jPlayer plugin. It watches the player service for the song to play, load or restart. * Encapsulates the jPlayer plugin. It watches the player service for the song to play, load or restart.
* It also enables jPlayer to attach event handlers to our UI through css Selectors. * It also enables jPlayer to attach event handlers to our UI through css Selectors.
*/ */
angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstash.settings', 'jamstash.subsonic.service', 'jamstash.notifications', 'jamstash.utils', 'angular-locker']) angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstash.settings', 'jamstash.subsonic.service', 'jamstash.notifications', 'jamstash.utils', 'jamstash.persistence'])
.directive('jplayer', ['player', 'globals', 'subsonic', 'notifications', 'utils', 'locker', '$window', .directive('jplayer', ['player', 'globals', 'subsonic', 'notifications', 'utils', '$window', 'persistence',
function(playerService, globals, subsonic, notifications, utils, locker, $window) { function(playerService, globals, subsonic, notifications, utils, $window, persistence) {
'use strict'; 'use strict';
return { return {
restrict: 'EA', restrict: 'EA',
@ -30,6 +30,8 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
solution: audioSolution, solution: audioSolution,
supplied: 'mp3', supplied: 'mp3',
preload: 'auto', preload: 'auto',
errorAlerts: false,
warningAlerts: false,
cssSelectorAncestor: '#player', cssSelectorAncestor: '#player',
cssSelector: { cssSelector: {
play: '.PlayTrack', play: '.PlayTrack',
@ -68,6 +70,16 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
subsonic.scrobble(scope.currentSong); subsonic.scrobble(scope.currentSong);
scope.scrobbled = true; scope.scrobbled = true;
} }
},
error: function (event) {
var position = event.jPlayer.status.currentTime;
if(position) {
$player.jPlayer('play', position);
}
if (globals.settings.Debug) {
console.log("jPlayer error: ", event.jPlayer.error);
console.log("Stream interrupted, retrying from position: ", position);
}
} }
}); });
}; };
@ -75,17 +87,19 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
scope.$watch(function () { scope.$watch(function () {
return playerService.getPlayingSong(); return playerService.getPlayingSong();
}, function (newSong) { }, function (newSong) {
scope.currentSong = newSong; if(newSong !== undefined) {
$player.jPlayer('setMedia', {'mp3': newSong.url}); scope.currentSong = newSong;
if(playerService.loadSong === true) { $player.jPlayer('setMedia', {'mp3': newSong.url});
// Do not play, only load if(playerService.loadSong === true) {
playerService.loadSong = false; // Do not play, only load
scope.revealControls(); playerService.loadSong = false;
$player.jPlayer('pause', newSong.position); scope.revealControls();
} else { $player.jPlayer('pause', newSong.position);
$player.jPlayer('play'); } else {
if(globals.settings.NotificationSong) { $player.jPlayer('play');
notifications.showNotification(newSong.coverartthumb, utils.toHTML.un(newSong.name), utils.toHTML.un(newSong.artist + ' - ' + newSong.album), 'text', '#NextTrack'); if(globals.settings.NotificationSong) {
notifications.showNotification(newSong.coverartthumb, utils.toHTML.un(newSong.name), utils.toHTML.un(newSong.artist + ' - ' + newSong.album), 'text', '#NextTrack');
}
} }
} }
}); });
@ -104,23 +118,6 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
$('#songdetails').css('visibility', 'visible'); $('#songdetails').css('visibility', 'visible');
}; };
scope.saveTrackPosition = function () {
var audio = $player.data('jPlayer');
if (audio !== undefined && scope.currentSong !== undefined) {
var position = audio.status.currentTime;
if (position !== null) {
scope.currentSong.position = position;
locker.put('CurrentSong', scope.currentSong);
if (globals.settings.Debug) { console.log('Saving Current Position: ', scope.currentSong); }
}
}
};
scope.saveQueue = function () {
locker.put('CurrentQueue', playerService.queue);
if (globals.settings.Debug) { console.log('Saving Queue: ' + playerService.queue.length + ' songs'); }
};
scope.startSavePosition = function () { scope.startSavePosition = function () {
if (globals.settings.SaveTrackPosition) { if (globals.settings.SaveTrackPosition) {
if (timerid !== 0) { if (timerid !== 0) {
@ -128,15 +125,16 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas
} }
timerid = $window.setInterval(function () { timerid = $window.setInterval(function () {
var audio = $player.data('jPlayer'); var audio = $player.data('jPlayer');
if (globals.settings.SaveTrackPosition && audio.status.currentTime > 0 && audio.status.paused === false) { if (globals.settings.SaveTrackPosition && scope.currentSong !== undefined &&
audio !== undefined && audio.status.currentTime > 0 && audio.status.paused === false) {
$('#action_SaveProgress') $('#action_SaveProgress')
.fadeTo("slow", 0).delay(500) .fadeTo("slow", 0).delay(500)
.fadeTo("slow", 1).delay(500) .fadeTo("slow", 1).delay(500)
.fadeTo("slow", 0).delay(500) .fadeTo("slow", 0).delay(500)
.fadeTo("slow", 1); .fadeTo("slow", 1);
//TODO: Hyz: Pass position and queue as parameter and move this away in another service scope.currentSong.position = audio.status.currentTime;
scope.saveTrackPosition(); persistence.saveTrackPosition(scope.currentSong);
scope.saveQueue(); persistence.saveQueue();
} }
}, 30000); }, 30000);
} }

View file

@ -2,16 +2,17 @@ describe("jplayer directive", function() {
'use strict'; 'use strict';
var element, scope, $player, playingSong, var element, scope, $player, playingSong,
playerService, mockGlobals, subsonic, notifications, locker, $window; playerService, mockGlobals, subsonic, notifications, persistence, $window;
beforeEach(function() { beforeEach(function() {
playingSong = {};
// We redefine globals because in some tests we need to alter the settings // We redefine globals because in some tests we need to alter the settings
mockGlobals = { mockGlobals = {
settings: { settings: {
AutoPlay: false AutoPlay: false
} }
}; };
// Redefined to avoid firing 'play' with a previous test song
playingSong = undefined;
module('jamstash.player.directive', function($provide) { module('jamstash.player.directive', function($provide) {
// Mock the player service // Mock the player service
$provide.decorator('player', function($delegate) { $provide.decorator('player', function($delegate) {
@ -32,11 +33,12 @@ describe("jplayer directive", function() {
$provide.value('globals', mockGlobals); $provide.value('globals', mockGlobals);
}); });
inject(function($rootScope, $compile, _player_, _subsonic_, _notifications_, _locker_, _$window_) { spyOn($.fn, "jPlayer").and.callThrough();
inject(function($rootScope, $compile, _player_, _subsonic_, _notifications_, _persistence_, _$window_) {
playerService = _player_; playerService = _player_;
subsonic = _subsonic_; subsonic = _subsonic_;
notifications = _notifications_; notifications = _notifications_;
locker = _locker_; persistence = _persistence_;
$window = _$window_; $window = _$window_;
// Compile the directive // Compile the directive
scope = $rootScope.$new(); scope = $rootScope.$new();
@ -48,28 +50,28 @@ describe("jplayer directive", function() {
}); });
describe("When the player service's current song changes,", function() { describe("When the player service's current song changes,", function() {
beforeEach(function() { beforeEach(function() {
spyOn($.fn, "jPlayer").and.returnValue($.fn); // To avoid errors breaking the test, we stub jPlayer
$.fn.jPlayer.and.stub();
playingSong = {url: 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'}; playingSong = {url: 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'};
}); });
it("it sets jPlayer's media and stores the song for future scrobbling", function() { it("it sets jPlayer's media and stores the song for future scrobbling", function() {
scope.$apply(); scope.$apply();
expect($player.jPlayer).toHaveBeenCalledWith('setMedia', {'mp3': 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'}); expect($.fn.jPlayer).toHaveBeenCalledWith('setMedia', {'mp3': 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'});
expect(scope.currentSong).toEqual(playingSong); expect(scope.currentSong).toEqual(playingSong);
}); });
it("if the player service's loadSong flag is true, it does not play the song, it displays the player controls and sets the player to the song's supplied position", function() { it("if the player service's loadSong flag is true, it does not play the song, it displays the player controls and sets the player to the song's supplied position", function() {
spyOn(scope, "revealControls"); spyOn(scope, "revealControls");
playerService.loadSong = true;
playingSong.position = 42.2784; playingSong.position = 42.2784;
playerService.loadSong = true;
scope.$apply(); scope.$apply();
expect($player.jPlayer).not.toHaveBeenCalledWith('play'); expect($player.jPlayer).not.toHaveBeenCalledWith('play');
expect($player.jPlayer).toHaveBeenCalledWith('pause', playingSong.position); expect($player.jPlayer).toHaveBeenCalledWith('pause', 42.2784);
expect(playerService.loadSong).toBeFalsy(); expect(playerService.loadSong).toBeFalsy();
expect(scope.revealControls).toHaveBeenCalled(); expect(scope.revealControls).toHaveBeenCalled();
}); });
@ -86,6 +88,7 @@ describe("jplayer directive", function() {
it("if the global setting NotificationSong is true, it displays a notification", function() { it("if the global setting NotificationSong is true, it displays a notification", function() {
spyOn(notifications, "showNotification"); spyOn(notifications, "showNotification");
mockGlobals.settings.NotificationSong = true; mockGlobals.settings.NotificationSong = true;
scope.$apply(); scope.$apply();
expect(notifications.showNotification).toHaveBeenCalled(); expect(notifications.showNotification).toHaveBeenCalled();
@ -94,8 +97,7 @@ describe("jplayer directive", function() {
}); });
it("When the player service's restartSong flag is true, it restarts the current song and resets the flag to false", function() { it("When the player service's restartSong flag is true, it restarts the current song and resets the flag to false", function() {
spyOn($.fn, "jPlayer").and.returnValue($.fn); $.fn.jPlayer.and.stub();
playerService.restartSong = true; playerService.restartSong = true;
scope.$apply(); scope.$apply();
@ -125,15 +127,6 @@ describe("jplayer directive", function() {
}); });
}); });
it("When jPlayer starts to play the current song, it displays the player controls", function() {
spyOn(scope, "revealControls");
var e = $.jPlayer.event.play;
$player.trigger(e);
expect(scope.revealControls).toHaveBeenCalled();
});
it("When jPlayer gets new media, it resets the scrobbled flag to false", function() { it("When jPlayer gets new media, it resets the scrobbled flag to false", function() {
scope.scrobbled = true; scope.scrobbled = true;
@ -143,7 +136,30 @@ describe("jplayer directive", function() {
expect(scope.scrobbled).toBeFalsy(); expect(scope.scrobbled).toBeFalsy();
}); });
it("When jPlayer throws an error, it tries to restart playback at the last position", function() {
// Fake jPlayer's internal _trigger event because I can't trigger a manual error
var fakejPlayer = {
element: $player,
status: { currentTime: 10.4228 }
};
var error = $.jPlayer.event.error;
$.jPlayer.prototype._trigger.call(fakejPlayer, error);
expect($player.jPlayer).toHaveBeenCalledWith('play', 10.4228);
});
it("When jPlayer starts to play the current song, it displays the player controls", function() {
spyOn(scope, "revealControls");
var e = $.jPlayer.event.play;
$player.trigger(e);
expect(scope.revealControls).toHaveBeenCalled();
});
it("revealControls - it displays the song details and the player controls", function() { it("revealControls - it displays the song details and the player controls", function() {
$.fn.jPlayer.and.stub();
affix('#playermiddle').css('visibility', 'hidden'); affix('#playermiddle').css('visibility', 'hidden');
affix('#songdetails').css('visibility', 'hidden'); affix('#songdetails').css('visibility', 'hidden');
@ -190,78 +206,48 @@ describe("jplayer directive", function() {
}); });
}); });
describe("save to localStorage -", function() { describe("Given that the global setting SaveTrackPosition is true,", function() {
beforeEach(function() { beforeEach(function() {
spyOn(locker, "put"); mockGlobals.settings.SaveTrackPosition = true;
spyOn(persistence, "saveTrackPosition");
spyOn(persistence, "saveQueue");
jasmine.clock().install();
}); });
it("it saves the current song and its position to localStorage", function() { afterEach(function() {
var position = 48.0773; jasmine.clock().uninstall();
$player.data('jPlayer').status.currentTime = position;
scope.currentSong = {
id: 419
};
scope.saveTrackPosition();
expect(scope.currentSong.position).toBe(position);
expect(locker.put).toHaveBeenCalledWith('CurrentSong', scope.currentSong);
}); });
it("it saves the player queue to localStorage", function() { it("every 30 seconds, it saves the current song's position and the playing queue", function() {
var queue = [ scope.currentSong = { id: 419 };
{id: 2313}, $player.data('jPlayer').status.currentTime = 35.3877;
{id: 4268}, $player.data('jPlayer').status.paused = false;
{id: 5470}
];
playerService.queue = queue;
scope.saveQueue(); scope.startSavePosition();
jasmine.clock().tick(30001);
expect(locker.put).toHaveBeenCalledWith('CurrentQueue', queue); expect(scope.currentSong.position).toBe(35.3877);
expect(persistence.saveTrackPosition).toHaveBeenCalledWith(scope.currentSong);
expect(persistence.saveQueue).toHaveBeenCalled();
}); });
describe("Given that the global setting SaveTrackPosition is true,", function() { it("if the song is not playing, it does not save anything", function() {
beforeEach(function() { $player.data('jPlayer').status.currentTime = 0.0;
jasmine.clock().install(); $player.data('jPlayer').status.paused = true;
mockGlobals.settings.SaveTrackPosition = true;
spyOn(scope, "saveTrackPosition");
spyOn(scope, "saveQueue");
});
afterEach(function() { scope.startSavePosition();
jasmine.clock().uninstall(); jasmine.clock().tick(30001);
});
it("every 30 seconds, it saves the current song and queue", function() { expect(persistence.saveTrackPosition).not.toHaveBeenCalled();
$player.data('jPlayer').status.currentTime = 35.3877; expect(persistence.saveQueue).not.toHaveBeenCalled();
$player.data('jPlayer').status.paused = false; });
scope.startSavePosition(); it("if there was already a watcher, it clears it before adding a new one", function() {
jasmine.clock().tick(30001); spyOn($window, "clearInterval");
expect(scope.saveTrackPosition).toHaveBeenCalled(); scope.startSavePosition();
expect(scope.saveQueue).toHaveBeenCalled(); scope.startSavePosition();
}); expect($window.clearInterval).toHaveBeenCalled();
it("if the song is not playing, it does not save anything", function() {
$player.data('jPlayer').status.currentTime = 0.0;
$player.data('jPlayer').status.paused = true;
scope.startSavePosition();
jasmine.clock().tick(30001);
expect(scope.saveTrackPosition).not.toHaveBeenCalled();
expect(scope.saveQueue).not.toHaveBeenCalled();
});
it("if there was already a watcher, it clears it before watching", function() {
spyOn($window, "clearInterval");
scope.startSavePosition();
scope.startSavePosition();
expect($window.clearInterval).toHaveBeenCalled();
});
}); });
}); });
}); });