mirror of
https://github.com/gundb/panic-server.git
synced 2026-01-09 13:38:06 -05:00
Add editorconfig, change scripts
Editorconfig files help you keep a consistent style across projects. Recently, I've been using it to enforce spaces over tabs (I switched). This commit adds the config file and changes the project indent style to match. New script, "all-tests", runs the unit tests and the linter. The "test" script now just refers to unit tests.
This commit is contained in:
10
.editorconfig
Normal file
10
.editorconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
root = true
|
||||
|
||||
# global file settings
|
||||
[*]
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
trim_trailing_whitespace = true
|
||||
@@ -1,3 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- node
|
||||
|
||||
script: npm run all-tests
|
||||
|
||||
160
README.md
160
README.md
@@ -83,7 +83,7 @@ It's stringified, sent to the client, then evaluated in a special job context.
|
||||
console.log('This runs in your process.')
|
||||
|
||||
client.run(function () {
|
||||
console.log("This doesn't.")
|
||||
console.log("This doesn't.")
|
||||
})
|
||||
```
|
||||
|
||||
@@ -105,18 +105,18 @@ Because your function can't see any local scope variables, anything the function
|
||||
var clientPort = 8085
|
||||
|
||||
client.run(function () {
|
||||
var http = require('http')
|
||||
var server = new http.Server()
|
||||
var http = require('http')
|
||||
var server = new http.Server()
|
||||
|
||||
// The variable you sent.
|
||||
var port = this.props.port
|
||||
// The variable you sent.
|
||||
var port = this.props.port
|
||||
|
||||
server.listen(port)
|
||||
server.listen(port)
|
||||
}, {
|
||||
|
||||
// Sends the local variable
|
||||
// as `props.port`.
|
||||
port: clientPort
|
||||
// Sends the local variable
|
||||
// as `props.port`.
|
||||
port: clientPort
|
||||
})
|
||||
```
|
||||
|
||||
@@ -131,12 +131,12 @@ Prepare yourself, this is pretty awesome.
|
||||
|
||||
```js
|
||||
client.run(function () {
|
||||
var ip = require('ip')
|
||||
return ip.address()
|
||||
var ip = require('ip')
|
||||
return ip.address()
|
||||
}).then(function (ip) {
|
||||
|
||||
// The address of the other machine
|
||||
console.log(ip)
|
||||
// The address of the other machine
|
||||
console.log(ip)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -146,23 +146,23 @@ So, if one of your clients is a node process...
|
||||
|
||||
```js
|
||||
function sh () {
|
||||
var child = require('child_process')
|
||||
var spawn = child.spawnSync
|
||||
var child = require('child_process')
|
||||
var spawn = child.spawnSync
|
||||
|
||||
var cmd = this.props.cmd
|
||||
var args = this.props.args
|
||||
var cmd = this.props.cmd
|
||||
var args = this.props.args
|
||||
|
||||
var result = spawn(cmd, args || [])
|
||||
var result = spawn(cmd, args || [])
|
||||
|
||||
return result.stdout
|
||||
return result.stdout
|
||||
}
|
||||
|
||||
client.run(sh, {
|
||||
cmd: 'ls',
|
||||
args: ['-lah']
|
||||
cmd: 'ls',
|
||||
args: ['-lah']
|
||||
}).then(function (dir) {
|
||||
var output = dir.toString('utf8')
|
||||
console.log(output)
|
||||
var output = dir.toString('utf8')
|
||||
console.log(output)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -177,22 +177,22 @@ If your job throws an error, you'll get the message back on the server:
|
||||
|
||||
```js
|
||||
client.run(function () {
|
||||
throw new Error(
|
||||
'Hrmm, servers are on fire.'
|
||||
)
|
||||
throw new Error(
|
||||
'Hrmm, servers are on fire.'
|
||||
)
|
||||
}).catch(function (error) {
|
||||
console.log(error)
|
||||
/*
|
||||
{
|
||||
message: 'Hrmm, servers...',
|
||||
source: `function () {
|
||||
throw new Error(
|
||||
'Hrmm, servers are on fire.'
|
||||
)
|
||||
}`,
|
||||
platform: {} // platform.js
|
||||
}
|
||||
*/
|
||||
console.log(error)
|
||||
/*
|
||||
{
|
||||
message: 'Hrmm, servers...',
|
||||
source: `function () {
|
||||
throw new Error(
|
||||
'Hrmm, servers are on fire.'
|
||||
)
|
||||
}`,
|
||||
platform: {} // platform.js
|
||||
}
|
||||
*/
|
||||
})
|
||||
```
|
||||
|
||||
@@ -223,7 +223,7 @@ It'll pass both the `Client` and the socket ID.
|
||||
|
||||
```js
|
||||
clients.on('add', function (client, id) {
|
||||
console.log('New client:', id)
|
||||
console.log('New client:', id)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -232,7 +232,7 @@ Basically the same as `"add"`, just backwards.
|
||||
|
||||
```js
|
||||
clients.on('remove', function (client, id) {
|
||||
console.log('Client', id, 'left.')
|
||||
console.log('Client', id, 'left.')
|
||||
})
|
||||
```
|
||||
|
||||
@@ -246,8 +246,8 @@ var explorer = clients.filter('Internet Explorer')
|
||||
var opera = clients.filter('Opera Mini')
|
||||
|
||||
var despicable = new ClientList([
|
||||
explorer,
|
||||
opera,
|
||||
explorer,
|
||||
opera,
|
||||
])
|
||||
```
|
||||
|
||||
@@ -279,11 +279,11 @@ When passed a `String` or `RegExp`, it'll be used to match against the `platform
|
||||
|
||||
```javascript
|
||||
var list = clients.filter({
|
||||
layout: /(Gecko|Blink)/,
|
||||
os: {
|
||||
architecture: 64,
|
||||
family: /(OS X|Windows)/,
|
||||
},
|
||||
layout: /(Gecko|Blink)/,
|
||||
os: {
|
||||
architecture: 64,
|
||||
family: /(OS X|Windows)/,
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
@@ -291,22 +291,22 @@ Every setting above is optional, and you can create as loose or specific a query
|
||||
|
||||
```javascript
|
||||
var firefox = clients.filter(function (client, id, list) {
|
||||
// `id`: The unique client id
|
||||
// `list`: The parent list object, in this case `clients`
|
||||
// `id`: The unique client id
|
||||
// `list`: The parent list object, in this case `clients`
|
||||
|
||||
var platform = client.platform;
|
||||
var platform = client.platform;
|
||||
|
||||
/*
|
||||
This query only adds versions of
|
||||
Firefox later than version 36.
|
||||
*/
|
||||
if (platform.name === 'Firefox' && platform.version > '36') {
|
||||
// add this client to the new list
|
||||
return true;
|
||||
} else {
|
||||
// leave the client out of the new list
|
||||
return false;
|
||||
}
|
||||
/*
|
||||
This query only adds versions of
|
||||
Firefox later than version 36.
|
||||
*/
|
||||
if (platform.name === 'Firefox' && platform.version > '36') {
|
||||
// add this client to the new list
|
||||
return true;
|
||||
} else {
|
||||
// leave the client out of the new list
|
||||
return false;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
@@ -318,7 +318,7 @@ var firefox = clients.filter('Firefox')
|
||||
|
||||
// the list of firefox newer after version 36
|
||||
var firefoxAfter36 = firefox.filter(function (client) {
|
||||
return client.platform.version > '36'
|
||||
return client.platform.version > '36'
|
||||
});
|
||||
```
|
||||
|
||||
@@ -362,7 +362,7 @@ var bob = clients
|
||||
// and another, so long as it isn't alice or bob
|
||||
var carl = clients
|
||||
.excluding(
|
||||
new List([ alice, bob ])
|
||||
new List([ alice, bob ])
|
||||
)
|
||||
.pluck(1)
|
||||
```
|
||||
@@ -383,8 +383,8 @@ var minimum = clients.atLeast(2)
|
||||
|
||||
minimum.then(function () {
|
||||
|
||||
// 2 clients are connected now.
|
||||
return clients.run(/* ... */)
|
||||
// 2 clients are connected now.
|
||||
return clients.run(/* ... */)
|
||||
})
|
||||
```
|
||||
|
||||
@@ -406,10 +406,10 @@ However, if any client fails, the promise rejects.
|
||||
|
||||
```js
|
||||
panic.clients.run(function () {
|
||||
var ip = require('ip')
|
||||
return ip.address()
|
||||
var ip = require('ip')
|
||||
return ip.address()
|
||||
}).then(function (ips) {
|
||||
console.log(ips) // Array of IPs.
|
||||
console.log(ips) // Array of IPs.
|
||||
})
|
||||
```
|
||||
|
||||
@@ -431,9 +431,9 @@ It's basically a `.forEach` on the list. The function you pass will get the clie
|
||||
**Example**
|
||||
```javascript
|
||||
clients.each(function (client, id, list) {
|
||||
client.run(function () {
|
||||
// Fun stuff
|
||||
})
|
||||
client.run(function () {
|
||||
// Fun stuff
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
@@ -445,7 +445,7 @@ var list = new ClientList()
|
||||
list.chain() instanceof ClientList // true
|
||||
|
||||
class SubClass extends ClientList {
|
||||
coolNewMethod() { /* bacon */ }
|
||||
coolNewMethod() { /* bacon */ }
|
||||
}
|
||||
|
||||
var sub = new SubClass()
|
||||
@@ -506,8 +506,8 @@ A simple "Hello world" panic app.
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Connect to panic!
|
||||
panic.server('http://localhost:8080')
|
||||
// Connect to panic!
|
||||
panic.server('http://localhost:8080')
|
||||
</script>
|
||||
```
|
||||
|
||||
@@ -529,13 +529,13 @@ var browsers = clients.excluding(servers)
|
||||
// Wait for the browser to connect.
|
||||
browsers.on('add', function (browser) {
|
||||
|
||||
browser.run(function () {
|
||||
browser.run(function () {
|
||||
|
||||
// This is run in the browser!
|
||||
var header = document.createElement('h1')
|
||||
header.innerHTML = 'OHAI BROWSR!'
|
||||
document.body.appendChild(header)
|
||||
})
|
||||
// This is run in the browser!
|
||||
var header = document.createElement('h1')
|
||||
header.innerHTML = 'OHAI BROWSR!'
|
||||
document.body.appendChild(header)
|
||||
})
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
@@ -5,7 +5,8 @@
|
||||
"main": "src/index.js",
|
||||
"scripts": {
|
||||
"lint": "eslint src/**",
|
||||
"test": "mocha && npm run lint"
|
||||
"test": "mocha",
|
||||
"all-tests": "npm run test && npm run lint"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -13,63 +13,63 @@ var Promise = require('bluebird');
|
||||
*/
|
||||
function Client (client) {
|
||||
|
||||
/** Basic input validation. */
|
||||
if (!client.socket) {
|
||||
throw new Error('Invalid "client.socket" property.');
|
||||
}
|
||||
if (!client.platform) {
|
||||
throw new Error('Invalid "client.platform" property.');
|
||||
}
|
||||
/** Basic input validation. */
|
||||
if (!client.socket) {
|
||||
throw new Error('Invalid "client.socket" property.');
|
||||
}
|
||||
if (!client.platform) {
|
||||
throw new Error('Invalid "client.platform" property.');
|
||||
}
|
||||
|
||||
this.socket = client.socket;
|
||||
this.platform = client.platform;
|
||||
this.socket = client.socket;
|
||||
this.platform = client.platform;
|
||||
}
|
||||
|
||||
Client.prototype = {
|
||||
constructor: Client,
|
||||
constructor: Client,
|
||||
|
||||
/**
|
||||
* Sends a function to be run on the client.
|
||||
* @param {Function} job - A function to be run remotely.
|
||||
* The function will be stringified, so it cannot depend on
|
||||
* external "local" variables, including other functions.
|
||||
* @param {Object} [props] - Any variables used in the job.
|
||||
* @return {Promise} - Resolves if the job finishes,
|
||||
* rejects if it throws an error.
|
||||
*/
|
||||
run: function (job, props) {
|
||||
if (typeof job !== 'function') {
|
||||
throw new TypeError(
|
||||
'Expected job "' + job + '" to be a function.'
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Sends a function to be run on the client.
|
||||
* @param {Function} job - A function to be run remotely.
|
||||
* The function will be stringified, so it cannot depend on
|
||||
* external "local" variables, including other functions.
|
||||
* @param {Object} [props] - Any variables used in the job.
|
||||
* @return {Promise} - Resolves if the job finishes,
|
||||
* rejects if it throws an error.
|
||||
*/
|
||||
run: function (job, props) {
|
||||
if (typeof job !== 'function') {
|
||||
throw new TypeError(
|
||||
'Expected job "' + job + '" to be a function.'
|
||||
);
|
||||
}
|
||||
|
||||
var source = String(job);
|
||||
var jobID = Math.random()
|
||||
.toString(36)
|
||||
.slice(2);
|
||||
var source = String(job);
|
||||
var jobID = Math.random()
|
||||
.toString(36)
|
||||
.slice(2);
|
||||
|
||||
var socket = this.socket;
|
||||
var socket = this.socket;
|
||||
|
||||
/** Report the success or failure of the job. */
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
socket.once('disconnect', resolve);
|
||||
/** Report the success or failure of the job. */
|
||||
var promise = new Promise(function (resolve, reject) {
|
||||
socket.once('disconnect', resolve);
|
||||
|
||||
socket.once(jobID, function (report) {
|
||||
socket.removeListener('disconnect', resolve);
|
||||
socket.once(jobID, function (report) {
|
||||
socket.removeListener('disconnect', resolve);
|
||||
|
||||
if (report.hasOwnProperty('error')) {
|
||||
reject(report.error);
|
||||
} else {
|
||||
resolve(report.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
if (report.hasOwnProperty('error')) {
|
||||
reject(report.error);
|
||||
} else {
|
||||
resolve(report.value);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
socket.emit('run', source, jobID, props);
|
||||
socket.emit('run', source, jobID, props);
|
||||
|
||||
return promise;
|
||||
},
|
||||
return promise;
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = Client;
|
||||
|
||||
@@ -11,20 +11,20 @@ var Promise = require('bluebird');
|
||||
* @augments EventEmitter
|
||||
*/
|
||||
function ClientList(lists) {
|
||||
var list = this;
|
||||
Emitter.call(this);
|
||||
list.clients = {};
|
||||
var list = this;
|
||||
Emitter.call(this);
|
||||
list.clients = {};
|
||||
|
||||
var add = list.add.bind(list);
|
||||
var add = list.add.bind(list);
|
||||
|
||||
/** See if the user passed an array. */
|
||||
if (lists instanceof Array) {
|
||||
lists.forEach(function (list) {
|
||||
/** See if the user passed an array. */
|
||||
if (lists instanceof Array) {
|
||||
lists.forEach(function (list) {
|
||||
|
||||
/** Add each client, listening for additions. */
|
||||
list.each(add).on('add', add);
|
||||
});
|
||||
}
|
||||
/** Add each client, listening for additions. */
|
||||
list.each(add).on('add', add);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var API = ClientList.prototype = new Emitter();
|
||||
@@ -40,7 +40,7 @@ API.constructor = ClientList;
|
||||
* or a subclass.
|
||||
*/
|
||||
API.chain = function (list) {
|
||||
return new this.constructor(list);
|
||||
return new this.constructor(list);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -49,13 +49,13 @@ API.chain = function (list) {
|
||||
* @return {this} - The current context.
|
||||
*/
|
||||
API.each = function (cb) {
|
||||
var key;
|
||||
for (key in this.clients) {
|
||||
if (this.clients.hasOwnProperty(key)) {
|
||||
cb(this.clients[key], key, this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
var key;
|
||||
for (key in this.clients) {
|
||||
if (this.clients.hasOwnProperty(key)) {
|
||||
cb(this.clients[key], key, this);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -67,29 +67,29 @@ API.each = function (cb) {
|
||||
* @returns {this} - The current context.
|
||||
*/
|
||||
API.add = function (client) {
|
||||
var socket, list = this;
|
||||
socket = client.socket;
|
||||
var socket, list = this;
|
||||
socket = client.socket;
|
||||
|
||||
/**
|
||||
* Ignore disconnected clients,
|
||||
* or those already in the list.
|
||||
*/
|
||||
if (!socket.connected || this.get(socket.id)) {
|
||||
return this;
|
||||
}
|
||||
/**
|
||||
* Ignore disconnected clients,
|
||||
* or those already in the list.
|
||||
*/
|
||||
if (!socket.connected || this.get(socket.id)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Add the client. */
|
||||
this.clients[socket.id] = client;
|
||||
/** Add the client. */
|
||||
this.clients[socket.id] = client;
|
||||
|
||||
/** Remove on disconnect. */
|
||||
socket.on('disconnect', function () {
|
||||
list.remove(client);
|
||||
});
|
||||
/** Remove on disconnect. */
|
||||
socket.on('disconnect', function () {
|
||||
list.remove(client);
|
||||
});
|
||||
|
||||
/** Fire the 'add' event. */
|
||||
this.emit('add', client, socket.id);
|
||||
/** Fire the 'add' event. */
|
||||
this.emit('add', client, socket.id);
|
||||
|
||||
return this;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -99,17 +99,17 @@ API.add = function (client) {
|
||||
*/
|
||||
API.remove = function (client) {
|
||||
|
||||
/** Make sure we really have that client. */
|
||||
if (client.socket.id in this.clients) {
|
||||
/** Make sure we really have that client. */
|
||||
if (client.socket.id in this.clients) {
|
||||
|
||||
/** Remove the client. */
|
||||
delete this.clients[client.socket.id];
|
||||
/** Remove the client. */
|
||||
delete this.clients[client.socket.id];
|
||||
|
||||
/** Fire the 'remove' event. */
|
||||
this.emit('remove', client, client.socket.id);
|
||||
}
|
||||
/** Fire the 'remove' event. */
|
||||
this.emit('remove', client, client.socket.id);
|
||||
}
|
||||
|
||||
return this;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -118,7 +118,7 @@ API.remove = function (client) {
|
||||
* @return {Object|null} - The client object, if found.
|
||||
*/
|
||||
API.get = function (ID) {
|
||||
return this.clients[ID] || null;
|
||||
return this.clients[ID] || null;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -129,32 +129,32 @@ API.get = function (ID) {
|
||||
*/
|
||||
API.filter = function (query) {
|
||||
|
||||
/** Create a new target list. */
|
||||
var list = this.chain();
|
||||
/** Create a new target list. */
|
||||
var list = this.chain();
|
||||
|
||||
function filter (client, ID) {
|
||||
if (query instanceof Function && query(client, ID)) {
|
||||
list.add(client);
|
||||
return;
|
||||
} else if (typeof query === 'string' || query instanceof RegExp) {
|
||||
query = {
|
||||
name: query
|
||||
};
|
||||
}
|
||||
if (typeof query === 'object' && query) {
|
||||
if (match(query, client.platform)) {
|
||||
list.add(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
function filter (client, ID) {
|
||||
if (query instanceof Function && query(client, ID)) {
|
||||
list.add(client);
|
||||
return;
|
||||
} else if (typeof query === 'string' || query instanceof RegExp) {
|
||||
query = {
|
||||
name: query
|
||||
};
|
||||
}
|
||||
if (typeof query === 'object' && query) {
|
||||
if (match(query, client.platform)) {
|
||||
list.add(client);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter everything in the list, then listen
|
||||
* for future clients.
|
||||
*/
|
||||
this.each(filter).on('add', filter);
|
||||
/**
|
||||
* Filter everything in the list, then listen
|
||||
* for future clients.
|
||||
*/
|
||||
this.each(filter).on('add', filter);
|
||||
|
||||
return list;
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -165,33 +165,33 @@ API.filter = function (query) {
|
||||
*/
|
||||
API.excluding = function (exclude) {
|
||||
|
||||
/**
|
||||
* Add anything not in the exclusion list.
|
||||
* Remember .filter is reactive.
|
||||
*/
|
||||
var list = this.filter(function (client) {
|
||||
var excluded = exclude.get(client.socket.id);
|
||||
/**
|
||||
* Add anything not in the exclusion list.
|
||||
* Remember .filter is reactive.
|
||||
*/
|
||||
var list = this.filter(function (client) {
|
||||
var excluded = exclude.get(client.socket.id);
|
||||
|
||||
return !excluded;
|
||||
});
|
||||
return !excluded;
|
||||
});
|
||||
|
||||
var self = this;
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* Add clients removed from the exclusion list,
|
||||
* and contained in the original list.
|
||||
*/
|
||||
exclude.on('remove', function (client) {
|
||||
var socket = client.socket;
|
||||
var connected = socket.connected;
|
||||
var relevant = self.get(socket.id);
|
||||
/**
|
||||
* Add clients removed from the exclusion list,
|
||||
* and contained in the original list.
|
||||
*/
|
||||
exclude.on('remove', function (client) {
|
||||
var socket = client.socket;
|
||||
var connected = socket.connected;
|
||||
var relevant = self.get(socket.id);
|
||||
|
||||
if (connected && relevant) {
|
||||
list.add(client);
|
||||
}
|
||||
});
|
||||
if (connected && relevant) {
|
||||
list.add(client);
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
return list;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -202,16 +202,16 @@ API.excluding = function (exclude) {
|
||||
* rejects if any of them fail.
|
||||
*/
|
||||
API.run = function (job, props) {
|
||||
var jobs = [];
|
||||
var jobs = [];
|
||||
|
||||
/** Run the job on each client. */
|
||||
this.each(function (client) {
|
||||
var promise = client.run(job, props);
|
||||
jobs.push(promise);
|
||||
});
|
||||
/** Run the job on each client. */
|
||||
this.each(function (client) {
|
||||
var promise = client.run(job, props);
|
||||
jobs.push(promise);
|
||||
});
|
||||
|
||||
/** Wait for all jobs to finish. */
|
||||
return Promise.all(jobs);
|
||||
/** Wait for all jobs to finish. */
|
||||
return Promise.all(jobs);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -220,27 +220,27 @@ API.run = function (job, props) {
|
||||
* @return {Promise} - Resolves when the minimum is reached.
|
||||
*/
|
||||
API.atLeast = function (min) {
|
||||
var list = this;
|
||||
var list = this;
|
||||
|
||||
/** Check to see if we already have enough. */
|
||||
if (list.length >= min) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
/** Check to see if we already have enough. */
|
||||
if (list.length >= min) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return new Promise(function (resolve) {
|
||||
return new Promise(function (resolve) {
|
||||
|
||||
/** Wait for new clients. */
|
||||
list.on('add', function cb () {
|
||||
/** Wait for new clients. */
|
||||
list.on('add', function cb () {
|
||||
|
||||
/** If we have enough... */
|
||||
if (list.length >= min) {
|
||||
/** If we have enough... */
|
||||
if (list.length >= min) {
|
||||
|
||||
/** Unsubscribe and resolve. */
|
||||
list.removeListener('add', cb);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
/** Unsubscribe and resolve. */
|
||||
list.removeListener('add', cb);
|
||||
resolve();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -251,38 +251,38 @@ API.atLeast = function (min) {
|
||||
*/
|
||||
API.pluck = function (num) {
|
||||
|
||||
/** Create a new target list. */
|
||||
var list = this.chain();
|
||||
var self = this;
|
||||
/** Create a new target list. */
|
||||
var list = this.chain();
|
||||
var self = this;
|
||||
|
||||
/**
|
||||
* Add a client if there's still room.
|
||||
* @param {Object} client - A client object.
|
||||
* @return {undefined}
|
||||
*/
|
||||
function measure(client) {
|
||||
if (!list.atCapacity) {
|
||||
list.add(client);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Add a client if there's still room.
|
||||
* @param {Object} client - A client object.
|
||||
* @return {undefined}
|
||||
*/
|
||||
function measure(client) {
|
||||
if (!list.atCapacity) {
|
||||
list.add(client);
|
||||
}
|
||||
}
|
||||
|
||||
/** Check to see if it's already full. */
|
||||
list.on('add', function () {
|
||||
if (list.length === num) {
|
||||
list.atCapacity = true;
|
||||
}
|
||||
});
|
||||
/** Check to see if it's already full. */
|
||||
list.on('add', function () {
|
||||
if (list.length === num) {
|
||||
list.atCapacity = true;
|
||||
}
|
||||
});
|
||||
|
||||
/** See if we can replace the lost client. */
|
||||
list.on('remove', function () {
|
||||
list.atCapacity = false;
|
||||
self.each(measure);
|
||||
});
|
||||
/** See if we can replace the lost client. */
|
||||
list.on('remove', function () {
|
||||
list.atCapacity = false;
|
||||
self.each(measure);
|
||||
});
|
||||
|
||||
/** Add as many clients as we can. */
|
||||
this.each(measure).on('add', measure);
|
||||
/** Add as many clients as we can. */
|
||||
this.each(measure).on('add', measure);
|
||||
|
||||
return list;
|
||||
return list;
|
||||
};
|
||||
|
||||
API.atCapacity = false;
|
||||
@@ -292,21 +292,21 @@ API.atCapacity = false;
|
||||
* @returns {Number} - The length of the list.
|
||||
*/
|
||||
Object.defineProperty(API, 'length', {
|
||||
get: function () {
|
||||
get: function () {
|
||||
|
||||
/** Feature detect Object.keys. */
|
||||
if (Object.keys instanceof Function) {
|
||||
return Object.keys(this.clients).length;
|
||||
}
|
||||
/** Feature detect Object.keys. */
|
||||
if (Object.keys instanceof Function) {
|
||||
return Object.keys(this.clients).length;
|
||||
}
|
||||
|
||||
/** Fall back to iterating. */
|
||||
var length = 0;
|
||||
this.each(function () {
|
||||
length += 1;
|
||||
});
|
||||
/** Fall back to iterating. */
|
||||
var length = 0;
|
||||
this.each(function () {
|
||||
length += 1;
|
||||
});
|
||||
|
||||
return length;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = ClientList;
|
||||
|
||||
@@ -12,37 +12,37 @@
|
||||
* @return {Boolean} - Whether the platform matches the query.
|
||||
*/
|
||||
function match(query, platform) {
|
||||
var key, value, matches = true;
|
||||
var key, value, matches = true;
|
||||
|
||||
/** Check all query options. */
|
||||
for (key in query) {
|
||||
if (!(query.hasOwnProperty(key))) {
|
||||
continue;
|
||||
}
|
||||
/** Check all query options. */
|
||||
for (key in query) {
|
||||
if (!(query.hasOwnProperty(key))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
value = query[key];
|
||||
value = query[key];
|
||||
|
||||
if (value instanceof RegExp) {
|
||||
if (value instanceof RegExp) {
|
||||
|
||||
/** Tests if the expression matches. */
|
||||
matches = matches && !!platform[key].match(value);
|
||||
} else if (typeof value === 'string') {
|
||||
/** Tests if the expression matches. */
|
||||
matches = matches && !!platform[key].match(value);
|
||||
} else if (typeof value === 'string') {
|
||||
|
||||
/**
|
||||
* Check for equality against the expression.
|
||||
* Loose check for string vs number cases
|
||||
* (like os.architecture).
|
||||
*/
|
||||
matches = matches && platform[key] == value;
|
||||
} else if (value instanceof Object) {
|
||||
/**
|
||||
* Check for equality against the expression.
|
||||
* Loose check for string vs number cases
|
||||
* (like os.architecture).
|
||||
*/
|
||||
matches = matches && platform[key] == value;
|
||||
} else if (value instanceof Object) {
|
||||
|
||||
/** Recursively match deeper queries. */
|
||||
return match(value, platform[key] || {});
|
||||
}
|
||||
}
|
||||
/** Recursively match deeper queries. */
|
||||
return match(value, platform[key] || {});
|
||||
}
|
||||
}
|
||||
|
||||
/** Whether the query matches. */
|
||||
return matches;
|
||||
/** Whether the query matches. */
|
||||
return matches;
|
||||
}
|
||||
|
||||
module.exports = match;
|
||||
|
||||
@@ -15,13 +15,13 @@ var client;
|
||||
* @returns {String} - The whole webpacked client bundle.
|
||||
*/
|
||||
Object.defineProperty(panic, 'client', {
|
||||
get: function () {
|
||||
if (!client) {
|
||||
client = fs.readFileSync(file, 'utf8');
|
||||
}
|
||||
get: function () {
|
||||
if (!client) {
|
||||
client = fs.readFileSync(file, 'utf8');
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
return client;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -32,9 +32,9 @@ Object.defineProperty(panic, 'client', {
|
||||
* @return {undefined}
|
||||
*/
|
||||
function serve(req, res) {
|
||||
if (req.url === '/panic.js') {
|
||||
res.end(panic.client);
|
||||
}
|
||||
if (req.url === '/panic.js') {
|
||||
res.end(panic.client);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -44,18 +44,18 @@ function serve(req, res) {
|
||||
* @return {undefined}
|
||||
*/
|
||||
function upgrade(socket) {
|
||||
socket.on('handshake', function (platform) {
|
||||
socket.on('handshake', function (platform) {
|
||||
|
||||
/** Create a new panic client. */
|
||||
var client = new Client({
|
||||
socket: socket,
|
||||
platform: platform,
|
||||
});
|
||||
/** Create a new panic client. */
|
||||
var client = new Client({
|
||||
socket: socket,
|
||||
platform: platform,
|
||||
});
|
||||
|
||||
/** Add the new client. */
|
||||
clients.add(client);
|
||||
/** Add the new client. */
|
||||
clients.add(client);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -67,17 +67,17 @@ function upgrade(socket) {
|
||||
*/
|
||||
function open(server) {
|
||||
|
||||
if (!(server instanceof Server)) {
|
||||
server = new Server();
|
||||
}
|
||||
if (!(server instanceof Server)) {
|
||||
server = new Server();
|
||||
}
|
||||
|
||||
/** Handle /panic.js route. */
|
||||
server.on('request', serve);
|
||||
/** Handle /panic.js route. */
|
||||
server.on('request', serve);
|
||||
|
||||
/** Upgrade with socket.io */
|
||||
io(server).on('connection', upgrade);
|
||||
/** Upgrade with socket.io */
|
||||
io(server).on('connection', upgrade);
|
||||
|
||||
return server;
|
||||
return server;
|
||||
}
|
||||
|
||||
module.exports = open;
|
||||
|
||||
254
test/Client.js
254
test/Client.js
@@ -6,159 +6,159 @@ var Promise = require('bluebird');
|
||||
var expect = require('expect');
|
||||
|
||||
describe('A client', function () {
|
||||
var client, socket, platform;
|
||||
var client, socket, platform;
|
||||
|
||||
beforeEach(function () {
|
||||
socket = new Emitter();
|
||||
beforeEach(function () {
|
||||
socket = new Emitter();
|
||||
|
||||
platform = {
|
||||
name: 'Node.js',
|
||||
version: '6.6.0',
|
||||
};
|
||||
platform = {
|
||||
name: 'Node.js',
|
||||
version: '6.6.0',
|
||||
};
|
||||
|
||||
client = new Client({
|
||||
socket: socket,
|
||||
platform: platform,
|
||||
});
|
||||
});
|
||||
client = new Client({
|
||||
socket: socket,
|
||||
platform: platform,
|
||||
});
|
||||
});
|
||||
|
||||
it('should expose the socket', function () {
|
||||
expect(client.socket).toBe(socket);
|
||||
});
|
||||
it('should expose the socket', function () {
|
||||
expect(client.socket).toBe(socket);
|
||||
});
|
||||
|
||||
it('should expose the platform', function () {
|
||||
expect(client.platform).toBe(platform);
|
||||
});
|
||||
it('should expose the platform', function () {
|
||||
expect(client.platform).toBe(platform);
|
||||
});
|
||||
|
||||
it('should validate the socket', function () {
|
||||
function fail () {
|
||||
return new Client({
|
||||
// Missing "socket".
|
||||
platform: platform,
|
||||
});
|
||||
}
|
||||
expect(fail).toThrow();
|
||||
});
|
||||
it('should validate the socket', function () {
|
||||
function fail () {
|
||||
return new Client({
|
||||
// Missing "socket".
|
||||
platform: platform,
|
||||
});
|
||||
}
|
||||
expect(fail).toThrow();
|
||||
});
|
||||
|
||||
it('should validate the platform', function () {
|
||||
function fail () {
|
||||
return new Client({
|
||||
socket: new Emitter(),
|
||||
// Missing "platform".
|
||||
});
|
||||
}
|
||||
expect(fail).toThrow();
|
||||
});
|
||||
it('should validate the platform', function () {
|
||||
function fail () {
|
||||
return new Client({
|
||||
socket: new Emitter(),
|
||||
// Missing "platform".
|
||||
});
|
||||
}
|
||||
expect(fail).toThrow();
|
||||
});
|
||||
|
||||
describe('"run" call', function () {
|
||||
var spy;
|
||||
describe('"run" call', function () {
|
||||
var spy;
|
||||
|
||||
beforeEach(function () {
|
||||
spy = expect.createSpy();
|
||||
});
|
||||
beforeEach(function () {
|
||||
spy = expect.createSpy();
|
||||
});
|
||||
|
||||
it('should send jobs to the client', function () {
|
||||
client.socket.on('run', spy);
|
||||
client.run(function () {});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
it('should send jobs to the client', function () {
|
||||
client.socket.on('run', spy);
|
||||
client.run(function () {});
|
||||
expect(spy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should make sure the job is a function', function () {
|
||||
function fail () {
|
||||
client.run(9000);
|
||||
}
|
||||
expect(fail).toThrow(TypeError);
|
||||
});
|
||||
it('should make sure the job is a function', function () {
|
||||
function fail () {
|
||||
client.run(9000);
|
||||
}
|
||||
expect(fail).toThrow(TypeError);
|
||||
});
|
||||
|
||||
it('should send the stringified job', function () {
|
||||
client.socket.on('run', spy);
|
||||
client.run(function () {
|
||||
// I haz a comment.
|
||||
});
|
||||
var str = spy.calls[0].arguments[0];
|
||||
expect(str).toContain('I haz a comment');
|
||||
});
|
||||
it('should send the stringified job', function () {
|
||||
client.socket.on('run', spy);
|
||||
client.run(function () {
|
||||
// I haz a comment.
|
||||
});
|
||||
var str = spy.calls[0].arguments[0];
|
||||
expect(str).toContain('I haz a comment');
|
||||
});
|
||||
|
||||
it('should pass a job ID', function () {
|
||||
client.socket.on('run', spy);
|
||||
client.run(function () {});
|
||||
client.run(function () {});
|
||||
var id1 = spy.calls[0].arguments[1];
|
||||
var id2 = spy.calls[1].arguments[1];
|
||||
expect(id1).toBeA('string');
|
||||
expect(id2).toBeA('string');
|
||||
expect(id1).toNotBe(id2);
|
||||
});
|
||||
it('should pass a job ID', function () {
|
||||
client.socket.on('run', spy);
|
||||
client.run(function () {});
|
||||
client.run(function () {});
|
||||
var id1 = spy.calls[0].arguments[1];
|
||||
var id2 = spy.calls[1].arguments[1];
|
||||
expect(id1).toBeA('string');
|
||||
expect(id2).toBeA('string');
|
||||
expect(id1).toNotBe(id2);
|
||||
});
|
||||
|
||||
it('should send the props to the client', function () {
|
||||
client.socket.on('run', spy);
|
||||
var props = {};
|
||||
client.run(function () {}, props);
|
||||
var args = spy.calls[0].arguments;
|
||||
expect(args[2]).toBe(props);
|
||||
});
|
||||
it('should send the props to the client', function () {
|
||||
client.socket.on('run', spy);
|
||||
var props = {};
|
||||
client.run(function () {}, props);
|
||||
var args = spy.calls[0].arguments;
|
||||
expect(args[2]).toBe(props);
|
||||
});
|
||||
|
||||
it('should return a bluebird promise', function () {
|
||||
var job = client.run(function () {});
|
||||
expect(job).toBeA(Promise);
|
||||
});
|
||||
it('should return a bluebird promise', function () {
|
||||
var job = client.run(function () {});
|
||||
expect(job).toBeA(Promise);
|
||||
});
|
||||
|
||||
it('should resolve when the job does', function (done) {
|
||||
client.socket.on('run', spy);
|
||||
var job = client.run(function () {});
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
it('should resolve when the job does', function (done) {
|
||||
client.socket.on('run', spy);
|
||||
var job = client.run(function () {});
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
|
||||
job.then(done);
|
||||
client.socket.emit(jobID, {});
|
||||
});
|
||||
job.then(done);
|
||||
client.socket.emit(jobID, {});
|
||||
});
|
||||
|
||||
it('should resolve to the job value', function () {
|
||||
client.socket.on('run', spy);
|
||||
var job = client.run(function () {});
|
||||
it('should resolve to the job value', function () {
|
||||
client.socket.on('run', spy);
|
||||
var job = client.run(function () {});
|
||||
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
|
||||
client.socket.emit(jobID, {
|
||||
value: 'Hello world!',
|
||||
});
|
||||
client.socket.emit(jobID, {
|
||||
value: 'Hello world!',
|
||||
});
|
||||
|
||||
return job.then(function (value) {
|
||||
expect(value).toBe('Hello world!');
|
||||
});
|
||||
});
|
||||
return job.then(function (value) {
|
||||
expect(value).toBe('Hello world!');
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject if the job fails', function (done) {
|
||||
client.socket.on('run', spy);
|
||||
var job = client.run(function () {});
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
var error = new Error('".run" rejection test.');
|
||||
it('should reject if the job fails', function (done) {
|
||||
client.socket.on('run', spy);
|
||||
var job = client.run(function () {});
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
var error = new Error('".run" rejection test.');
|
||||
|
||||
job.catch(function (err) {
|
||||
expect(err).toBe(error);
|
||||
done();
|
||||
});
|
||||
job.catch(function (err) {
|
||||
expect(err).toBe(error);
|
||||
done();
|
||||
});
|
||||
|
||||
client.socket.emit(jobID, {
|
||||
error: error,
|
||||
});
|
||||
});
|
||||
client.socket.emit(jobID, {
|
||||
error: error,
|
||||
});
|
||||
});
|
||||
|
||||
it('should unsubscribe once finished', function () {
|
||||
var socket = client.socket;
|
||||
socket.on('run', spy);
|
||||
client.run(function () {});
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
expect(socket.listenerCount(jobID)).toBe(1);
|
||||
socket.emit(jobID, {});
|
||||
expect(socket.listenerCount(jobID)).toBe(0);
|
||||
});
|
||||
it('should unsubscribe once finished', function () {
|
||||
var socket = client.socket;
|
||||
socket.on('run', spy);
|
||||
client.run(function () {});
|
||||
var jobID = spy.calls[0].arguments[1];
|
||||
expect(socket.listenerCount(jobID)).toBe(1);
|
||||
socket.emit(jobID, {});
|
||||
expect(socket.listenerCount(jobID)).toBe(0);
|
||||
});
|
||||
|
||||
it('should resolve if disconnected', function (done) {
|
||||
var job = client.run(function () {});
|
||||
job.then(done);
|
||||
client.socket.emit('disconnect');
|
||||
});
|
||||
it('should resolve if disconnected', function (done) {
|
||||
var job = client.run(function () {});
|
||||
job.then(done);
|
||||
client.socket.emit('disconnect');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
456
test/index.js
456
test/index.js
@@ -8,273 +8,273 @@ var Promise = require('bluebird');
|
||||
var expect = require('chai').expect;
|
||||
|
||||
describe('A clientList', function () {
|
||||
var list, client;
|
||||
var list, client;
|
||||
|
||||
beforeEach(function () {
|
||||
list = new ClientList();
|
||||
client = new Client({
|
||||
name: 'Node.js'
|
||||
});
|
||||
});
|
||||
beforeEach(function () {
|
||||
list = new ClientList();
|
||||
client = new Client({
|
||||
name: 'Node.js'
|
||||
});
|
||||
});
|
||||
|
||||
it('should emit when a client is added', function () {
|
||||
var fired = false;
|
||||
list.on('add', function () {
|
||||
fired = true;
|
||||
}).add(client);
|
||||
expect(fired).to.eq(true);
|
||||
});
|
||||
it('should emit when a client is added', function () {
|
||||
var fired = false;
|
||||
list.on('add', function () {
|
||||
fired = true;
|
||||
}).add(client);
|
||||
expect(fired).to.eq(true);
|
||||
});
|
||||
|
||||
it('should emit when a client is removed', function () {
|
||||
var fired = false;
|
||||
list.on('remove', function () {
|
||||
fired = true;
|
||||
})
|
||||
.add(client)
|
||||
.remove(client);
|
||||
expect(fired).to.eq(true);
|
||||
});
|
||||
it('should emit when a client is removed', function () {
|
||||
var fired = false;
|
||||
list.on('remove', function () {
|
||||
fired = true;
|
||||
})
|
||||
.add(client)
|
||||
.remove(client);
|
||||
expect(fired).to.eq(true);
|
||||
});
|
||||
|
||||
it('should return length when "len()" is called', function () {
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(1);
|
||||
list.remove(client);
|
||||
expect(list.length).to.eq(0);
|
||||
});
|
||||
it('should return length when "len()" is called', function () {
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(1);
|
||||
list.remove(client);
|
||||
expect(list.length).to.eq(0);
|
||||
});
|
||||
|
||||
it('should not add disconnected clients', function () {
|
||||
client.socket.connected = false;
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(0);
|
||||
});
|
||||
it('should not add disconnected clients', function () {
|
||||
client.socket.connected = false;
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(0);
|
||||
});
|
||||
|
||||
it('should remove a client on disconnect', function () {
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(1);
|
||||
client.socket.emit('disconnect');
|
||||
expect(list.length).to.eq(0);
|
||||
});
|
||||
it('should remove a client on disconnect', function () {
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(1);
|
||||
client.socket.emit('disconnect');
|
||||
expect(list.length).to.eq(0);
|
||||
});
|
||||
|
||||
it('should resolve when all clients finish', function (done) {
|
||||
var socket = client.socket;
|
||||
socket.on('run', function (cb, jobID) {
|
||||
socket.emit(jobID, {});
|
||||
});
|
||||
list.add(client).run(function () {})
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
it('should resolve when all clients finish', function (done) {
|
||||
var socket = client.socket;
|
||||
socket.on('run', function (cb, jobID) {
|
||||
socket.emit(jobID, {});
|
||||
});
|
||||
list.add(client).run(function () {})
|
||||
.then(function () {
|
||||
done();
|
||||
})
|
||||
.catch(done);
|
||||
});
|
||||
|
||||
it('should reject a promise if an error is sent', function (done) {
|
||||
client.socket.on('run', function (cb, job) {
|
||||
client.socket.emit(job, {
|
||||
error: 'fake error',
|
||||
});
|
||||
});
|
||||
list.add(client).run(function () {})
|
||||
.catch(function (err) {
|
||||
expect(err).to.eq('fake error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('should reject a promise if an error is sent', function (done) {
|
||||
client.socket.on('run', function (cb, job) {
|
||||
client.socket.emit(job, {
|
||||
error: 'fake error',
|
||||
});
|
||||
});
|
||||
list.add(client).run(function () {})
|
||||
.catch(function (err) {
|
||||
expect(err).to.eq('fake error');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not emit "add" if it contains the client', function () {
|
||||
var called = 0;
|
||||
list.on('add', function () {
|
||||
called += 1;
|
||||
});
|
||||
list.add(client);
|
||||
list.add(client);
|
||||
expect(called).to.eq(1);
|
||||
});
|
||||
it('should not emit "add" if it contains the client', function () {
|
||||
var called = 0;
|
||||
list.on('add', function () {
|
||||
called += 1;
|
||||
});
|
||||
list.add(client);
|
||||
list.add(client);
|
||||
expect(called).to.eq(1);
|
||||
});
|
||||
|
||||
describe('filter', function () {
|
||||
it('should not mutate the original list', function () {
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(1);
|
||||
list.filter(function () {
|
||||
return false;
|
||||
});
|
||||
expect(list.length).to.eq(1);
|
||||
});
|
||||
describe('filter', function () {
|
||||
it('should not mutate the original list', function () {
|
||||
list.add(client);
|
||||
expect(list.length).to.eq(1);
|
||||
list.filter(function () {
|
||||
return false;
|
||||
});
|
||||
expect(list.length).to.eq(1);
|
||||
});
|
||||
|
||||
it('should return a new, filtered list', function () {
|
||||
list.add(client);
|
||||
var servers = list.filter('Node.js');
|
||||
var browsers = list.filter(function (client) {
|
||||
return client.platform.name !== 'Node.js';
|
||||
});
|
||||
expect(servers.length).to.eq(1);
|
||||
expect(browsers.length).to.eq(0);
|
||||
});
|
||||
it('should return a new, filtered list', function () {
|
||||
list.add(client);
|
||||
var servers = list.filter('Node.js');
|
||||
var browsers = list.filter(function (client) {
|
||||
return client.platform.name !== 'Node.js';
|
||||
});
|
||||
expect(servers.length).to.eq(1);
|
||||
expect(browsers.length).to.eq(0);
|
||||
});
|
||||
|
||||
it('should be reactive to changes to the parent list', function () {
|
||||
var servers = list.filter('Node.js');
|
||||
expect(servers.length).to.eq(0);
|
||||
list.add(client);
|
||||
expect(servers.length).to.eq(1);
|
||||
});
|
||||
});
|
||||
it('should be reactive to changes to the parent list', function () {
|
||||
var servers = list.filter('Node.js');
|
||||
expect(servers.length).to.eq(0);
|
||||
list.add(client);
|
||||
expect(servers.length).to.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('exclusion', function () {
|
||||
it('should not contain excluded clients', function () {
|
||||
list.add(client);
|
||||
var filtered = list.excluding(list);
|
||||
expect(filtered.length).to.eq(0);
|
||||
});
|
||||
describe('exclusion', function () {
|
||||
it('should not contain excluded clients', function () {
|
||||
list.add(client);
|
||||
var filtered = list.excluding(list);
|
||||
expect(filtered.length).to.eq(0);
|
||||
});
|
||||
|
||||
it('should react to removals if they are connected', function () {
|
||||
var decoy = new Client();
|
||||
var exclusion = new ClientList()
|
||||
.add(client)
|
||||
.add(decoy);
|
||||
var filtered = list.excluding(exclusion);
|
||||
list.add(client).add(new Client());
|
||||
expect(filtered.length).to.eq(1);
|
||||
exclusion.remove(client).remove(decoy);
|
||||
expect(filtered.length).to.eq(2);
|
||||
});
|
||||
});
|
||||
it('should react to removals if they are connected', function () {
|
||||
var decoy = new Client();
|
||||
var exclusion = new ClientList()
|
||||
.add(client)
|
||||
.add(decoy);
|
||||
var filtered = list.excluding(exclusion);
|
||||
list.add(client).add(new Client());
|
||||
expect(filtered.length).to.eq(1);
|
||||
exclusion.remove(client).remove(decoy);
|
||||
expect(filtered.length).to.eq(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('number constraint', function () {
|
||||
it('should return no more than the number requested', function () {
|
||||
list.add(client)
|
||||
.add(new Client())
|
||||
.add(new Client());
|
||||
expect(list.pluck(1).length).to.eq(1);
|
||||
});
|
||||
describe('number constraint', function () {
|
||||
it('should return no more than the number requested', function () {
|
||||
list.add(client)
|
||||
.add(new Client())
|
||||
.add(new Client());
|
||||
expect(list.pluck(1).length).to.eq(1);
|
||||
});
|
||||
|
||||
it('should listen for additions', function () {
|
||||
var subset = list.pluck(2);
|
||||
expect(subset.length).not.to.eq(2);
|
||||
list.add(new Client()).add(new Client());
|
||||
expect(subset.length).to.eq(2);
|
||||
list.add(new Client());
|
||||
expect(subset.length).to.eq(2);
|
||||
});
|
||||
it('should listen for additions', function () {
|
||||
var subset = list.pluck(2);
|
||||
expect(subset.length).not.to.eq(2);
|
||||
list.add(new Client()).add(new Client());
|
||||
expect(subset.length).to.eq(2);
|
||||
list.add(new Client());
|
||||
expect(subset.length).to.eq(2);
|
||||
});
|
||||
|
||||
it('should replace a client when it disconnects', function () {
|
||||
var subset = list.pluck(1);
|
||||
list.add(client).add(new Client());
|
||||
expect(subset.length).to.eq(1);
|
||||
client.socket.emit('disconnect');
|
||||
// It should be replaced with
|
||||
// the second connected client.
|
||||
expect(subset.length).to.eq(1);
|
||||
});
|
||||
it('should replace a client when it disconnects', function () {
|
||||
var subset = list.pluck(1);
|
||||
list.add(client).add(new Client());
|
||||
expect(subset.length).to.eq(1);
|
||||
client.socket.emit('disconnect');
|
||||
// It should be replaced with
|
||||
// the second connected client.
|
||||
expect(subset.length).to.eq(1);
|
||||
});
|
||||
|
||||
it('should set a flag whether the constraint is met', function () {
|
||||
var subset = list.pluck(1);
|
||||
expect(subset.atCapacity).to.eq(false);
|
||||
list.add(client);
|
||||
expect(subset.atCapacity).to.eq(true);
|
||||
client.socket.emit('disconnect');
|
||||
expect(subset.atCapacity).to.eq(false);
|
||||
});
|
||||
it('should set a flag whether the constraint is met', function () {
|
||||
var subset = list.pluck(1);
|
||||
expect(subset.atCapacity).to.eq(false);
|
||||
list.add(client);
|
||||
expect(subset.atCapacity).to.eq(true);
|
||||
client.socket.emit('disconnect');
|
||||
expect(subset.atCapacity).to.eq(false);
|
||||
});
|
||||
|
||||
it('should play well with exclusions', function () {
|
||||
var bob, alice = list.pluck(1);
|
||||
bob = list.excluding(alice).pluck(1);
|
||||
list.add(client)
|
||||
.add(new Client())
|
||||
.add(new Client());
|
||||
expect(alice.length).to.eq(1);
|
||||
expect(bob.length).to.eq(1);
|
||||
});
|
||||
});
|
||||
it('should play well with exclusions', function () {
|
||||
var bob, alice = list.pluck(1);
|
||||
bob = list.excluding(alice).pluck(1);
|
||||
list.add(client)
|
||||
.add(new Client())
|
||||
.add(new Client());
|
||||
expect(alice.length).to.eq(1);
|
||||
expect(bob.length).to.eq(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('minimum qualifier', function () {
|
||||
describe('minimum qualifier', function () {
|
||||
|
||||
it('should resolve when the minimum is reached', function () {
|
||||
var promise = list.atLeast(1);
|
||||
var called = false;
|
||||
promise.then(function () {
|
||||
called = true;
|
||||
});
|
||||
expect(called).to.eq(false);
|
||||
list.add(new Client());
|
||||
it('should resolve when the minimum is reached', function () {
|
||||
var promise = list.atLeast(1);
|
||||
var called = false;
|
||||
promise.then(function () {
|
||||
called = true;
|
||||
});
|
||||
expect(called).to.eq(false);
|
||||
list.add(new Client());
|
||||
|
||||
// Mocha will wait for this to resolve.
|
||||
return promise;
|
||||
});
|
||||
// Mocha will wait for this to resolve.
|
||||
return promise;
|
||||
});
|
||||
|
||||
it('should resolve if the min is already reached', function () {
|
||||
var promise = list.atLeast(0);
|
||||
it('should resolve if the min is already reached', function () {
|
||||
var promise = list.atLeast(0);
|
||||
|
||||
// This will time out if unresolved.
|
||||
return promise;
|
||||
});
|
||||
// This will time out if unresolved.
|
||||
return promise;
|
||||
});
|
||||
|
||||
it('should resolve to undefined', function () {
|
||||
function validate (arg) {
|
||||
expect(arg).to.eq(undefined);
|
||||
}
|
||||
var immediate = list.atLeast(0).then(validate);
|
||||
var later = list.atLeast(1).then(validate);
|
||||
it('should resolve to undefined', function () {
|
||||
function validate (arg) {
|
||||
expect(arg).to.eq(undefined);
|
||||
}
|
||||
var immediate = list.atLeast(0).then(validate);
|
||||
var later = list.atLeast(1).then(validate);
|
||||
|
||||
list.add(new Client());
|
||||
list.add(new Client());
|
||||
|
||||
return Promise.all([immediate, later]);
|
||||
});
|
||||
return Promise.all([immediate, later]);
|
||||
});
|
||||
|
||||
it('should resolve if it has more than enough', function () {
|
||||
list.add(new Client()).add(new Client());
|
||||
it('should resolve if it has more than enough', function () {
|
||||
list.add(new Client()).add(new Client());
|
||||
|
||||
return list.atLeast(1);
|
||||
});
|
||||
return list.atLeast(1);
|
||||
});
|
||||
|
||||
it('should unsubscribe after resolving', function () {
|
||||
list.atLeast(1);
|
||||
expect(list.listenerCount('add')).to.eq(1);
|
||||
list.add(new Client());
|
||||
expect(list.listenerCount('add')).to.eq(0);
|
||||
});
|
||||
it('should unsubscribe after resolving', function () {
|
||||
list.atLeast(1);
|
||||
expect(list.listenerCount('add')).to.eq(1);
|
||||
list.add(new Client());
|
||||
expect(list.listenerCount('add')).to.eq(0);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('The ClientList constructor', function () {
|
||||
var list1, list2, client1, client2;
|
||||
var list1, list2, client1, client2;
|
||||
|
||||
beforeEach(function () {
|
||||
list1 = new ClientList();
|
||||
list2 = new ClientList();
|
||||
client1 = new Client();
|
||||
client2 = new Client();
|
||||
list1.add(client1);
|
||||
list2.add(client2);
|
||||
});
|
||||
beforeEach(function () {
|
||||
list1 = new ClientList();
|
||||
list2 = new ClientList();
|
||||
client1 = new Client();
|
||||
client2 = new Client();
|
||||
list1.add(client1);
|
||||
list2.add(client2);
|
||||
});
|
||||
|
||||
it('should accept an array of clientLists', function () {
|
||||
var list = new ClientList([list1, list2]);
|
||||
it('should accept an array of clientLists', function () {
|
||||
var list = new ClientList([list1, list2]);
|
||||
|
||||
// it should contain both clients
|
||||
expect(list.get(client1.socket.id)).to.eq(client1);
|
||||
expect(list.get(client2.socket.id)).to.eq(client2);
|
||||
});
|
||||
// it should contain both clients
|
||||
expect(list.get(client1.socket.id)).to.eq(client1);
|
||||
expect(list.get(client2.socket.id)).to.eq(client2);
|
||||
});
|
||||
|
||||
it('should reactively add new clients from source lists', function () {
|
||||
var list = new ClientList([list1, list2]);
|
||||
var client3 = new Client();
|
||||
expect(list.get(client3.socket.id)).to.eq(null);
|
||||
list1.add(client3);
|
||||
expect(list.get(client3.socket.id)).to.eq(client3);
|
||||
});
|
||||
it('should reactively add new clients from source lists', function () {
|
||||
var list = new ClientList([list1, list2]);
|
||||
var client3 = new Client();
|
||||
expect(list.get(client3.socket.id)).to.eq(null);
|
||||
list1.add(client3);
|
||||
expect(list.get(client3.socket.id)).to.eq(client3);
|
||||
});
|
||||
|
||||
it('should be subclassable', function () {
|
||||
function Sub() {
|
||||
ClientList.call(this);
|
||||
}
|
||||
Sub.prototype = new ClientList();
|
||||
Sub.prototype.constructor = Sub;
|
||||
it('should be subclassable', function () {
|
||||
function Sub() {
|
||||
ClientList.call(this);
|
||||
}
|
||||
Sub.prototype = new ClientList();
|
||||
Sub.prototype.constructor = Sub;
|
||||
|
||||
var sub = new Sub();
|
||||
expect(sub).to.be.an.instanceof(Sub);
|
||||
var sub = new Sub();
|
||||
expect(sub).to.be.an.instanceof(Sub);
|
||||
|
||||
// chained inheritance
|
||||
expect(sub.filter('Firefox')).to.be.an.instanceof(Sub);
|
||||
expect(sub.pluck(1)).to.be.an.instanceof(Sub);
|
||||
});
|
||||
// chained inheritance
|
||||
expect(sub.filter('Firefox')).to.be.an.instanceof(Sub);
|
||||
expect(sub.pluck(1)).to.be.an.instanceof(Sub);
|
||||
});
|
||||
});
|
||||
|
||||
20
test/mock.js
20
test/mock.js
@@ -3,19 +3,19 @@ var Emitter = require('events');
|
||||
var Client = require('../src/Client');
|
||||
|
||||
function mock (platform) {
|
||||
var rand = Math.random();
|
||||
var rand = Math.random();
|
||||
|
||||
// Fake a socket.io socket.
|
||||
var socket = new Emitter();
|
||||
socket.connected = true;
|
||||
socket.id = rand.toString(36).slice(2);
|
||||
// Fake a socket.io socket.
|
||||
var socket = new Emitter();
|
||||
socket.connected = true;
|
||||
socket.id = rand.toString(36).slice(2);
|
||||
|
||||
return new Client({
|
||||
socket: socket,
|
||||
platform: platform || {},
|
||||
});
|
||||
return new Client({
|
||||
socket: socket,
|
||||
platform: platform || {},
|
||||
});
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
Client: mock,
|
||||
Client: mock,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user