Refactors subsonic's getSongs(), adds a recursiveGetSongs()

So that the separation between subsonic-service and subsonic-controller is clear: the service makes requests and deals with $http, the controller displays, add to the player queue, etc.
I've also added a recursive version of getSongs() which enables us to play all the songs in a directory recursively, e.g. all the songs of an artist regardless of their album.

- Adds unit-tests to the controller and service for getSongs() and recursiveGetSongs()
This commit is contained in:
Hyzual 2015-05-03 22:12:16 +02:00
parent 7be524af00
commit ee25f7046e
5 changed files with 326 additions and 97 deletions

View file

@ -138,7 +138,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
}).then(function (subsonicResponse) {
if(subsonicResponse.indexes !== undefined && (subsonicResponse.indexes.index !== undefined || subsonicResponse.indexes.shortcut !== undefined)) {
// Make sure shortcut, index and each index's artist are arrays
// because Madsonic will return objects and not arrays if there is only 1 artist
// because Madsonic will return an object when there's only one element
var formattedResponse = {};
formattedResponse.shortcut = [].concat(subsonicResponse.indexes.shortcut);
formattedResponse.index = [].concat(subsonicResponse.indexes.index);
@ -164,6 +164,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
params: params
}).then(function (subsonicResponse) {
if(subsonicResponse.directory.child !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var childArray = [].concat(subsonicResponse.directory.child);
if (childArray.length > 0) {
content.song = [];
@ -203,6 +204,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
params: params
}).then(function (subsonicResponse) {
if(subsonicResponse.albumList.album !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var albumArray = [].concat(subsonicResponse.albumList.album);
if (albumArray.length > 0) {
content.song = [];
@ -253,52 +255,25 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
});
return deferred.promise;
},
getSongs: function (id, action) {
var exception = {reason: 'No songs found on the Subsonic server.'};
getSongs: function (id) {
var exception = {reason: 'This directory is empty.'};
var promise = subsonicService.subsonicRequest('getMusicDirectory.view', {
params: {
id: id
}
}).then(function (subsonicResponse) {
if(subsonicResponse.directory.child !== undefined) {
var items = [].concat(subsonicResponse.directory.child);
if (items.length > 0) {
content.selectedAlbum = id;
if (action == 'add') {
angular.forEach(items, function (item, key) {
player.queue.push(map.mapSong(item));
});
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else if (action == 'play') {
player.queue = [];
angular.forEach(items, function (item, key) {
player.queue.push(map.mapSong(item));
});
var next = player.queue[0];
player.play(next);
notifications.updateMessage(items.length + ' Song(s) Added to Queue', true);
} else {
if (subsonicResponse.directory.id != 'undefined') {
var albumId = subsonicResponse.directory.id;
var albumName = subsonicResponse.directory.name;
if (content.breadcrumb.length > 0) { content.breadcrumb.splice(1, (content.breadcrumb.length - 1)); }
content.breadcrumb.push({ 'type': 'album', 'id': albumId, 'name': albumName });
}
content.song = [];
content.album = [];
var albums = [];
angular.forEach(items, function (item, key) {
if (item.isDir) {
albums.push(map.mapAlbum(item));
} else {
content.song.push(map.mapSong(item));
}
});
if (albums.length > 0) {
content.album = albums;
}
}
return content;
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var children = [].concat(subsonicResponse.directory.child);
if (children.length > 0) {
var allChildren = _(children).partition(function (item) {
return item.isDir;
});
return {
directories: map.mapAlbums(allChildren[0]),
songs: map.mapSongs(allChildren[1])
};
}
}
// We end up here for every else
@ -307,6 +282,41 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
return promise;
},
// This is used when we add or play a directory, so we recursively get all its contents
recursiveGetSongs: function (id) {
var deferred = $q.defer();
// We first use getSongs() to get the contents of the root directory
subsonicService.getSongs(id).then(function (data) {
var directories = data.directories;
var songs = data.songs;
// If there are only songs, we return them immediately: this is a leaf directory and the end of the recursion
if (directories.length === 0) {
deferred.resolve(songs);
} else {
// otherwise, for each directory, we call ourselves
var promises = [];
angular.forEach(directories, function (dir) {
var subdirectoryRequest = subsonicService.recursiveGetSongs(dir.id).then(function (data) {
// This is where we join all the songs together in a single array
return songs.concat(data);
});
promises.push(subdirectoryRequest);
});
// since all of this is asynchronous, we need to wait for all the requests to finish by using $q.all()
var allRequestsFinished = $q.all(promises).then(function (data) {
// and since $q.all() wraps everything in another array, we use flatten() to end up with only one array of songs
return _(data).flatten();
});
deferred.resolve(allRequestsFinished);
}
}, function () {
// Even if getSongs returns an error, we resolve with an empty array. Otherwise one empty directory somewhere
// would keep us from playing all the songs of a directory recursively
deferred.resolve([]);
});
return deferred.promise;
},
search: function (query, type) {
if(_([0, 1, 2]).contains(type)) {
var promise = subsonicService.subsonicRequest('search2.view', {
@ -315,6 +325,8 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
}
}).then(function (subsonicResponse) {
if (!_.isEmpty(subsonicResponse.searchResult2)) {
// Make sure that song, album and artist are arrays using concat
// because Madsonic will return an object when there's only one element
switch (type) {
case 0:
if (subsonicResponse.searchResult2.song !== undefined) {
@ -357,6 +369,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
params: params
}).then(function (subsonicResponse) {
if(subsonicResponse.randomSongs !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var songArray = [].concat(subsonicResponse.randomSongs.song);
if (songArray.length > 0) {
return map.mapSongs(songArray);
@ -384,6 +397,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
var promise = subsonicService.getStarred()
.then(function (starred) {
if(starred.song !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var songArray = [].concat(starred.song);
if (songArray.length > 0) {
// Return random subarray of songs
@ -402,6 +416,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
var promise = subsonicService.subsonicRequest('getPlaylists.view')
.then(function (subsonicResponse) {
if(subsonicResponse.playlists.playlist !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var playlistArray = [].concat(subsonicResponse.playlists.playlist);
if (playlistArray.length > 0) {
var allPlaylists = _(playlistArray).partition(function (item) {
@ -424,6 +439,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
}
}).then(function (subsonicResponse) {
if (subsonicResponse.playlist.entry !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var entryArray = [].concat(subsonicResponse.playlist.entry);
if (entryArray.length > 0) {
return map.mapSongs(entryArray);
@ -514,6 +530,7 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
})
.then(function (subsonicResponse) {
if (subsonicResponse.podcasts !== undefined && subsonicResponse.podcasts.channel !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var channelArray = [].concat(subsonicResponse.podcasts.channel);
if (channelArray.length > 0) {
return channelArray;
@ -535,10 +552,12 @@ angular.module('jamstash.subsonic.service', ['angular-underscore/utils',
}).then(function (subsonicResponse) {
var episodes = [];
if (subsonicResponse.podcasts.channel !== undefined) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var channelArray = [].concat(subsonicResponse.podcasts.channel);
if (channelArray.length > 0) {
var channel = channelArray[0];
if (channel !== null && channel.id === id) {
// Make sure this is an array using concat because Madsonic will return an object when there's only one element
var episodesArray = [].concat(channel.episode);
episodes = _(episodesArray).filter(function (episode) {
return episode.status === "completed";

View file

@ -1,8 +1,8 @@
describe("Subsonic service -", function() {
'use strict';
var subsonic, mockBackend, mockGlobals,
response;
var subsonic, mockBackend, mockGlobals, $q,
response, url;
beforeEach(function() {
// We redefine it because in some tests we need to alter the settings
mockGlobals = {
@ -46,9 +46,10 @@ describe("Subsonic service -", function() {
});
});
inject(function (_subsonic_, $httpBackend) {
inject(function (_subsonic_, $httpBackend, _$q_) {
subsonic = _subsonic_;
mockBackend = $httpBackend;
$q = _$q_;
});
response = {"subsonic-response": {status: "ok", version: "1.10.2"}};
});
@ -59,7 +60,7 @@ describe("Subsonic service -", function() {
});
describe("subsonicRequest() -", function() {
var partialUrl, url;
var partialUrl;
beforeEach(function() {
partialUrl = '/getStarred.view';
url ='http://demo.subsonic.com/rest/getStarred.view?'+
@ -129,7 +130,6 @@ describe("Subsonic service -", function() {
});
describe("getAlbums() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getMusicDirectory.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=21'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
@ -182,62 +182,127 @@ describe("Subsonic service -", function() {
});
});
//TODO: Hyz: Rename into getDirectory(), because we don't know if there will be songs or other directories in it
describe("getSongs() -", function() {
var url;
beforeEach(function() {
var url = 'http://demo.subsonic.com/rest/getMusicDirectory.view?'+
url = 'http://demo.subsonic.com/rest/getMusicDirectory.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=209'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
});
it("Given that there were 2 songs in the given directory id in my library, when I get the songs from that directory, then a promise will be resolved with an array of 2 songs", function() {
it("Given a directory containing 2 songs and 1 subdirectory and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 2 songs and an array of 1 subdirectory", function() {
response["subsonic-response"].directory = {
child: [
{ id: 778 },
{ id: 614 }
{ id: 614 },
{ id: 205, isDir: true}
]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getSongs(209);
//TODO: Hyz: Replace with toBeResolvedWith() when getSongs() is refactored
var success = function (data) {
expect(data.album).toEqual([]);
expect(data.song).toEqual([
{ id: 778 },
{ id: 614 }
]);
};
promise.then(success);
mockBackend.flush();
expect(promise).toBeResolved();
expect(promise).toBeResolvedWith({
directories: [{ id: 205, isDir: true}],
songs: [{id: 778}, {id: 614}]
});
});
it("Given that there was only 1 song in the given directory id in my Madsonic library, when I get the songs from that directory, then a promise will be resolved with an array of 1 song", function() {
it("Given a directory containing 1 song in my Madsonic library and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 1 song and an empty array of subdirectories", function() {
response["subsonic-response"].directory = {
child: { id: 402 }
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getSongs(209);
//TODO: Hyz: Replace with toBeResolvedWith() when getSongs() is refactored
var success = function (data) {
expect(data.album).toEqual([]);
expect(data.song).toEqual([
{ id: 402 }
]);
};
promise.then(success);
mockBackend.flush();
expect(promise).toBeResolved();
expect(promise).toBeResolvedWith({
directories: [],
songs: [{id: 402}]
});
});
it("Given a directory that didn't contain anything and given its id, when I get the songs from that directory, then a promise will be rejected with an error message", function() {
response["subsonic-response"].directory = {};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getSongs(209);
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'This directory is empty.'});
});
});
describe("recursiveGetSongs() -", function() {
var deferred;
beforeEach(function() {
deferred = $q.defer();
spyOn(subsonic, 'getSongs').and.returnValue(deferred.promise);
});
it("Given a directory containing 2 songs and a subdirectory itself containing 2 songs and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 4 songs", function() {
// Mock getSongs so we are only testing the recursivity
var firstDeferred = $q.defer();
var secondDeferred = $q.defer();
subsonic.getSongs.and.callFake(function (id) {
// First call to getSongs
if (id === 499) {
return firstDeferred.promise;
// Second call to getSongs
} else if (id === 553) {
return secondDeferred.promise;
}
});
var promise = subsonic.recursiveGetSongs(499);
// On the first call to getSongs, we expect 2 songs and a subdirectory
firstDeferred.resolve({
directories: [{ id: 553, type: 'byfolder' }],
songs: [
{ id: 695 },
{ id: 227 }
]
});
// On the second call, we expect 2 songs
secondDeferred.resolve({
directories: [],
songs: [
{ id: 422 },
{ id: 171 }
]
});
expect(promise).toBeResolvedWith([
{ id: 695 },
{ id: 227 },
{ id: 422 },
{ id: 171 },
]);
});
it("Given a directory containing only 2 songs and given its id, when I get the songs from that directory, then a promise will be resolved with an array of 2 songs", function() {
var promise = subsonic.recursiveGetSongs(14);
deferred.resolve({
directories: [],
songs: [
{ id: 33 }, { id: 595 }
]
});
expect(promise).toBeResolvedWith([
{ id: 33 }, { id: 595 }
]);
});
it("Given a directory that didn't contain anything and given its id, when I get the songs from that directory, then a promise will be resolved with an empty array", function() {
var promise = subsonic.recursiveGetSongs(710);
deferred.reject({reason: 'This directory is empty.'});
expect(promise).toBeResolvedWith([]);
});
});
describe("getAlbumListBy() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getAlbumList.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&offset=0'+'&p=enc:cGFzc3dvcmQ%3D'+'&size=3&type=newest'+'&u=Hyzual&v=1.10.2';
@ -331,7 +396,6 @@ describe("Subsonic service -", function() {
});
describe("getRandomStarredSongs() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getStarred.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
@ -393,7 +457,6 @@ describe("Subsonic service -", function() {
});
describe("getRandomSongs() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getRandomSongs.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D'+'&size=3'+'&u=Hyzual&v=1.10.2';
@ -508,7 +571,6 @@ describe("Subsonic service -", function() {
});
describe("getArtists() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getIndexes.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
@ -689,7 +751,6 @@ describe("Subsonic service -", function() {
});
describe("getPlaylist() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getPlaylist.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=9123'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
@ -781,7 +842,6 @@ describe("Subsonic service -", function() {
});
describe("getPodcasts() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getPodcasts.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&includeEpisodes=false'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
@ -833,7 +893,6 @@ describe("Subsonic service -", function() {
});
describe("getPodcast() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getPodcasts.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=2695&includeEpisodes=true'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
@ -933,7 +992,6 @@ describe("Subsonic service -", function() {
});
describe("search() -", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/search2.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D'+'&query=unintersetingly'+'&u=Hyzual&v=1.10.2';

View file

@ -41,7 +41,7 @@
<div id="BreadCrumbs" class="floatleft">
<div class="crumb"><a ng-click="toggleArtists()" title="Toggle Artists">Artists</a> &gt;</div>
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'artist'}"><a ng-click="getAlbums(o.id, o.name)">{{o.name}}</a> &gt;</div>
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'album'}"><a ng-click="getSongs(o.id, '')">{{o.name}}</a> &gt;</div>
<div class="crumb" ng-repeat="o in BreadCrumbs | filter:{type:'album'}"><a ng-click="getSongs('display', o.id, o.name)">{{o.name}}</a> &gt;</div>
</div>
</div>
</li>
@ -49,10 +49,10 @@
<div class="clear"></div>
<ul class="simplelist songlist noselect">
<div class="" ng-repeat="o in album" ng-switch on="o.type">
<li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': settings.DefaultLibraryLayout.id == 'grid'}" ng-click="getSongs(o.id, '')" parentid="{{o.parentid}}">
<li class="album" ng-switch-when="byfolder" id="{{o.id}}" ng-class="{'selected': selectedAlbum == o.id, 'albumgrid': settings.DefaultLibraryLayout.id == 'grid'}" ng-click="getSongs('display', o.id, o.name)" parentid="{{o.parentid}}">
<div class="itemactions">
<a class="add hover" href="" title="Add To Play Queue" ng-click="getSongs(o.id, 'add')" stop-event="click"></a>
<a class="play hover" href="" title="Play" ng-click="getSongs(o.id, 'play')" stop-event="click"></a>
<a class="add hover" href="" title="Add To Play Queue" ng-click="getSongs('add', o.id, o.name)" stop-event="click"></a>
<a class="play hover" href="" title="Play" ng-click="getSongs('play', o.id, o.name)" stop-event="click"></a>
<a class="download hover" href="" ng-click="download(o.id)" title="Download" stop-event="click"></a>
<a title="Favorite hover" href="" ng-class="{'favorite': o.starred, 'rate': !o.starred}" ng-click="updateFavorite(o)" stop-event="click"></a>
<a class="info hover" href="" title="{{'Created: ' + o.date}}"></a>

View file

@ -279,10 +279,11 @@ angular.module('jamstash.subsonic.controller', ['jamstash.subsonic.service', 'ja
* @param {String} action the action to be taken with the songs. Must be 'add', 'play' or 'display'
* @return {Promise} the original promise passed in first param. That way we can chain it further !
*/
//TODO: Hyz: Maybe we should move this to a service
$scope.requestSongs = function (promise, action) {
$scope.handleErrors(promise)
.then(function (songs) {
if(action === 'play') {
if (action === 'play') {
player.emptyQueue().addSongs(songs).playFirstSong();
notifications.updateMessage(songs.length + ' Song(s) Added to Queue', true);
} else if (action === 'add') {
@ -300,18 +301,33 @@ angular.module('jamstash.subsonic.controller', ['jamstash.subsonic.service', 'ja
return promise;
};
$scope.getSongs = function (id, action) {
subsonic.getSongs(id, action).then(function (data) {
$scope.album = data.album;
$scope.song = data.song;
$scope.BreadCrumbs = data.breadcrumb;
$scope.selectedAutoAlbum = data.selectedAutoAlbum;
$scope.selectedArtist = data.selectedArtist;
$scope.selectedPlaylist = data.selectedPlaylist;
if ($scope.SelectedAlbumSort.id != "default") {
sortSubsonicAlbums($scope.SelectedAlbumSort.id);
}
});
$scope.getSongs = function (action, id, name) {
var promise;
if (action === 'play' || action === 'add') {
promise = subsonic.recursiveGetSongs(id);
$scope.requestSongs(promise, action);
} else if (action === 'display') {
promise = subsonic.getSongs(id);
$scope.handleErrors(promise).then(function (data) {
$scope.album = data.directories;
$scope.song = data.songs;
if ($scope.BreadCrumbs.length > 0) {
$scope.BreadCrumbs.splice(1, ($scope.BreadCrumbs.length - 1));
}
$scope.BreadCrumbs.push({'type': 'album', 'id': id, 'name': name});
$scope.selectedAutoAlbum = null;
$scope.selectedArtist = null;
$scope.selectedAlbum = id;
$scope.selectedAutoPlaylist = null;
$scope.selectedPlaylist = null;
$scope.selectedPodcast = null;
if ($scope.SelectedAlbumSort.id !== "default") {
sortSubsonicAlbums($scope.SelectedAlbumSort.id);
}
}, function (error) {
notifications.updateMessage(error.reason, true);
});
}
};
$scope.getRandomStarredSongs = function (action) {

View file

@ -22,6 +22,8 @@ describe("Subsonic controller", function() {
// Mock the subsonic service
subsonic = jasmine.createSpyObj("subsonic", [
"getSongs",
"recursiveGetSongs",
"getAlbums",
"getArtists",
"getGenres",
@ -38,6 +40,8 @@ describe("Subsonic controller", function() {
]);
// We make them return different promises and use our deferred variable only when testing
// a particular function, so that they stay isolated
subsonic.getSongs.and.returnValue($q.defer().promise);
subsonic.recursiveGetSongs.and.returnValue($q.defer().promise);
subsonic.getAlbums.and.returnValue($q.defer().promise);
subsonic.getArtists.and.returnValue($q.defer().promise);
subsonic.getGenres.and.returnValue($q.defer().promise);
@ -80,6 +84,138 @@ describe("Subsonic controller", function() {
scope.selectedPlaylist = null;
});
describe("getSongs -", function() {
beforeEach(function() {
scope.BreadCrumbs = [];
scope.SelectedAlbumSort= {
id: "default"
};
subsonic.getSongs.and.returnValue(deferred.promise);
subsonic.recursiveGetSongs.and.returnValue(deferred.promise);
spyOn(scope, "requestSongs").and.returnValue(deferred.promise);
});
it("Given a music directory that contained 3 songs and given its id and name, when I display it, then subsonic-service will be called, the breadcrumbs will be updated and the songs will be published to the scope", function() {
scope.getSongs('display', 87, 'Covetous Dadayag');
deferred.resolve({
directories: [],
songs: [
{ id: 660 },
{ id: 859 },
{ id: 545 }
]
});
scope.$apply();
expect(subsonic.getSongs).toHaveBeenCalledWith(87);
expect(scope.album).toEqual([]);
expect(scope.song).toEqual([
{ id: 660 },
{ id: 859 },
{ id: 545 }
]);
expect(scope.BreadCrumbs).toEqual([{
type: 'album',
id: 87,
name: 'Covetous Dadayag'
}]);
expect(scope.selectedAutoAlbum).toBeNull();
expect(scope.selectedArtist).toBeNull();
expect(scope.selectedAlbum).toBe(87);
expect(scope.selectedAutoPlaylist).toBeNull();
expect(scope.selectedPlaylist).toBeNull();
expect(scope.selectedPodcast).toBeNull();
});
it("Given that there was a previous level in the breadcrumbs, when I display a music directory, then the album breadcrumb will be added", function() {
scope.BreadCrumbs = [
{
type: 'artist',
id: 73,
name: 'Evan Mestanza'
}
];
scope.getSongs('display', 883, 'Pitiedly preutilizable');
deferred.resolve({
directories: [],
songs: []
});
scope.$apply();
expect(scope.BreadCrumbs).toEqual([
{
type: 'artist',
id: 73,
name: 'Evan Mestanza'
}, {
type: 'album',
id: 883,
name: 'Pitiedly preutilizable'
}
]);
});
it("Given a music directory that contained 2 songs and 1 subdirectory and given its id and name, when I display it, then subsonic-service will be called, the songs and directory will be published to the scope", function() {
scope.getSongs('display', 6, 'Potsander dormilona');
deferred.resolve({
directories: [{id: 387, type: 'byfolder'}],
songs: [
{ id: 102 },
{ id: 340 }
]
});
scope.$apply();
expect(scope.album).toEqual([
{id: 387, type: 'byfolder'}
]);
expect(scope.song).toEqual([
{ id: 102 },
{ id: 340 }
]);
});
it("Given a music directory, when I display it, then handleErrors will handle HTTP and Subsonic errors", function() {
spyOn(scope, 'handleErrors').and.returnValue(deferred.promise);
scope.getSongs('display', 88);
expect(scope.handleErrors).toHaveBeenCalledWith(deferred.promise);
});
it("Given a music directory that didn't contain anything, when I display it, then an error notification will be displayed", function() {
scope.getSongs('display', 214, 'Upside bunemost');
deferred.reject({reason: 'This directory is empty.'});
scope.$apply();
expect(notifications.updateMessage).toHaveBeenCalledWith('This directory is empty.', true);
});
it("Given a music directory that contained 3 songs and given its id, when I add it to the playing queue, then requestSongs() will be called", function() {
scope.getSongs('add', 720);
deferred.resolve([
{ id: 927 },
{ id: 598 },
{ id: 632 }
]);
scope.$apply();
expect(subsonic.recursiveGetSongs).toHaveBeenCalledWith(720);
expect(scope.requestSongs).toHaveBeenCalledWith(deferred.promise, 'add');
});
it("Given a music directory that contained 3 songs and given its id, when I play it, then requestSongs() will be called", function() {
scope.getSongs('play', 542);
deferred.resolve([
{ id: 905 },
{ id: 181 },
{ id: 880 }
]);
scope.$apply();
expect(subsonic.recursiveGetSongs).toHaveBeenCalledWith(542);
expect(scope.requestSongs).toHaveBeenCalledWith(deferred.promise, 'play');
});
});
describe("Given that my library contained 3 songs, ", function() {
var response;
beforeEach(function() {