Jamstash/app/subsonic/subsonic-service_test.js
Hyzual ef57aac043 Refactors subsonic-service's getPodcast() to use subsonicRequest()
So we can move closer to consistent error-reporting and protocol usage.
- refactors subonic-controller's getPodcast().
- Also sets all the selected things (playlist, album, etc.) to null.
- Adds a mapPodcasts() method to model-service.js. Just as mapSongs(), it maps an array of podcast episodes and returns it.
- Adds unit-tests.
2015-03-24 18:35:48 +01:00

651 lines
27 KiB
JavaScript

describe("Subsonic service -", function() {
'use strict';
var subsonic, mockBackend, mockGlobals;
var response;
beforeEach(function() {
// We redefine it because in some tests we need to alter the settings
mockGlobals = {
settings: {
AutoPlaylistSize: 3,
Username: "Hyzual",
Password: "enc:cGFzc3dvcmQ=",
Protocol: "jsonp",
ApiVersion: "1.10.2",
ApplicationName: "Jamstash",
Timeout: 20000
},
// TODO: Hyz: Remove it when everything is refactored
BaseURL: function () {
return 'http://demo.subsonic.com/rest';
}
};
module('jamstash.subsonic.service', function ($provide) {
$provide.value('globals', mockGlobals);
// Mock the model service
$provide.decorator('map', function ($delegate) {
$delegate.mapSong = function (argument) {
return argument;
};
$delegate.mapPodcast = function (argument) {
return argument;
};
return $delegate;
});
// Mock utils.getValue
$provide.decorator('utils', function ($delegate) {
$delegate.getValue = function () {
return undefined;
};
return $delegate;
});
});
inject(function (_subsonic_, $httpBackend) {
subsonic = _subsonic_;
mockBackend = $httpBackend;
});
response = {"subsonic-response": {status: "ok", version: "1.10.2"}};
});
afterEach(function() {
mockBackend.verifyNoOutstandingExpectation();
mockBackend.verifyNoOutstandingRequest();
});
describe("subsonicRequest -", function() {
var partialUrl, url;
beforeEach(function() {
partialUrl = '/getStarred.view';
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';
});
it("Given that the Subsonic server is not responding, when I make a request to Subsonic it returns an error object with a message", function() {
mockBackend.expectJSONP(url).respond(503, 'Service Unavailable');
var promise = subsonic.subsonicRequest(partialUrl);
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'Error when contacting the Subsonic server.', httpError: 503});
});
it("Given a missing parameter, when I make a request to Subsonic it returns an error object with a message", function() {
delete mockGlobals.settings.Password;
var missingPasswordUrl = 'http://demo.subsonic.com/rest/getStarred.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&u=Hyzual&v=1.10.2';
var errorResponse = {"subsonic-response" : {
"status" : "failed",
"version" : "1.10.2",
"error" : {"code" : 10,"message" : "Required parameter is missing."}
}};
mockBackend.expectJSONP(missingPasswordUrl).respond(JSON.stringify(errorResponse));
var promise = subsonic.subsonicRequest(partialUrl);
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'Error when contacting the Subsonic server.', subsonicError: {code: 10, message:'Required parameter is missing.'}});
});
it("Given a partialUrl that does not start with '/', it adds '/' before it and makes a correct request", function() {
partialUrl = 'getStarred.view';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
subsonic.subsonicRequest(partialUrl);
mockBackend.flush();
});
it("Given $http config params, it does not overwrite them", function() {
partialUrl = 'scrobble.view';
url ='http://demo.subsonic.com/rest/scrobble.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&id=75&p=enc:cGFzc3dvcmQ%3D&submission=false&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
subsonic.subsonicRequest(partialUrl, {
params: {
id: 75,
submission: false
}
});
mockBackend.flush();
});
it("Given that the global protocol setting is 'json', when I make a request to Subsonic it uses GET and does not use the JSON_CALLBACK parameter", function() {
mockGlobals.settings.Protocol = 'json';
var getUrl = 'http://demo.subsonic.com/rest/getStarred.view?'+
'c=Jamstash&f=json&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
mockBackend.expectGET(getUrl).respond(JSON.stringify(response));
var promise = subsonic.subsonicRequest(partialUrl);
mockBackend.flush();
expect(promise).toBeResolvedWith({status: "ok", version: "1.10.2"});
});
});
describe("getStarred -", 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';
});
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.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getStarred();
mockBackend.flush();
expect(promise).toBeResolvedWith({
artist: [{id: 2245}],
album: [{id: 1799},{id: 20987}],
song: [{id: 2478},{id: 14726},{id: 742}]
});
});
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.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getStarred();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'Nothing is starred on the Subsonic server.'});
});
});
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';
});
describe("Given that the global setting AutoPlaylist Size is 3", function() {
it("and given that I have more than 3 starred songs in my library, when getting random starred songs, it returns 3 starred songs", function() {
var library = [
{id: 11841},{id: 12061},{id: 17322},{id: 1547},{id: 14785}
];
response["subsonic-response"].starred = {song: library};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
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);
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]);
}
});
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.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomStarredSongs();
mockBackend.flush();
expect(promise).toBeResolvedWith([{id: 11841}]);
});
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.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomStarredSongs();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'No starred songs found on the Subsonic server.'});
});
});
});
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';
});
describe("Given that the global setting AutoPlaylist Size is 3", function() {
it("and given that I have more than 3 songs in my library, when getting random songs, it returns 3 songs", function() {
var library = [
{id: 1143},{id: 5864},{id: 7407},{id: 6471},{id: 59}
];
response["subsonic-response"].randomSongs = {song: library};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomSongs();
// We create a spy in order to get the results of the promise
var success = jasmine.createSpy("success");
promise.then(success);
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]);
}
});
it("and given that I have only 1 song in my library, when getting random songs, it returns that song", function() {
response["subsonic-response"].randomSongs = {song: [{id: 7793}]};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomSongs();
mockBackend.flush();
expect(promise).toBeResolvedWith([{id: 7793}]);
});
it("and given that I don't have any song in my library, when getting random songs, it returns an error object with a message", function() {
response["subsonic-response"].randomSongs = {song: []};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomSongs();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'No songs found on the Subsonic server.'});
});
it("and given a genre, when getting random songs, it returns 3 songs from the given genre", function() {
url = 'http://demo.subsonic.com/rest/getRandomSongs.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&genre=Rock'+'&p=enc:cGFzc3dvcmQ%3D'+'&size=3'+'&u=Hyzual&v=1.10.2';
var library = [
{id: 9408},{id: 9470},{id: 6932}
];
response["subsonic-response"].randomSongs = {song: library};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomSongs('Rock');
mockBackend.flush();
expect(promise).toBeResolvedWith([{id: 9408},{id: 9470},{id: 6932}]);
});
it("and given a folder id, when getting random songs, it returns 3 songs from the given folder", function() {
url = 'http://demo.subsonic.com/rest/getRandomSongs.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&musicFolderId=2'+'&p=enc:cGFzc3dvcmQ%3D'+'&size=3'+'&u=Hyzual&v=1.10.2';
var library = [
{id: 9232},{id: 3720},{id: 8139}
];
response["subsonic-response"].randomSongs = {song: library};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getRandomSongs('', 2);
mockBackend.flush();
expect(promise).toBeResolvedWith([{id: 9232},{id: 3720},{id: 8139}]);
});
});
});
it("ping - when I ping Subsonic, it returns Subsonic's response, containing its REST API version", function() {
var url = 'http://demo.subsonic.com/rest/ping.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.ping();
mockBackend.flush();
expect(promise).toBeResolvedWith({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?' +
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=45872'+'&p=enc:cGFzc3dvcmQ%3D'+'&submisssion=true'+'&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.scrobble(song);
mockBackend.flush();
expect(promise).toBeResolvedWith(true);
});
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';
});
it("Given that I have 2 artists at the top level, when I get the artists, it returns those 2 artists", function() {
response["subsonic-response"].indexes = {
shortcut: [
{
id: "8534",
name: "Podcast"
}
],
index: [
{
name : "R",
artist: [
{
id: "5493",
name: "Ricki Perish"
}
]
},
{
name : "T",
artist: [
{
id: "4934",
name: "Terese Hoth"
}
]
}
]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getArtists();
mockBackend.flush();
expect(promise).toBeResolvedWith(response["subsonic-response"].indexes);
});
it("When I get the artists of a given folder, it builds the correct url", function() {
var url = 'http://demo.subsonic.com/rest/getIndexes.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&musicFolderId=42'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
subsonic.getArtists(42);
mockBackend.flush();
});
it("Given that I don't have any artist or shortcut in my library (empty server), when I get the artists, it returns an error object with a message", function() {
response["subsonic-response"].indexes = {};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getArtists();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'No artist found on the Subsonic server.'});
});
});
describe("getPlaylists - ", function() {
var url, myPlaylist, publicPlaylist;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getPlaylists.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
publicPlaylist = {
id: "563",
owner: "Rima Zentz"
};
myPlaylist = {
id: "829",
owner: "Hyzual"
};
});
it("Given that there is 1 public playlist and 1 playlist that I own in my library, when I get the playlists, it returns them separated between my playlists and public playlists", function() {
response["subsonic-response"].playlists = {
playlist: [ publicPlaylist, myPlaylist ]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPlaylists();
mockBackend.flush();
expect(promise).toBeResolvedWith({
playlists: [ myPlaylist ],
playlistsPublic: [ publicPlaylist ]
});
});
it("Given that there is only 1 playlist in my library and that I own it, when I get the playlists, it returns my playlist and an empty array for public playlists", function() {
response["subsonic-response"].playlists = {
playlist: [ myPlaylist ]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPlaylists();
mockBackend.flush();
expect(promise).toBeResolvedWith({
playlists: [ myPlaylist ],
playlistsPublic: []
});
});
it("Given that there is only 1 public playlist in my library and that I don't own it, when I get the playlists, it returns an empty array and the public playlist", function() {
response["subsonic-response"].playlists = {
playlist: [ publicPlaylist ]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPlaylists();
mockBackend.flush();
expect(promise).toBeResolvedWith({
playlists: [],
playlistsPublic: [ publicPlaylist ]
});
});
it("Given that I don't have any playlist in my library, when I get the playlists, it returns an error object with a message", function() {
response["subsonic-response"].playlists = {};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPlaylists();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'No playlist found on the Subsonic server.'});
});
});
describe("getPlaylist - ", function() {
var url;
beforeEach(function() {
url = 'http://demo.subsonic.com/rest/getPlaylist.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
});
it("Given a playlist with 2 songs in it, when I get it, it returns the 2 songs of the playlist", 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';
response["subsonic-response"].playlist = {
id: 9123,
entry: [
{ id: 2860 },
{ id: 4762 }
]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPlaylist(9123);
mockBackend.flush();
expect(promise).toBeResolvedWith([
{ id: 2860 },
{ id: 4762 }
]);
});
it("Given that the playlist I want to get is empty (0 songs in it), when I get it, it returns an error object with a message", function() {
response["subsonic-response"].playlist = {};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPlaylist();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'This playlist is empty.'});
});
});
it("Given a name, when I create a new playlist, an empty resolved promise will be returned", function() {
var url = 'http://demo.subsonic.com/rest/createPlaylist.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&name=Apolloship'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.newPlaylist('Apolloship');
mockBackend.flush();
expect(promise).toBeResolved();
});
it("Given a playlist id, when I delete that playlist, an empty resolved promise will be returned", function() {
var url = 'http://demo.subsonic.com/rest/deletePlaylist.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp'+'&id=7579'+'&p=enc:cGFzc3dvcmQ%3D&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.deletePlaylist(7579);
mockBackend.flush();
expect(promise).toBeResolved();
});
it("Given an array of songs and a playlist id, when I save that playlist, an empty resolved promise will be returned", function() {
var url = 'http://demo.subsonic.com/rest/createPlaylist.view?'+
'c=Jamstash&callback=JSON_CALLBACK&f=jsonp&p=enc:cGFzc3dvcmQ%3D'+'&playlistId=7071'+
'&songId=2801&songId=1002&songId=6612'+
'&u=Hyzual&v=1.10.2';
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var songs = [
{ id: 2801 },
{ id: 1002 },
{ id: 6612 }
];
var promise = subsonic.savePlaylist(7071, songs);
mockBackend.flush();
expect(promise).toBeResolved();
});
describe("When I load the podcasts,", 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';
});
it("given that there were podcasts in the library, then a promise will be resolved with an array of podcasts", function() {
response["subsonic-response"].podcasts = {
channel: [
{ id: 7820 },
{ id: 5174 },
{ id: 2404 }
]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPodcasts();
mockBackend.flush();
expect(promise).toBeResolvedWith([
{ id: 7820 },
{ id: 5174 },
{ id: 2404 }
]);
});
it("given that there weren't any podcast in the library, then a promise will be rejected with an error message", function() {
response["subsonic-response"].podcasts = {};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPodcasts();
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'No podcast found on the Subsonic server.'});
});
});
describe("When I load a podcast,", 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';
});
it("given a podcast id, then a promise will be resolved with an array of songs from all the non-skipped episodes of that podcast", function() {
response["subsonic-response"].podcasts = {
channel: [
{
id: 2695,
episode: [
{
id: 691,
status: "completed"
}, {
id: 771,
status: "skipped"
}, {
id: 227,
status: "completed"
}
]
}
]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPodcast(2695);
mockBackend.flush();
expect(promise).toBeResolvedWith([
{
id: 691,
status: "completed"
}, {
id: 227,
status: "completed"
}
]);
});
it("given that the podcast I wanted to get didn't exist in the library, when I try to get it, then a promise will be rejected with an error message", function() {
response["subsonic-response"].podcasts = {};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPodcast(2695);
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'This podcast was not found on the Subsonic server.'});
});
it("given that the podcast I wanted to get was empty (0 non-skipped episode in it), when I get it, then a promise will be rejected with an error message", function() {
response["subsonic-response"].podcasts = {
channel: [
{
id: 2695,
episode: [
{
id: 678,
status: "skipped"
},
{
id: 972,
status: "skipped"
}
]
}
]
};
mockBackend.expectJSONP(url).respond(JSON.stringify(response));
var promise = subsonic.getPodcast(2695);
mockBackend.flush();
expect(promise).toBeRejectedWith({reason: 'No downloaded episode found for this podcast. Please check the podcast settings.'});
});
});
});