Merge pull request #744 from jodal/feature/js-updates
Various updates to Mopidy.js
This commit is contained in:
commit
af238d5259
131
docs/api/js.rst
131
docs/api/js.rst
@ -74,7 +74,7 @@ development setup in the ``js/`` dir in our repo. The instructions in
|
|||||||
Creating an instance
|
Creating an instance
|
||||||
====================
|
====================
|
||||||
|
|
||||||
Once you got Mopidy.js loaded, you need to create an instance of the wrapper:
|
Once you have Mopidy.js loaded, you need to create an instance of the wrapper:
|
||||||
|
|
||||||
.. code-block:: js
|
.. code-block:: js
|
||||||
|
|
||||||
@ -100,6 +100,31 @@ later:
|
|||||||
// ... do other stuff, like hooking up events ...
|
// ... do other stuff, like hooking up events ...
|
||||||
mopidy.connect();
|
mopidy.connect();
|
||||||
|
|
||||||
|
When creating an instance, you can specify the following settings:
|
||||||
|
|
||||||
|
``autoConnect``
|
||||||
|
Whether or not to connect to the WebSocket on instance creation. Defaults
|
||||||
|
to true.
|
||||||
|
|
||||||
|
``backoffDelayMin``
|
||||||
|
The minimum number of milliseconds to wait after a connection error before
|
||||||
|
we try to reconnect. For every failed attempt, the backoff delay is doubled
|
||||||
|
until it reaches ``backoffDelayMax``. Defaults to 1000.
|
||||||
|
|
||||||
|
``backoffDelayMax``
|
||||||
|
The maximum number of milliseconds to wait after a connection error before
|
||||||
|
we try to reconnect. Defaults to 64000.
|
||||||
|
|
||||||
|
``webSocket``
|
||||||
|
An existing WebSocket object to be used instead of creating a new
|
||||||
|
WebSocket. Defaults to undefined.
|
||||||
|
|
||||||
|
``webSocketUrl``
|
||||||
|
URL used when creating new WebSocket objects. Defaults to
|
||||||
|
``ws://<document.location.host>/mopidy/ws``, or
|
||||||
|
``ws://localhost/mopidy/ws`` if ``document.location.host`` isn't
|
||||||
|
available, like it is in the browser environment.
|
||||||
|
|
||||||
|
|
||||||
Hooking up to events
|
Hooking up to events
|
||||||
====================
|
====================
|
||||||
@ -145,7 +170,8 @@ Once your Mopidy.js object has connected to the Mopidy server and emits the
|
|||||||
|
|
||||||
Any calls you make before the ``state:online`` event is emitted will fail. If
|
Any calls you make before the ``state:online`` event is emitted will fail. If
|
||||||
you've hooked up an errback (more on that a bit later) to the promise returned
|
you've hooked up an errback (more on that a bit later) to the promise returned
|
||||||
from the call, the errback will be called with an error message.
|
from the call, the errback will be called with a ``Mopidy.ConnectionError``
|
||||||
|
instance.
|
||||||
|
|
||||||
All methods in Mopidy's :ref:`core-api` is available via Mopidy.js. The core
|
All methods in Mopidy's :ref:`core-api` is available via Mopidy.js. The core
|
||||||
API attributes is *not* available, but that shouldn't be a problem as we've
|
API attributes is *not* available, but that shouldn't be a problem as we've
|
||||||
@ -204,22 +230,34 @@ Instead, typical usage will look like this:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
mopidy.playback.getCurrentTrack().then(
|
mopidy.playback.getCurrentTrack()
|
||||||
printCurrentTrack, console.error.bind(console));
|
.done(printCurrentTrack);
|
||||||
|
|
||||||
The first function passed to ``then()``, ``printCurrentTrack``, is the callback
|
The function passed to ``done()``, ``printCurrentTrack``, is the callback
|
||||||
that will be called if the method call succeeds. The second function,
|
that will be called if the method call succeeds. If anything goes wrong,
|
||||||
``console.error``, is the errback that will be called if anything goes wrong.
|
``done()`` will throw an exception.
|
||||||
If you don't hook up an errback, debugging will be hard as errors will silently
|
|
||||||
go missing.
|
|
||||||
|
|
||||||
For debugging, you may be interested in errors from function without
|
If you want to explicitly handle any errors and avoid an exception being
|
||||||
interesting return values as well. In that case, you can pass ``null`` as the
|
thrown, you can register an error handler function anywhere in a promise
|
||||||
callback:
|
chain. The function will be called with the error object as the only argument:
|
||||||
|
|
||||||
.. code-block:: js
|
.. code-block:: js
|
||||||
|
|
||||||
mopidy.playback.next().then(null, console.error.bind(console));
|
mopidy.playback.getCurrentTrack()
|
||||||
|
.catch(console.error.bind(console));
|
||||||
|
.done(printCurrentTrack);
|
||||||
|
|
||||||
|
You can also register the error handler at the end of the promise chain by
|
||||||
|
passing it as the second argument to ``done()``:
|
||||||
|
|
||||||
|
.. code-block:: js
|
||||||
|
|
||||||
|
mopidy.playback.getCurrentTrack()
|
||||||
|
.done(printCurrentTrack, console.error.bind(console));
|
||||||
|
|
||||||
|
If you don't hook up an error handler function and never call ``done()`` on the
|
||||||
|
promise object, when.js will log warnings to the console that you have
|
||||||
|
unhandled errors. In general, unhandled errors will not go silently missing.
|
||||||
|
|
||||||
The promise objects returned by Mopidy.js adheres to the `CommonJS Promises/A
|
The promise objects returned by Mopidy.js adheres to the `CommonJS Promises/A
|
||||||
<http://wiki.commonjs.org/wiki/Promises/A>`_ standard. We use the
|
<http://wiki.commonjs.org/wiki/Promises/A>`_ standard. We use the
|
||||||
@ -283,44 +321,38 @@ Example to get started with
|
|||||||
|
|
||||||
.. code-block:: js
|
.. code-block:: js
|
||||||
|
|
||||||
var consoleError = console.error.bind(console);
|
|
||||||
|
|
||||||
var trackDesc = function (track) {
|
var trackDesc = function (track) {
|
||||||
return track.name + " by " + track.artists[0].name +
|
return track.name + " by " + track.artists[0].name +
|
||||||
" from " + track.album.name;
|
" from " + track.album.name;
|
||||||
};
|
};
|
||||||
|
|
||||||
var queueAndPlayFirstPlaylist = function () {
|
var queueAndPlay = function (playlistNum, trackNum) {
|
||||||
mopidy.playlists.getPlaylists().then(function (playlists) {
|
playlistNum = playlistNum || 0;
|
||||||
var playlist = playlists[0];
|
trackNum = trackNum || 0;
|
||||||
|
mopidy.playlists.getPlaylists().done(function (playlists) {
|
||||||
|
var playlist = playlists[playlistNum];
|
||||||
console.log("Loading playlist:", playlist.name);
|
console.log("Loading playlist:", playlist.name);
|
||||||
mopidy.tracklist.add(playlist.tracks).then(function (tlTracks) {
|
mopidy.tracklist.add(playlist.tracks).done(function (tlTracks) {
|
||||||
mopidy.playback.play(tlTracks[0]).then(function () {
|
mopidy.playback.play(tlTracks[trackNum]).done(function () {
|
||||||
mopidy.playback.getCurrentTrack().then(function (track) {
|
mopidy.playback.getCurrentTrack().done(function (track) {
|
||||||
console.log("Now playing:", trackDesc(track));
|
console.log("Now playing:", trackDesc(track));
|
||||||
}, consoleError);
|
});
|
||||||
}, consoleError);
|
});
|
||||||
}, consoleError);
|
});
|
||||||
}, consoleError);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var mopidy = new Mopidy(); // Connect to server
|
var mopidy = new Mopidy(); // Connect to server
|
||||||
mopidy.on(console.log.bind(console)); // Log all events
|
mopidy.on(console.log.bind(console)); // Log all events
|
||||||
mopidy.on("state:online", queueAndPlayFirstPlaylist);
|
mopidy.on("state:online", queueAndPlay);
|
||||||
|
|
||||||
Approximately the same behavior in a more functional style, using chaining
|
Approximately the same behavior in a more functional style, using chaining
|
||||||
of promisies.
|
of promises.
|
||||||
|
|
||||||
.. code-block:: js
|
.. code-block:: js
|
||||||
|
|
||||||
var consoleError = console.error.bind(console);
|
var get = function (key, object) {
|
||||||
|
return object[key];
|
||||||
var getFirst = function (list) {
|
|
||||||
return list[0];
|
|
||||||
};
|
|
||||||
|
|
||||||
var extractTracks = function (playlist) {
|
|
||||||
return playlist.tracks;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
var printTypeAndName = function (model) {
|
var printTypeAndName = function (model) {
|
||||||
@ -339,33 +371,36 @@ Example to get started with
|
|||||||
// By returning any arguments we get, the function can be inserted
|
// By returning any arguments we get, the function can be inserted
|
||||||
// anywhere in the chain.
|
// anywhere in the chain.
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
return mopidy.playback.getCurrentTrack().then(function (track) {
|
return mopidy.playback.getCurrentTrack()
|
||||||
console.log("Now playing:", trackDesc(track));
|
.done(function (track) {
|
||||||
return args;
|
console.log("Now playing:", trackDesc(track));
|
||||||
});
|
return args;
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var queueAndPlayFirstPlaylist = function () {
|
var queueAndPlay = function (playlistNum, trackNum) {
|
||||||
|
playlistNum = playlistNum || 0;
|
||||||
|
trackNum = trackNum || 0;
|
||||||
mopidy.playlists.getPlaylists()
|
mopidy.playlists.getPlaylists()
|
||||||
// => list of Playlists
|
// => list of Playlists
|
||||||
.then(getFirst, consoleError)
|
.fold(get, playlistNum)
|
||||||
// => Playlist
|
// => Playlist
|
||||||
.then(printTypeAndName, consoleError)
|
.then(printTypeAndName)
|
||||||
// => Playlist
|
// => Playlist
|
||||||
.then(extractTracks, consoleError)
|
.fold(get, 'tracks')
|
||||||
// => list of Tracks
|
// => list of Tracks
|
||||||
.then(mopidy.tracklist.add, consoleError)
|
.then(mopidy.tracklist.add)
|
||||||
// => list of TlTracks
|
// => list of TlTracks
|
||||||
.then(getFirst, consoleError)
|
.fold(get, trackNum)
|
||||||
// => TlTrack
|
// => TlTrack
|
||||||
.then(mopidy.playback.play, consoleError)
|
.then(mopidy.playback.play)
|
||||||
// => null
|
// => null
|
||||||
.then(printNowPlaying, consoleError);
|
.done(printNowPlaying, console.error.bind(console));
|
||||||
};
|
};
|
||||||
|
|
||||||
var mopidy = new Mopidy(); // Connect to server
|
var mopidy = new Mopidy(); // Connect to server
|
||||||
mopidy.on(console.log.bind(console)); // Log all events
|
mopidy.on(console.log.bind(console)); // Log all events
|
||||||
mopidy.on("state:online", queueAndPlayFirstPlaylist);
|
mopidy.on("state:online", queueAndPlay);
|
||||||
|
|
||||||
9. The web page should now queue and play your first playlist every time your
|
9. The web page should now queue and play your first playlist every time your
|
||||||
load it. See the browser's console for output from the function, any errors,
|
load it. See the browser's console for output from the function, any errors,
|
||||||
|
|||||||
@ -78,6 +78,20 @@ Feature release.
|
|||||||
Mopidy's HTTP server among other Zeroconf-published HTTP servers on the
|
Mopidy's HTTP server among other Zeroconf-published HTTP servers on the
|
||||||
local network.
|
local network.
|
||||||
|
|
||||||
|
- Update Mopidy.js to use when.js 3. If you maintain a Mopidy client, you
|
||||||
|
should review the `differences between when.js 2 and 3
|
||||||
|
<https://github.com/cujojs/when/blob/master/docs/api.md#upgrading-to-30-from-2x>`_
|
||||||
|
and the `when.js debugging guide
|
||||||
|
<https://github.com/cujojs/when/blob/master/docs/api.md#debugging-promises>`_.
|
||||||
|
This version has been released to npm as Mopidy.js v0.3.0.
|
||||||
|
|
||||||
|
- All of Mopidy.js' promise rejection values are now of the Error type. This
|
||||||
|
ensures that all JavaScript VMs will show a useful stack trace if a rejected
|
||||||
|
promise's value is used to throw an exception. To allow catch clauses to
|
||||||
|
handle different errors differently, server side errors are of the type
|
||||||
|
``Mopidy.ServerError``, and connection related errors are of the type
|
||||||
|
``Mopidy.ConnectionError``.
|
||||||
|
|
||||||
**MPD frontend**
|
**MPD frontend**
|
||||||
|
|
||||||
- Proper command tokenization for MPD requests. This replaces the old regex
|
- Proper command tokenization for MPD requests. This replaces the old regex
|
||||||
|
|||||||
@ -26,7 +26,7 @@ module.exports = function (grunt) {
|
|||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
postBundleCB: function (err, src, next) {
|
postBundleCB: function (err, src, next) {
|
||||||
next(null, grunt.template.process("<%= meta.banner %>") + src);
|
next(err, grunt.template.process("<%= meta.banner %>") + src);
|
||||||
},
|
},
|
||||||
standalone: "Mopidy"
|
standalone: "Mopidy"
|
||||||
}
|
}
|
||||||
@ -45,7 +45,7 @@ module.exports = function (grunt) {
|
|||||||
},
|
},
|
||||||
options: {
|
options: {
|
||||||
postBundleCB: function (err, src, next) {
|
postBundleCB: function (err, src, next) {
|
||||||
next(null, grunt.template.process("<%= meta.banner %>") + src);
|
next(err, grunt.template.process("<%= meta.banner %>") + src);
|
||||||
},
|
},
|
||||||
standalone: "Mopidy"
|
standalone: "Mopidy"
|
||||||
}
|
}
|
||||||
|
|||||||
22
js/README.md
22
js/README.md
@ -47,13 +47,9 @@ See the [Mopidy.js documentation](http://docs.mopidy.com/en/latest/api/js/).
|
|||||||
Building from source
|
Building from source
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
1. Install [Node.js](http://nodejs.org/) and npm. There is a PPA if you're
|
1. Install [Node.js](http://nodejs.org/) and npm. If you're running Ubuntu:
|
||||||
running Ubuntu:
|
|
||||||
|
|
||||||
sudo apt-get install python-software-properties
|
sudo apt-get install nodejs-legacy npm
|
||||||
sudo add-apt-repository ppa:chris-lea/node.js
|
|
||||||
sudo apt-get update
|
|
||||||
sudo apt-get install nodejs
|
|
||||||
|
|
||||||
2. Enter the `js/` in Mopidy's Git repo dir and install all dependencies:
|
2. Enter the `js/` in Mopidy's Git repo dir and install all dependencies:
|
||||||
|
|
||||||
@ -84,6 +80,20 @@ To run other [grunt](http://gruntjs.com/) targets which isn't predefined in
|
|||||||
Changelog
|
Changelog
|
||||||
---------
|
---------
|
||||||
|
|
||||||
|
### 0.3.0 (UNRELEASED)
|
||||||
|
|
||||||
|
- Upgrade to when.js 3, which brings great performance improvements and better
|
||||||
|
debugging facilities. If you maintain a Mopidy client, you should review the
|
||||||
|
[differences between when.js 2 and 3](https://github.com/cujojs/when/blob/master/docs/api.md#upgrading-to-30-from-2x)
|
||||||
|
and the
|
||||||
|
[when.js debugging guide](https://github.com/cujojs/when/blob/master/docs/api.md#debugging-promises).
|
||||||
|
|
||||||
|
- All promise rejection values are now of the Error type. This ensures that all
|
||||||
|
JavaScript VMs will show a useful stack trace if a rejected promise's value
|
||||||
|
is used to throw an exception. To allow catch clauses to handle different
|
||||||
|
errors differently, server side errors are of the type `Mopidy.ServerError`,
|
||||||
|
and connection related errors are of the type `Mopidy.ConnectionError`.
|
||||||
|
|
||||||
### 0.2.0 (2014-01-04)
|
### 0.2.0 (2014-01-04)
|
||||||
|
|
||||||
- **Backwards incompatible change for Node.js users:**
|
- **Backwards incompatible change for Node.js users:**
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "mopidy",
|
"name": "mopidy",
|
||||||
"version": "0.2.0",
|
"version": "0.3.0",
|
||||||
"description": "Client lib for controlling a Mopidy music server over a WebSocket",
|
"description": "Client lib for controlling a Mopidy music server over a WebSocket",
|
||||||
"homepage": "http://www.mopidy.com/",
|
"homepage": "http://www.mopidy.com/",
|
||||||
"author": {
|
"author": {
|
||||||
@ -16,17 +16,18 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bane": "~1.1.0",
|
"bane": "~1.1.0",
|
||||||
"faye-websocket": "~0.7.2",
|
"faye-websocket": "~0.7.2",
|
||||||
"when": "~2.7.1"
|
"when": "~3.2.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"buster": "~0.7.8",
|
"buster": "~0.7.13",
|
||||||
"grunt": "~0.4.2",
|
"browserify": "~3",
|
||||||
|
"grunt": "~0.4.5",
|
||||||
"grunt-buster": "~0.3.1",
|
"grunt-buster": "~0.3.1",
|
||||||
"grunt-browserify": "~1.3.0",
|
"grunt-browserify": "~1.3.2",
|
||||||
"grunt-contrib-jshint": "~0.8.0",
|
"grunt-contrib-jshint": "~0.10.0",
|
||||||
"grunt-contrib-uglify": "~0.2.7",
|
"grunt-contrib-uglify": "~0.5.0",
|
||||||
"grunt-contrib-watch": "~0.5.3",
|
"grunt-contrib-watch": "~0.6.1",
|
||||||
"phantomjs": "~1.9.2-6"
|
"phantomjs": "~1.9.7-8"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "grunt test",
|
"test": "grunt test",
|
||||||
|
|||||||
@ -24,13 +24,27 @@ function Mopidy(settings) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mopidy.ConnectionError = function (message) {
|
||||||
|
this.name = "ConnectionError";
|
||||||
|
this.message = message;
|
||||||
|
};
|
||||||
|
Mopidy.ConnectionError.prototype = new Error();
|
||||||
|
Mopidy.ConnectionError.prototype.constructor = Mopidy.ConnectionError;
|
||||||
|
|
||||||
|
Mopidy.ServerError = function (message) {
|
||||||
|
this.name = "ServerError";
|
||||||
|
this.message = message;
|
||||||
|
};
|
||||||
|
Mopidy.ServerError.prototype = new Error();
|
||||||
|
Mopidy.ServerError.prototype.constructor = Mopidy.ServerError;
|
||||||
|
|
||||||
Mopidy.WebSocket = websocket.Client;
|
Mopidy.WebSocket = websocket.Client;
|
||||||
|
|
||||||
Mopidy.prototype._configure = function (settings) {
|
Mopidy.prototype._configure = function (settings) {
|
||||||
var currentHost = (typeof document !== "undefined" &&
|
var currentHost = (typeof document !== "undefined" &&
|
||||||
document.location.host) || "localhost";
|
document.location.host) || "localhost";
|
||||||
settings.webSocketUrl = settings.webSocketUrl ||
|
settings.webSocketUrl = settings.webSocketUrl ||
|
||||||
"ws://" + currentHost + "/mopidy/ws/";
|
"ws://" + currentHost + "/mopidy/ws";
|
||||||
|
|
||||||
if (settings.autoConnect !== false) {
|
if (settings.autoConnect !== false) {
|
||||||
settings.autoConnect = true;
|
settings.autoConnect = true;
|
||||||
@ -102,10 +116,9 @@ Mopidy.prototype._cleanup = function (closeEvent) {
|
|||||||
Object.keys(this._pendingRequests).forEach(function (requestId) {
|
Object.keys(this._pendingRequests).forEach(function (requestId) {
|
||||||
var resolver = this._pendingRequests[requestId];
|
var resolver = this._pendingRequests[requestId];
|
||||||
delete this._pendingRequests[requestId];
|
delete this._pendingRequests[requestId];
|
||||||
resolver.reject({
|
var error = new Mopidy.ConnectionError("WebSocket closed");
|
||||||
message: "WebSocket closed",
|
error.closeEvent = closeEvent;
|
||||||
closeEvent: closeEvent
|
resolver.reject(error);
|
||||||
});
|
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
this.emit("state:offline");
|
this.emit("state:offline");
|
||||||
@ -141,33 +154,25 @@ Mopidy.prototype._handleWebSocketError = function (error) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Mopidy.prototype._send = function (message) {
|
Mopidy.prototype._send = function (message) {
|
||||||
var deferred = when.defer();
|
|
||||||
|
|
||||||
switch (this._webSocket.readyState) {
|
switch (this._webSocket.readyState) {
|
||||||
case Mopidy.WebSocket.CONNECTING:
|
case Mopidy.WebSocket.CONNECTING:
|
||||||
deferred.resolver.reject({
|
return when.reject(
|
||||||
message: "WebSocket is still connecting"
|
new Mopidy.ConnectionError("WebSocket is still connecting"));
|
||||||
});
|
|
||||||
break;
|
|
||||||
case Mopidy.WebSocket.CLOSING:
|
case Mopidy.WebSocket.CLOSING:
|
||||||
deferred.resolver.reject({
|
return when.reject(
|
||||||
message: "WebSocket is closing"
|
new Mopidy.ConnectionError("WebSocket is closing"));
|
||||||
});
|
|
||||||
break;
|
|
||||||
case Mopidy.WebSocket.CLOSED:
|
case Mopidy.WebSocket.CLOSED:
|
||||||
deferred.resolver.reject({
|
return when.reject(
|
||||||
message: "WebSocket is closed"
|
new Mopidy.ConnectionError("WebSocket is closed"));
|
||||||
});
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
|
var deferred = when.defer();
|
||||||
message.jsonrpc = "2.0";
|
message.jsonrpc = "2.0";
|
||||||
message.id = this._nextRequestId();
|
message.id = this._nextRequestId();
|
||||||
this._pendingRequests[message.id] = deferred.resolver;
|
this._pendingRequests[message.id] = deferred.resolver;
|
||||||
this._webSocket.send(JSON.stringify(message));
|
this._webSocket.send(JSON.stringify(message));
|
||||||
this.emit("websocket:outgoingMessage", message);
|
this.emit("websocket:outgoingMessage", message);
|
||||||
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Mopidy.prototype._nextRequestId = (function () {
|
Mopidy.prototype._nextRequestId = (function () {
|
||||||
@ -208,19 +213,22 @@ Mopidy.prototype._handleResponse = function (responseMessage) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var error;
|
||||||
var resolver = this._pendingRequests[responseMessage.id];
|
var resolver = this._pendingRequests[responseMessage.id];
|
||||||
delete this._pendingRequests[responseMessage.id];
|
delete this._pendingRequests[responseMessage.id];
|
||||||
|
|
||||||
if (responseMessage.hasOwnProperty("result")) {
|
if (responseMessage.hasOwnProperty("result")) {
|
||||||
resolver.resolve(responseMessage.result);
|
resolver.resolve(responseMessage.result);
|
||||||
} else if (responseMessage.hasOwnProperty("error")) {
|
} else if (responseMessage.hasOwnProperty("error")) {
|
||||||
resolver.reject(responseMessage.error);
|
error = new Mopidy.ServerError(responseMessage.error.message);
|
||||||
|
error.code = responseMessage.error.code;
|
||||||
|
error.data = responseMessage.error.data;
|
||||||
|
resolver.reject(error);
|
||||||
this._console.warn("Server returned error:", responseMessage.error);
|
this._console.warn("Server returned error:", responseMessage.error);
|
||||||
} else {
|
} else {
|
||||||
resolver.reject({
|
error = new Error("Response without 'result' or 'error' received");
|
||||||
message: "Response without 'result' or 'error' received",
|
error.data = {response: responseMessage};
|
||||||
data: {response: responseMessage}
|
resolver.reject(error);
|
||||||
});
|
|
||||||
this._console.warn(
|
this._console.warn(
|
||||||
"Response without 'result' or 'error' received. Message was:",
|
"Response without 'result' or 'error' received. Message was:",
|
||||||
responseMessage);
|
responseMessage);
|
||||||
|
|||||||
@ -48,7 +48,7 @@ buster.testCase("Mopidy", {
|
|||||||
document.location.host || "localhost";
|
document.location.host || "localhost";
|
||||||
|
|
||||||
assert.calledOnceWith(this.webSocketConstructorStub,
|
assert.calledOnceWith(this.webSocketConstructorStub,
|
||||||
"ws://" + currentHost + "/mopidy/ws/");
|
"ws://" + currentHost + "/mopidy/ws");
|
||||||
},
|
},
|
||||||
|
|
||||||
"does not connect when autoConnect is false": function () {
|
"does not connect when autoConnect is false": function () {
|
||||||
@ -84,7 +84,7 @@ buster.testCase("Mopidy", {
|
|||||||
document.location.host || "localhost";
|
document.location.host || "localhost";
|
||||||
|
|
||||||
assert.calledOnceWith(this.webSocketConstructorStub,
|
assert.calledOnceWith(this.webSocketConstructorStub,
|
||||||
"ws://" + currentHost + "/mopidy/ws/");
|
"ws://" + currentHost + "/mopidy/ws");
|
||||||
},
|
},
|
||||||
|
|
||||||
"does nothing when the WebSocket is open": function () {
|
"does nothing when the WebSocket is open": function () {
|
||||||
@ -169,12 +169,18 @@ buster.testCase("Mopidy", {
|
|||||||
this.mopidy._cleanup(closeEvent);
|
this.mopidy._cleanup(closeEvent);
|
||||||
|
|
||||||
assert.equals(Object.keys(this.mopidy._pendingRequests).length, 0);
|
assert.equals(Object.keys(this.mopidy._pendingRequests).length, 0);
|
||||||
when.join(promise1, promise2).then(done(function () {
|
when.settle([promise1, promise2]).done(
|
||||||
assert(false, "Promises should be rejected");
|
done(function (descriptors) {
|
||||||
}), done(function (error) {
|
assert.equals(descriptors.length, 2);
|
||||||
assert.equals(error.message, "WebSocket closed");
|
descriptors.forEach(function (d) {
|
||||||
assert.same(error.closeEvent, closeEvent);
|
assert.equals(d.state, "rejected");
|
||||||
}));
|
assert(d.reason instanceof Error);
|
||||||
|
assert(d.reason instanceof Mopidy.ConnectionError);
|
||||||
|
assert.equals(d.reason.message, "WebSocket closed");
|
||||||
|
assert.same(d.reason.closeEvent, closeEvent);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
"emits 'state:offline' event when done": function () {
|
"emits 'state:offline' event when done": function () {
|
||||||
@ -388,12 +394,17 @@ buster.testCase("Mopidy", {
|
|||||||
var promise = this.mopidy._send({method: "foo"});
|
var promise = this.mopidy._send({method: "foo"});
|
||||||
|
|
||||||
refute.called(this.mopidy._webSocket.send);
|
refute.called(this.mopidy._webSocket.send);
|
||||||
promise.then(done(function () {
|
promise.done(
|
||||||
assert(false);
|
done(function () {
|
||||||
}), done(function (error) {
|
assert(false);
|
||||||
assert.equals(
|
}),
|
||||||
error.message, "WebSocket is still connecting");
|
done(function (error) {
|
||||||
}));
|
assert(error instanceof Error);
|
||||||
|
assert(error instanceof Mopidy.ConnectionError);
|
||||||
|
assert.equals(
|
||||||
|
error.message, "WebSocket is still connecting");
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
"immediately rejects request if CLOSING": function (done) {
|
"immediately rejects request if CLOSING": function (done) {
|
||||||
@ -402,12 +413,16 @@ buster.testCase("Mopidy", {
|
|||||||
var promise = this.mopidy._send({method: "foo"});
|
var promise = this.mopidy._send({method: "foo"});
|
||||||
|
|
||||||
refute.called(this.mopidy._webSocket.send);
|
refute.called(this.mopidy._webSocket.send);
|
||||||
promise.then(done(function () {
|
promise.done(
|
||||||
assert(false);
|
done(function () {
|
||||||
}), done(function (error) {
|
assert(false);
|
||||||
assert.equals(
|
}),
|
||||||
error.message, "WebSocket is closing");
|
done(function (error) {
|
||||||
}));
|
assert(error instanceof Error);
|
||||||
|
assert(error instanceof Mopidy.ConnectionError);
|
||||||
|
assert.equals(error.message, "WebSocket is closing");
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
"immediately rejects request if CLOSED": function (done) {
|
"immediately rejects request if CLOSED": function (done) {
|
||||||
@ -416,12 +431,16 @@ buster.testCase("Mopidy", {
|
|||||||
var promise = this.mopidy._send({method: "foo"});
|
var promise = this.mopidy._send({method: "foo"});
|
||||||
|
|
||||||
refute.called(this.mopidy._webSocket.send);
|
refute.called(this.mopidy._webSocket.send);
|
||||||
promise.then(done(function () {
|
promise.done(
|
||||||
assert(false);
|
done(function () {
|
||||||
}), done(function (error) {
|
assert(false);
|
||||||
assert.equals(
|
}),
|
||||||
error.message, "WebSocket is closed");
|
done(function (error) {
|
||||||
}));
|
assert(error instanceof Error);
|
||||||
|
assert(error instanceof Mopidy.ConnectionError);
|
||||||
|
assert.equals(error.message, "WebSocket is closed");
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -544,7 +563,11 @@ buster.testCase("Mopidy", {
|
|||||||
"rejects and logs requests which get errors back": function (done) {
|
"rejects and logs requests which get errors back": function (done) {
|
||||||
var stub = this.stub(this.mopidy._console, "warn");
|
var stub = this.stub(this.mopidy._console, "warn");
|
||||||
var promise = this.mopidy._send({method: "bar"});
|
var promise = this.mopidy._send({method: "bar"});
|
||||||
var responseError = {message: "Error", data: {}};
|
var responseError = {
|
||||||
|
code: -32601,
|
||||||
|
message: "Method not found",
|
||||||
|
data: {}
|
||||||
|
};
|
||||||
var responseMessage = {
|
var responseMessage = {
|
||||||
jsonrpc: "2.0",
|
jsonrpc: "2.0",
|
||||||
id: Object.keys(this.mopidy._pendingRequests)[0],
|
id: Object.keys(this.mopidy._pendingRequests)[0],
|
||||||
@ -555,11 +578,49 @@ buster.testCase("Mopidy", {
|
|||||||
|
|
||||||
assert.calledOnceWith(stub,
|
assert.calledOnceWith(stub,
|
||||||
"Server returned error:", responseError);
|
"Server returned error:", responseError);
|
||||||
promise.then(done(function () {
|
promise.done(
|
||||||
assert(false);
|
done(function () {
|
||||||
}), done(function (error) {
|
assert(false);
|
||||||
assert.equals(error, responseError);
|
}),
|
||||||
}));
|
done(function (error) {
|
||||||
|
assert(error instanceof Error);
|
||||||
|
assert.equals(error.code, responseError.code);
|
||||||
|
assert.equals(error.message, responseError.message);
|
||||||
|
assert.equals(error.data, responseError.data);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
"rejects and logs requests which get errors without data": function (done) {
|
||||||
|
var stub = this.stub(this.mopidy._console, "warn");
|
||||||
|
var promise = this.mopidy._send({method: "bar"});
|
||||||
|
var responseError = {
|
||||||
|
code: -32601,
|
||||||
|
message: "Method not found"
|
||||||
|
// 'data' key intentionally missing
|
||||||
|
};
|
||||||
|
var responseMessage = {
|
||||||
|
jsonrpc: "2.0",
|
||||||
|
id: Object.keys(this.mopidy._pendingRequests)[0],
|
||||||
|
error: responseError
|
||||||
|
};
|
||||||
|
|
||||||
|
this.mopidy._handleResponse(responseMessage);
|
||||||
|
|
||||||
|
assert.calledOnceWith(stub,
|
||||||
|
"Server returned error:", responseError);
|
||||||
|
promise.done(
|
||||||
|
done(function () {
|
||||||
|
assert(false);
|
||||||
|
}),
|
||||||
|
done(function (error) {
|
||||||
|
assert(error instanceof Error);
|
||||||
|
assert(error instanceof Mopidy.ServerError);
|
||||||
|
assert.equals(error.code, responseError.code);
|
||||||
|
assert.equals(error.message, responseError.message);
|
||||||
|
refute.defined(error.data);
|
||||||
|
})
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
"rejects and logs responses without result or error": function (done) {
|
"rejects and logs responses without result or error": function (done) {
|
||||||
@ -575,14 +636,18 @@ buster.testCase("Mopidy", {
|
|||||||
assert.calledOnceWith(stub,
|
assert.calledOnceWith(stub,
|
||||||
"Response without 'result' or 'error' received. Message was:",
|
"Response without 'result' or 'error' received. Message was:",
|
||||||
responseMessage);
|
responseMessage);
|
||||||
promise.then(done(function () {
|
promise.done(
|
||||||
assert(false);
|
done(function () {
|
||||||
}), done(function (error) {
|
assert(false);
|
||||||
assert.equals(
|
}),
|
||||||
error.message,
|
done(function (error) {
|
||||||
"Response without 'result' or 'error' received");
|
assert(error instanceof Error);
|
||||||
assert.equals(error.data.response, responseMessage);
|
assert.equals(
|
||||||
}));
|
error.message,
|
||||||
|
"Response without 'result' or 'error' received");
|
||||||
|
assert.equals(error.data.response, responseMessage);
|
||||||
|
})
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user