diff --git a/app/common/main-controller.js b/app/common/main-controller.js index 3752673..465ee3e 100644 --- a/app/common/main-controller.js +++ b/app/common/main-controller.js @@ -443,27 +443,33 @@ return $sce.trustAsHtml(html); }; - function loadTrackPosition() { - //TODO: HYZ: Unit test + $scope.loadTrackPosition = function () { if (utils.browserStorageCheck()) { // Load Saved Song var song = angular.fromJson(localStorage.getItem('CurrentSong')); if (song) { player.load(song); - // Load Saved Queue - var items = angular.fromJson(localStorage.getItem('CurrentQueue')); - if (items) { - player.queue = items; - 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)'); } - } } } else { if (globals.settings.Debug) { console.log('HTML5::loadStorage not supported on your browser'); } } - } + }; + + $scope.loadQueue = function () { + if(utils.browserStorageCheck()) { + // load Saved queue + var queue = angular.fromJson(localStorage.getItem('CurrentQueue')); + if (queue) { + player.queue = 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)'); } + } + } else { + if (globals.settings.Debug) { console.log('HTML5::loadStorage not supported on your browser'); } + } + }; /* Launch on Startup */ $scope.loadSettings(); @@ -476,7 +482,8 @@ if ($scope.loggedIn()) { //$scope.ping(); if (globals.settings.SaveTrackPosition) { - loadTrackPosition(); + $scope.loadQueue(); + $scope.loadTrackPosition(); //FIXME: HYZ: player.startSaveTrackPosition(); } } diff --git a/app/common/main-controller_test.js b/app/common/main-controller_test.js index 045974b..bf6e350 100644 --- a/app/common/main-controller_test.js +++ b/app/common/main-controller_test.js @@ -1,30 +1,132 @@ describe("Main controller", function() { 'use strict'; - describe("updateFavorite -", function() { + var scope, $rootScope, utils, globals, model, notifications, player; + beforeEach(function() { + module('JamStash'); - it("when starring a song, it notifies the user that the star was saved", function() { + inject(function ($controller, _$rootScope_, _$document_, _$window_, _$location_, _$cookieStore_, _utils_, _globals_, _model_, _notifications_, _player_) { + $rootScope = _$rootScope_; + scope = $rootScope.$new(); + utils = _utils_; + globals = _globals_; + model = _model_; + notifications = _notifications_; + player = _player_; + + $controller('AppController', { + $scope: scope, + $rootScope: $rootScope, + $document: _$document_, + $window: _$window_, + $location: _$location_, + $cookieStore: _$cookieStore_, + utils: utils, + globals: globals, + model: model, + notifications: notifications, + player: player + }); + }); + }); + + xdescribe("updateFavorite -", function() { + + xit("when starring a song, it notifies the user that the star was saved", function() { }); - it("when starring an album, it notifies the user that the star was saved", function() { + xit("when starring an album, it notifies the user that the star was saved", function() { }); - it("when starring an artist, it notifies the user that the star was saved", function() { + xit("when starring an artist, it notifies the user that the star was saved", function() { }); - it("given that the Subsonic server returns an error, when starring something, it notifies the user with the error message", function() { + xit("given that the Subsonic server returns an error, when starring something, it notifies the user with the error message", function() { //TODO: move to higher level }); - it("given that the Subsonic server is unreachable, when starring something, it notifies the user with the HTTP error code", function() { + xit("given that the Subsonic server is unreachable, when starring something, it notifies the user with the HTTP error code", function() { //TODO: move to higher level }); }); - describe("toggleSetting -", function() { + xdescribe("toggleSetting -", function() { }); + + describe("load from localStorage -", function() { + var fakeStorage; + beforeEach(function() { + fakeStorage = {}; + + spyOn(localStorage, "getItem").and.callFake(function(key) { + return fakeStorage[key]; + }); + spyOn(utils, "browserStorageCheck").and.returnValue(true); + }); + + 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(localStorage.getItem).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(localStorage.getItem).toHaveBeenCalledWith('CurrentSong'); + expect(player.load).not.toHaveBeenCalled(); + }); + }); + + describe("loadQueue -", function() { + beforeEach(function() { + spyOn(notifications, "updateMessage"); + player.queue = []; + }); + + 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(localStorage.getItem).toHaveBeenCalledWith('CurrentQueue'); + expect(player.queue).toEqual(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(localStorage.getItem).toHaveBeenCalledWith('CurrentQueue'); + expect(player.queue).toEqual([]); + expect(notifications.updateMessage).not.toHaveBeenCalled(); + }); + }); + }); }); diff --git a/app/player/player-directive.js b/app/player/player-directive.js index 9a682d9..a7c584d 100644 --- a/app/player/player-directive.js +++ b/app/player/player-directive.js @@ -1,14 +1,19 @@ -angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstash.settings']) +/** + * jamstash.player.directive module + * + * 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. + */ +angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstash.settings', 'jamstash.subsonic.service']) -.directive('jplayer', ['player', 'globals', function(playerService, globals) { +.directive('jplayer', ['player', 'globals', 'subsonic', function(playerService, globals, subsonic) { 'use strict'; return { restrict: 'EA', template: '
', - link: function(scope, element, attrs) { + link: function(scope, element) { var $player = element.children('div'); - console.log($player); var audioSolution = 'html,flash'; if (globals.settings.ForceFlash) { audioSolution = 'flash,html'; @@ -17,7 +22,7 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas var updatePlayer = function() { $player.jPlayer({ // Flash fallback for outdated browser not supporting HTML5 audio/video tags - // http://jplayer.org/download/ + // TODO: Hyz: Replace in Grunt ! swfPath: 'bower_components/jplayer/dist/jplayer/jquery.jplayer.swf', wmode: 'window', solution: audioSolution, @@ -37,8 +42,8 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas }, play: function() { console.log('jplayer play'); - $('#playermiddle').css('visibility', 'visible'); - $('#songdetails').css('visibility', 'visible'); + scope.revealControls(); + scope.scrobbled = false; }, pause: function() { console.log('jplayer pause'); @@ -47,18 +52,37 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas console.log('jplayer ended'); playerService.nextTrack(); scope.$apply(); + }, + timeupdate: function (event) { + // Scrobble song once percentage is reached + var p = event.jPlayer.status.currentPercentAbsolute; + if (!scope.scrobbled && p > 30) { + if (globals.settings.Debug) { console.log('LAST.FM SCROBBLE - Percent Played: ' + p); } + subsonic.scrobble(scope.currentSong); + scope.scrobbled = true; + } } }); }; updatePlayer(); + scope.currentSong = {}; + scope.scrobbled = false; + scope.$watch(function () { return playerService.getPlayingSong(); }, function (newVal) { console.log('playingSong changed !'); - $player.jPlayer('setMedia', {'mp3': newVal.url}) - .jPlayer('play'); + scope.currentSong = newVal; + $player.jPlayer('setMedia', {'mp3': newVal.url}); + if(playerService.loadSong === true) { + // Do not play, only load + playerService.loadSong = false; + scope.revealControls(); + } else { + $player.jPlayer('play'); + } }); scope.$watch(function () { @@ -70,6 +94,12 @@ angular.module('jamstash.player.directive', ['jamstash.player.service', 'jamstas playerService.restartSong = false; } }); + + scope.revealControls = function () { + $('#playermiddle').css('visibility', 'visible'); + $('#songdetails').css('visibility', 'visible'); + }; + } //end link }; }]); diff --git a/app/player/player-directive_test.js b/app/player/player-directive_test.js index 0b3cc71..5c78a1b 100644 --- a/app/player/player-directive_test.js +++ b/app/player/player-directive_test.js @@ -1,27 +1,26 @@ describe("jplayer directive", function() { 'use strict'; - var element, scope, playerService, globalsService, $player, playingSong; - - function mockGetPlayingSong() { - return playingSong; - } + var element, scope, playerService, globals, subsonic, $player, playingSong; beforeEach(function() { playingSong = {}; module('jamstash.player.directive', function($provide) { // Mock the player service $provide.decorator('player', function($delegate) { - $delegate.getPlayingSong = jasmine.createSpy('getPlayingSong').and.callFake(mockGetPlayingSong); + $delegate.getPlayingSong = jasmine.createSpy('getPlayingSong').and.callFake(function() { + return playingSong; + }); $delegate.nextTrack = jasmine.createSpy('nextTrack'); return $delegate; }); }); - inject(function($rootScope, $compile, _player_, _globals_) { + inject(function($rootScope, $compile, _player_, _globals_, _subsonic_) { playerService = _player_; - globalsService = _globals_; + globals = _globals_; + subsonic = _subsonic_; // Compile the directive scope = $rootScope.$new(); element = '
'; @@ -31,14 +30,38 @@ describe("jplayer directive", function() { $player = element.children('div'); }); - it("When the player service's current playing song changes, it sets jplayer's media and plays the song", function() { - spyOn($.fn, "jPlayer").and.returnValue($.fn); + describe("When the player service's current song changes,", function() { - playingSong = {url: 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'}; - scope.$apply(); + beforeEach(function() { + spyOn($.fn, "jPlayer").and.returnValue($.fn); + playingSong = {url: 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'}; + }); - expect($player.jPlayer).toHaveBeenCalledWith('setMedia', {'mp3': 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'}); - expect($player.jPlayer).toHaveBeenCalledWith('play'); + it("it sets jPlayer's media and stores the song for future scrobbling", function() { + scope.$apply(); + + expect($player.jPlayer).toHaveBeenCalledWith('setMedia', {'mp3': 'https://gantry.com/antemarital/vigorless?a=oropharyngeal&b=killcrop#eviscerate'}); + expect(scope.currentSong).toEqual(playingSong); + }); + + it("if the player service's loadSong flag is true, it does not play the song and it displays the player controls", function() { + spyOn(scope, "revealControls"); + + playerService.loadSong = true; + scope.$apply(); + + expect($player.jPlayer).not.toHaveBeenCalledWith('play'); + expect(playerService.loadSong).toBeFalsy(); + expect(scope.revealControls).toHaveBeenCalled(); + }); + + it("otherwise, it plays it", function() { + playerService.loadSong = false; + scope.$apply(); + + expect($player.jPlayer).toHaveBeenCalledWith('play'); + expect(playerService.loadSong).toBeFalsy(); + }); }); it("When the player service's restartSong flag is true, it restarts the current song and resets the flag to false", function() { @@ -51,7 +74,7 @@ describe("jplayer directive", function() { expect(playerService.restartSong).toBeFalsy(); }); - it("When jplayer has finished the current song, it plays the next track using the", function() { + it("When jplayer has finished the current song, it asks the player service for the next track", function() { var e = $.jPlayer.event.ended; $player.trigger(e); @@ -59,12 +82,67 @@ describe("jplayer directive", function() { }); it("When jPlayer starts to play the current song, it displays the player controls", function() { - affix('#playermiddle').css('visibility', 'hidden'); - affix('#songdetails').css('visibility', 'hidden'); + spyOn(scope, "revealControls"); + var e = $.jPlayer.event.play; $player.trigger(e); + expect(scope.revealControls).toHaveBeenCalled(); + }); + + it("When jPlayer starts to play the current song, it resets the scrobbled flag to false", function() { + scope.scrobbled = true; + + var e = $.jPlayer.event.play; + $player.trigger(e); + + expect(scope.scrobbled).toBeFalsy(); + }); + + it("revealControls - it displays the song details and the player controls", function() { + affix('#playermiddle').css('visibility', 'hidden'); + affix('#songdetails').css('visibility', 'hidden'); + + scope.revealControls(); + expect($('#playermiddle').css('visibility')).toEqual('visible'); expect($('#songdetails').css('visibility')).toEqual('visible'); }); + + describe("Scrobbling -", function() { + var fakejPlayer, timeUpdate; + beforeEach(function() { + spyOn(subsonic, "scrobble"); + scope.currentSong = { + id: 5375 + }; + + // Fake jPlayer's internal _trigger event because I can't trigger a manual timeupdate + fakejPlayer = { + element: $player, + status: { currentPercentAbsolute: 31 } + }; + timeUpdate = $.jPlayer.event.timeupdate; + }); + + it("Given a song that hasn't been scrobbled yet, When jPlayer reaches 30 percent of it, it scrobbles to last.fm using the subsonic service and sets the flag to true", function() { + scope.scrobbled = false; + + // Trigger our fake timeupdate + $.jPlayer.prototype._trigger.call(fakejPlayer, timeUpdate); + + expect(subsonic.scrobble).toHaveBeenCalledWith(scope.currentSong); + expect(scope.scrobbled).toBeTruthy(); + }); + + it("Given a song that has already been scrobbled, when jPlayer reaches 30 percent of it, it does not scrobble again and leaves the flag to true", function() { + scope.scrobbled = true; + + // Trigger our fake timeupdate + $.jPlayer.prototype._trigger.call(fakejPlayer, timeUpdate); + + expect(subsonic.scrobble).not.toHaveBeenCalled(); + expect(scope.scrobbled).toBeTruthy(); + }); + }); }); diff --git a/app/player/player-service.js b/app/player/player-service.js index ed25174..4ff8493 100644 --- a/app/player/player-service.js +++ b/app/player/player-service.js @@ -1,7 +1,7 @@ /** * jamstash.player.service Module * -* Enables app-wide control of the behavior of the player directive. +* Manages the player and playing queue. Use it to play a song, go to next track or add songs to the queue. */ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-underscore/utils']) @@ -13,9 +13,9 @@ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-undersc playingIndex: -1, playingSong: {}, restartSong: false, + loadSong: false, play: function(song) { - console.log('player service - play()', song); var songIndexInQueue; // Find the song's index in the queue, if it's in there songIndexInQueue = player.queue.indexOf(song); @@ -31,22 +31,20 @@ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-undersc }, playFirstSong: function() { - console.log('player service - playFirstSong()'); player.playingIndex = 0; player.play(player.queue[0]); }, load: function(song) { - console.log('player service - load()'); + player.loadSong = true; + player.play(song); }, restart: function() { - console.log('player service - restart()'); player.restartSong = true; }, nextTrack: function() { - console.log('player service - nextTrack()'); if((player.playingIndex + 1) < player.queue.length) { var nextTrack = player.queue[player.playingIndex + 1]; player.playingIndex++; @@ -55,7 +53,6 @@ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-undersc }, previousTrack: function() { - console.log(('player service - previousTrack()')); if((player.playingIndex - 1) > 0) { var previousTrack = player.queue[player.playingIndex - 1]; player.playingIndex--; @@ -66,22 +63,18 @@ angular.module('jamstash.player.service', ['jamstash.settings', 'angular-undersc }, emptyQueue: function() { - console.log('player service - emptyQueue()'); player.queue = []; }, shuffleQueue: function() { - console.log('player service - shuffleQueue()'); player.queue = _.shuffle(player.queue); }, addSong: function(song) { - console.log('player service - addSong()'); player.queue.push(song); }, removeSong: function(song) { - console.log('player service - removeSong()'); var index = player.queue.indexOf(song); player.queue.splice(index, 1); }, diff --git a/app/player/player-service_test.js b/app/player/player-service_test.js index 0246a00..799d1d9 100644 --- a/app/player/player-service_test.js +++ b/app/player/player-service_test.js @@ -180,9 +180,18 @@ describe("Player service -", function() { player.restart(); expect(player.restartSong).toBeTruthy(); }); + + it("When I load the song, the flag for the directive is set", function() { + spyOn(player, "play"); + + player.load(song); + + expect(player.loadSong).toBeTruthy(); + expect(player.play).toHaveBeenCalledWith(song); + }); }); - describe("Given that there is no song in my playing queue", function() { + describe("Given that my playing queue is empty", function() { beforeEach(function() { player.queue = []; diff --git a/app/player/player.html b/app/player/player.html index 1921aaa..f3646c2 100644 --- a/app/player/player.html +++ b/app/player/player.html @@ -5,10 +5,10 @@ Previous track - + Play - diff --git a/app/queue/queue.js b/app/queue/queue.js index 6c56d41..9b52c40 100644 --- a/app/queue/queue.js +++ b/app/queue/queue.js @@ -8,7 +8,6 @@ angular.module('jamstash.queue.controller', ['jamstash.player.service']) $scope.itemType = 'pl'; // TODO: Hyz: What is this ? $scope.playSong = function (song) { - console.log('Queue Controller - playSong()', song); player.play(song); }; diff --git a/app/subsonic/subsonic-service.js b/app/subsonic/subsonic-service.js index 1806445..b2948c0 100644 --- a/app/subsonic/subsonic-service.js +++ b/app/subsonic/subsonic-service.js @@ -793,7 +793,35 @@ angular.module('jamstash.subsonic.service', ['jamstash.settings', 'jamstash.util } }); return deferred.promise; + }, + scrobble: function (song) { + var id = song.id; + //TODO: Hyz: Refactor all this boilerplate into an http interceptor ? or something higher level than this + var exception = {reason: 'Error when contacting the Subsonic server.'}; + var deferred = $q.defer(); + var httpPromise; + if (globals.settings.Debug) { console.log('Scrobble Song: ' + id); } + if(globals.settings.Protocol === 'jsonp') { + httpPromise = $http.jsonp(globals.BaseURL() + '/scrobble.view?callback=JSON_CALLBACK&' + globals.BaseParams() + '&id=' + id + '&submission=true', + { + timeout: globals.settings.Timeout + }); + } else { + httpPromise = $http.get(globals.BaseURL() + '/scrobble.view?' + globals.BaseParams() + '&id=' + id + '&submission=true', + { + timeout: globals.settings.Timeout + }); + } + httpPromise.success(function (data, status) { + console.log(data); + if(globals.settings.Debug) { console.log('Successfully scrobbled song: ' + id); } + }).error(function(data, status) { + exception.httpError = status; + deferred.reject(exception); + }); + return deferred.promise; } + // End subsonic }; }]); diff --git a/app/subsonic/subsonic-service_test.js b/app/subsonic/subsonic-service_test.js index 8540fdc..09909cd 100644 --- a/app/subsonic/subsonic-service_test.js +++ b/app/subsonic/subsonic-service_test.js @@ -1,160 +1,174 @@ describe("Subsonic service -", function() { - 'use strict'; + 'use strict'; - var subsonic, mockBackend, mockGlobals, response; + var subsonic, mockBackend, mockGlobals, response; + beforeEach(function() { + // We redefine it because in some tests we need to alter the settings + mockGlobals = { + settings: { + AutoPlaylistSize: 3, + Protocol: 'jsonp' + }, + BaseURL: function () { + return 'http://demo.subsonic.com/rest'; + }, + BaseParams: function () { + return 'u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=jsonp'; + } + }; - var url = 'http://demo.subsonic.com/rest/getStarred.view?'+ - 'callback=JSON_CALLBACK&u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=jsonp'; + module('jamstash.subsonic.service', function ($provide) { + $provide.value('globals', mockGlobals); + }); - beforeEach(function() { - // We redefine it because in some tests we need to alter the settings - mockGlobals = { - settings: { - AutoPlaylistSize: 3, - Protocol: 'jsonp' - }, - BaseURL: function () { - return 'http://demo.subsonic.com/rest'; - }, - BaseParams: function () { - return 'u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=jsonp'; - } - }; + inject(function (_subsonic_, $httpBackend) { + subsonic = _subsonic_; + mockBackend = $httpBackend; + }); + response = {"subsonic-response": {status: "ok", version: "1.10.2"}}; + }); - module('jamstash.subsonic.service', function ($provide) { - $provide.value('globals', mockGlobals); - }); + afterEach(function() { + mockBackend.verifyNoOutstandingExpectation(); + mockBackend.verifyNoOutstandingRequest(); + }); - inject(function (_subsonic_, $httpBackend) { - subsonic = _subsonic_; - mockBackend = $httpBackend; - }); - response = {"subsonic-response": {status: "ok", version: "1.10.2"}}; - }); + it("scrobble - Given a song, when I scrobble it, it returns true if there was no error", function() { + var song = { id: 45872 }; + var url = 'http://demo.subsonic.com/rest/scrobble.view?' + + 'callback=JSON_CALLBACK&u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=jsonp&id=45872&submission=true'; - afterEach(function() { - mockBackend.verifyNoOutstandingExpectation(); - mockBackend.verifyNoOutstandingRequest(); - }); + mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); - describe("getStarred -", function() { + var promise = subsonic.scrobble(song); + mockBackend.flush(); - it("Given that I have 2 starred albums, 1 starred artist and 3 starred songs in my library, when getting everything starred, it returns them all", function() { - response["subsonic-response"].starred = {artist: [{id: 2245}], album: [{id: 1799},{id: 20987}], song: [{id: 2478},{id: 14726},{id: 742}]}; - mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); + expect(promise).toBeResolved(); + }); - var promise = subsonic.getStarred(); - mockBackend.flush(); + describe("getStarred -", function() { + var url = 'http://demo.subsonic.com/rest/getStarred.view?'+ + 'callback=JSON_CALLBACK&u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=jsonp'; - expect(promise).toBeResolvedWith({artist: [ - {id: 2245} - ], album: [ - {id: 1799},{id: 20987} - ], song: [ - {id: 2478},{id: 14726},{id: 742} - ] - }); - }); + it("Given that I have 2 starred albums, 1 starred artist and 3 starred songs in my library, when getting everything starred, it returns them all", function() { + response["subsonic-response"].starred = {artist: [{id: 2245}], album: [{id: 1799},{id: 20987}], song: [{id: 2478},{id: 14726},{id: 742}]}; + mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); - it("Given that the global protocol setting is 'json' and given that I have 3 starred songs in my library, when getting everything starred, it uses GET and returns 3 starred songs", function() { - mockGlobals.settings.Protocol = 'json'; - mockGlobals.BaseParams = function() { return 'u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=json'; }; - var getUrl = 'http://demo.subsonic.com/rest/getStarred.view?' + - 'u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=json'; - response["subsonic-response"].starred = {song: [ - {id: "2147"},{id:"9847"},{id:"214"} - ]}; - mockBackend.expectGET(getUrl).respond(200, JSON.stringify(response)); + var promise = subsonic.getStarred(); + mockBackend.flush(); - var promise = subsonic.getStarred(); - mockBackend.flush(); + expect(promise).toBeResolvedWith({artist: [ + {id: 2245} + ], album: [ + {id: 1799},{id: 20987} + ], song: [ + {id: 2478},{id: 14726},{id: 742} + ] + }); + }); - expect(promise).toBeResolvedWith({song: [ - {id: "2147"},{id:"9847"},{id:"214"}] - }); - }); + it("Given that the global protocol setting is 'json' and given that I have 3 starred songs in my library, when getting everything starred, it uses GET and returns 3 starred songs", function() { + mockGlobals.settings.Protocol = 'json'; + mockGlobals.BaseParams = function() { return 'u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=json'; }; + var getUrl = 'http://demo.subsonic.com/rest/getStarred.view?' + + 'u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=json'; + response["subsonic-response"].starred = {song: [ + {id: "2147"},{id:"9847"},{id:"214"} + ]}; + mockBackend.expectGET(getUrl).respond(200, JSON.stringify(response)); - it("Given that there is absolutely nothing starred in my library, when getting everything starred, it returns an error object with a message", function() { - response["subsonic-response"].starred = {}; - mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); + var promise = subsonic.getStarred(); + mockBackend.flush(); - var promise = subsonic.getStarred(); - mockBackend.flush(); + expect(promise).toBeResolvedWith({song: [ + {id: "2147"},{id:"9847"},{id:"214"}] + }); + }); - expect(promise).toBeRejectedWith({reason: 'Nothing is starred on the Subsonic server.'}); - }); + it("Given that there is absolutely nothing starred in my library, when getting everything starred, it returns an error object with a message", function() { + response["subsonic-response"].starred = {}; + mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); - it("Given that the Subsonic server is not responding, when getting everything starred, it returns an error object with a message", function() { - mockBackend.whenJSONP(url).respond(503, 'Service Unavailable'); + var promise = subsonic.getStarred(); + mockBackend.flush(); - var promise = subsonic.getStarred(); - mockBackend.flush(); + expect(promise).toBeRejectedWith({reason: 'Nothing is starred on the Subsonic server.'}); + }); + // TODO: Hyz: Those tests should be at a higher level, we are repeating them for everything... + it("Given that the Subsonic server is not responding, when getting everything starred, it returns an error object with a message", function() { + mockBackend.whenJSONP(url).respond(503, 'Service Unavailable'); - expect(promise).toBeRejectedWith({reason: 'Error when contacting the Subsonic server.', httpError: 503}); - }); + var promise = subsonic.getStarred(); + mockBackend.flush(); - it("Given a missing parameter, when getting the starred songs, it returns an error object with a message", function() { - mockGlobals.BaseParams = function() { return 'u=Hyzual&v=1.10.2&c=Jamstash&f=jsonp';}; - var missingPasswordUrl = 'http://demo.subsonic.com/rest/getStarred.view?'+ - 'callback=JSON_CALLBACK&u=Hyzual&v=1.10.2&c=Jamstash&f=jsonp'; - var errorResponse = {"subsonic-response" : { - "status" : "failed", - "version" : "1.10.2", - "error" : {"code" : 10,"message" : "Required parameter is missing."} - }}; - mockBackend.whenJSONP(missingPasswordUrl).respond(200, errorResponse); + expect(promise).toBeRejectedWith({reason: 'Error when contacting the Subsonic server.', httpError: 503}); + }); - var promise = subsonic.getStarred(); - mockBackend.flush(); + it("Given a missing parameter, when getting the starred songs, it returns an error object with a message", function() { + mockGlobals.BaseParams = function() { return 'u=Hyzual&v=1.10.2&c=Jamstash&f=jsonp';}; + var missingPasswordUrl = 'http://demo.subsonic.com/rest/getStarred.view?'+ + 'callback=JSON_CALLBACK&u=Hyzual&v=1.10.2&c=Jamstash&f=jsonp'; + var errorResponse = {"subsonic-response" : { + "status" : "failed", + "version" : "1.10.2", + "error" : {"code" : 10,"message" : "Required parameter is missing."} + }}; + mockBackend.whenJSONP(missingPasswordUrl).respond(200, errorResponse); - expect(promise).toBeRejectedWith({reason: 'Error when contacting the Subsonic server.', subsonicError: {code: 10, message:'Required parameter is missing.'}}); - }); - }); //end getStarred + var promise = subsonic.getStarred(); + mockBackend.flush(); - describe("getRandomStarredSongs -", function() { - describe("Given that the global setting AutoPlaylist Size is 3", function() { + expect(promise).toBeRejectedWith({reason: 'Error when contacting the Subsonic server.', subsonicError: {code: 10, message:'Required parameter is missing.'}}); + }); + }); //end getStarred - it("and given that I have more than 3 starred songs in my library, when getting random starred songs, the result should be limited to 3 starred songs", function() { - var library = [ - {id: "11841"},{id: "12061"},{id: "17322"},{id: "1547"},{id: "14785"} - ]; - response["subsonic-response"].starred = {song: library}; - mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); + describe("getRandomStarredSongs -", function() { + var url = 'http://demo.subsonic.com/rest/getStarred.view?'+ + 'callback=JSON_CALLBACK&u=Hyzual&p=enc:cGFzc3dvcmQ=&v=1.10.2&c=Jamstash&f=jsonp'; - var promise = subsonic.getRandomStarredSongs(); - // We create a spy in order to get the results of the promise - var success = jasmine.createSpy("success"); - promise.then(success); + describe("Given that the global setting AutoPlaylist Size is 3", function() { - mockBackend.flush(); + it("and given that I have more than 3 starred songs in my library, when getting random starred songs, the result should be limited to 3 starred songs", function() { + var library = [ + {id: "11841"},{id: "12061"},{id: "17322"},{id: "1547"},{id: "14785"} + ]; + response["subsonic-response"].starred = {song: library}; + mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); - expect(promise).toBeResolved(); - expect(success).toHaveBeenCalled(); - var randomlyPickedSongs = success.calls.mostRecent().args[0]; - for (var i = 0; i < randomlyPickedSongs.length; i++) { - expect(library).toContain(randomlyPickedSongs[i]); - } - }); + var promise = subsonic.getRandomStarredSongs(); + // We create a spy in order to get the results of the promise + var success = jasmine.createSpy("success"); + promise.then(success); - it("and given that I have only 1 starred song in my library, when getting random starred songs, it returns my starred song", function() { - response["subsonic-response"].starred = {song: [{id: "11841"}]}; - mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); + mockBackend.flush(); - var promise = subsonic.getRandomStarredSongs(); - mockBackend.flush(); + expect(promise).toBeResolved(); + expect(success).toHaveBeenCalled(); + var randomlyPickedSongs = success.calls.mostRecent().args[0]; + for (var i = 0; i < randomlyPickedSongs.length; i++) { + expect(library).toContain(randomlyPickedSongs[i]); + } + }); - expect(promise).toBeResolvedWith([{id: "11841"}]); - }); + it("and given that I have only 1 starred song in my library, when getting random starred songs, it returns my starred song", function() { + response["subsonic-response"].starred = {song: [{id: "11841"}]}; + mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); - it("and given that I don't have any starred song in my library, when getting random starred songs, it returns an error object with a message", function() { - response["subsonic-response"].starred = {song: []}; - mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); + var promise = subsonic.getRandomStarredSongs(); + mockBackend.flush(); - var promise = subsonic.getRandomStarredSongs(); - mockBackend.flush(); + expect(promise).toBeResolvedWith([{id: "11841"}]); + }); - expect(promise).toBeRejectedWith({reason: 'No starred songs found on the Subsonic server.'}); - }); - }); - }); -}); \ No newline at end of file + it("and given that I don't have any starred song in my library, when getting random starred songs, it returns an error object with a message", function() { + response["subsonic-response"].starred = {song: []}; + mockBackend.whenJSONP(url).respond(200, JSON.stringify(response)); + + var promise = subsonic.getRandomStarredSongs(); + mockBackend.flush(); + + expect(promise).toBeRejectedWith({reason: 'No starred songs found on the Subsonic server.'}); + }); + }); + }); +});