Outsource eslint config

Yipes, looks like I still had a bunch of rules stuffed in my
eslintrc file. A while ago I pulled those out into a standalone eslint
config. This commit adds that config, removes all the duplicated rules,
and fixes the errors ensuing from revised formatting preferences.
This commit is contained in:
Jesse Gibson
2016-11-25 16:05:59 -07:00
parent c84158af0a
commit 37a3838912
9 changed files with 426 additions and 624 deletions

View File

@@ -1,229 +1,17 @@
module.exports = {
"env": {
"browser": true,
"commonjs": true,
"node": true
},
"extends": "eslint:recommended",
"rules": {
"accessor-pairs": "error",
"array-bracket-spacing": [
"error",
"never"
],
"array-callback-return": "error",
"arrow-body-style": "error",
"arrow-parens": "error",
"arrow-spacing": "error",
"block-scoped-var": "error",
"block-spacing": "error",
"brace-style": [
"error",
"1tbs"
],
"callback-return": "off",
"camelcase": "error",
"comma-spacing": [
"error",
{
"after": true,
"before": false
}
],
"comma-style": [
"error",
"last"
],
"complexity": "error",
"computed-property-spacing": [
"error",
"never"
],
"consistent-return": "error",
"consistent-this": "off",
"curly": "error",
"default-case": "error",
"dot-location": [
"error",
"property"
],
"dot-notation": "error",
"eol-last": "error",
"eqeqeq": "error",
"func-names": "off",
"func-style": [
"error",
"declaration"
],
"generator-star-spacing": "error",
"global-require": "error",
"guard-for-in": "error",
"handle-callback-err": "error",
"id-blacklist": "error",
"id-length": "error",
"id-match": "error",
"indent": "off",
"init-declarations": "off",
"jsx-quotes": "error",
"key-spacing": "error",
"keyword-spacing": [
"error",
{
"after": true,
"before": true
}
],
"linebreak-style": [
"error",
"unix"
],
"lines-around-comment": "error",
"max-depth": "error",
"max-len": "off",
"max-nested-callbacks": "error",
"max-params": "error",
"max-statements": "off",
"max-statements-per-line": "error",
"new-cap": "error",
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "off",
"newline-per-chained-call": "error",
"no-alert": "error",
"no-array-constructor": "error",
"no-bitwise": "error",
"no-caller": "error",
"no-catch-shadow": "error",
"no-confusing-arrow": "error",
"no-console": "off",
"no-continue": "off",
"no-div-regex": "error",
"no-duplicate-imports": "error",
"no-else-return": "error",
"no-empty-function": "off",
"no-eq-null": "error",
"no-eval": "error",
"no-extend-native": "off",
"no-extra-bind": "error",
"no-extra-label": "error",
"no-extra-parens": "off",
"no-floating-decimal": "error",
"no-implicit-globals": "error",
"no-implied-eval": "error",
"no-inline-comments": "error",
"no-invalid-this": "off",
"no-iterator": "error",
"no-label-var": "error",
"no-labels": "error",
"no-lone-blocks": "error",
"no-lonely-if": "error",
"no-loop-func": "error",
"no-magic-numbers": "off",
"no-mixed-requires": "error",
"no-multi-spaces": "error",
"no-multi-str": "error",
"no-multiple-empty-lines": "error",
"no-native-reassign": "error",
"no-negated-condition": "error",
"no-nested-ternary": "error",
"no-new": "error",
"no-new-func": "error",
"no-new-object": "error",
"no-new-require": "error",
"no-new-wrappers": "error",
"no-octal-escape": "error",
"no-param-reassign": "off",
"no-path-concat": "error",
"no-plusplus": "error",
"no-process-env": "error",
"no-process-exit": "error",
"no-proto": "error",
"no-restricted-globals": "error",
"no-restricted-imports": "error",
"no-restricted-modules": "error",
"no-restricted-syntax": "error",
"no-return-assign": "error",
"no-script-url": "error",
"no-self-compare": "error",
"no-sequences": "error",
"no-shadow": "off",
"no-shadow-restricted-names": "error",
"no-spaced-func": "error",
"no-sync": "error",
"no-ternary": "off",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef-init": "error",
"no-undefined": "off",
"no-underscore-dangle": [
"error",
{
"allowAfterThis": true
}
],
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
"no-unused-expressions": "error",
"no-use-before-define": "error",
"no-useless-call": "error",
"no-useless-concat": "error",
"no-useless-constructor": "error",
"no-useless-escape": "error",
"no-var": "off",
"no-void": "error",
"no-warning-comments": "error",
"no-whitespace-before-property": "error",
"no-with": "error",
"object-curly-spacing": [
"error",
"never"
],
"object-shorthand": "off",
"one-var": "off",
"one-var-declaration-per-line": "off",
"operator-assignment": [
"error",
"always"
],
"operator-linebreak": "error",
"padded-blocks": "off",
"prefer-arrow-callback": "off",
"prefer-const": "error",
"prefer-reflect": "off",
"prefer-rest-params": "off",
"prefer-spread": "off",
"prefer-template": "off",
"quote-props": "off",
"quotes": [
"error",
"single"
],
"radix": "error",
"require-jsdoc": "off",
"require-yield": "error",
"semi": "error",
"semi-spacing": "error",
"sort-imports": "error",
"sort-vars": "off",
"space-before-blocks": "error",
"space-before-function-paren": "off",
"space-in-parens": [
"error",
"never"
],
"space-infix-ops": "error",
"space-unary-ops": "error",
"spaced-comment": "off",
"strict": "error",
"template-curly-spacing": "error",
"valid-jsdoc": "error",
"vars-on-top": "off",
"wrap-iife": "error",
"wrap-regex": "error",
"yield-star-spacing": "error",
"yoda": [
"error",
"never"
]
}
'use strict';
var eslint = exports;
eslint.env = {
node: true,
commonjs: true,
};
eslint.extends = [
'eslint:recommended',
'llama',
];
eslint.rules = {
'no-var': 'off',
'prefer-template': 'off',
};

View File

@@ -35,6 +35,7 @@
"devDependencies": {
"chai": "^3.5.0",
"eslint": "^3.0.1",
"eslint-config-llama": "^3.0.0",
"expect": "^1.20.2",
"mocha": "^3.0.1"
}

View File

@@ -15,10 +15,10 @@ function Client (client) {
/** Basic input validation. */
if (!client.socket) {
throw new Error('Invalid "client.socket" property.');
throw new Error('Invalid "client.socket" property.');
}
if (!client.platform) {
throw new Error('Invalid "client.platform" property.');
throw new Error('Invalid "client.platform" property.');
}
this.socket = client.socket;
@@ -38,37 +38,37 @@ Client.prototype = {
* rejects if it throws an error.
*/
run: function (job, props) {
if (typeof job !== 'function') {
throw new TypeError(
if (typeof job !== 'function') {
throw new TypeError(
'Expected job "' + job + '" to be a function.'
);
}
}
var source = String(job);
var jobID = Math.random()
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);
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;
},
};

View File

@@ -10,7 +10,7 @@ var Promise = require('bluebird');
* @class ClientList
* @augments EventEmitter
*/
function ClientList(lists) {
function ClientList (lists) {
var list = this;
Emitter.call(this);
list.clients = {};
@@ -19,11 +19,11 @@ function ClientList(lists) {
/** See if the user passed an array. */
if (lists instanceof Array) {
lists.forEach(function (list) {
lists.forEach(function (list) {
/** Add each client, listening for additions. */
list.each(add).on('add', add);
});
list.each(add).on('add', add);
});
}
}
@@ -51,9 +51,9 @@ API.chain = function (list) {
API.each = function (cb) {
var key;
for (key in this.clients) {
if (this.clients.hasOwnProperty(key)) {
cb(this.clients[key], key, this);
}
if (this.clients.hasOwnProperty(key)) {
cb(this.clients[key], key, this);
}
}
return this;
};
@@ -75,7 +75,7 @@ API.add = function (client) {
* or those already in the list.
*/
if (!socket.connected || this.get(socket.id)) {
return this;
return this;
}
/** Add the client. */
@@ -83,7 +83,7 @@ API.add = function (client) {
/** Remove on disconnect. */
socket.on('disconnect', function () {
list.remove(client);
list.remove(client);
});
/** Fire the 'add' event. */
@@ -103,10 +103,10 @@ API.remove = function (client) {
if (client.socket.id in this.clients) {
/** Remove the client. */
delete this.clients[client.socket.id];
delete this.clients[client.socket.id];
/** Fire the 'remove' event. */
this.emit('remove', client, client.socket.id);
this.emit('remove', client, client.socket.id);
}
return this;
@@ -132,20 +132,26 @@ API.filter = function (query) {
/** Create a new target list. */
var list = this.chain();
/**
* Adds matching clients to the new filtered list.
* @param {Client} client - A connected client.
* @param {String} ID - The client identifier.
* @return {undefined}
*/
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)) {
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);
}
}
}
/**
@@ -170,9 +176,9 @@ API.excluding = function (exclude) {
* Remember .filter is reactive.
*/
var list = this.filter(function (client) {
var excluded = exclude.get(client.socket.id);
var excluded = exclude.get(client.socket.id);
return !excluded;
return !excluded;
});
var self = this;
@@ -182,13 +188,13 @@ API.excluding = function (exclude) {
* 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);
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;
@@ -206,8 +212,8 @@ API.run = function (job, props) {
/** Run the job on each client. */
this.each(function (client) {
var promise = client.run(job, props);
jobs.push(promise);
var promise = client.run(job, props);
jobs.push(promise);
});
/** Wait for all jobs to finish. */
@@ -224,22 +230,22 @@ API.atLeast = function (min) {
/** Check to see if we already have enough. */
if (list.length >= min) {
return Promise.resolve();
return Promise.resolve();
}
return new Promise(function (resolve) {
/** Wait for new clients. */
list.on('add', function cb () {
list.on('add', function cb () {
/** If we have enough... */
if (list.length >= min) {
if (list.length >= min) {
/** Unsubscribe and resolve. */
list.removeListener('add', cb);
resolve();
}
});
list.removeListener('add', cb);
resolve();
}
});
});
};
@@ -260,23 +266,23 @@ API.pluck = function (num) {
* @param {Object} client - A client object.
* @return {undefined}
*/
function measure(client) {
if (!list.atCapacity) {
list.add(client);
}
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;
}
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);
list.atCapacity = false;
self.each(measure);
});
/** Add as many clients as we can. */
@@ -295,18 +301,18 @@ Object.defineProperty(API, 'length', {
get: function () {
/** Feature detect Object.keys. */
if (Object.keys instanceof Function) {
return Object.keys(this.clients).length;
}
if (Object.keys instanceof Function) {
return Object.keys(this.clients).length;
}
/** Fall back to iterating. */
var length = 0;
this.each(function () {
length += 1;
});
var length = 0;
this.each(function () {
length += 1;
});
return length;
}
return length;
},
});
module.exports = ClientList;

View File

@@ -1,4 +1,4 @@
/*eslint eqeqeq: "off"*/
/* eslint eqeqeq: "off"*/
'use strict';
/**
@@ -11,34 +11,34 @@
* @param {Object} platform - A platform.js object.
* @return {Boolean} - Whether the platform matches the query.
*/
function match(query, platform) {
function match (query, platform) {
var key, value, matches = true;
/** Check all query options. */
for (key in query) {
if (!(query.hasOwnProperty(key))) {
continue;
}
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') {
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) {
matches = matches && platform[key] == value;
} else if (value instanceof Object) {
/** Recursively match deeper queries. */
return match(value, platform[key] || {});
}
return match(value, platform[key] || {});
}
}
/** Whether the query matches. */

View File

@@ -1,4 +1,4 @@
/*eslint-disable no-sync*/
/* eslint-disable no-sync*/
'use strict';
var io = require('socket.io');
@@ -16,12 +16,12 @@ var client;
*/
Object.defineProperty(panic, 'client', {
get: function () {
if (!client) {
client = fs.readFileSync(file, 'utf8');
}
if (!client) {
client = fs.readFileSync(file, 'utf8');
}
return client;
}
return client;
},
});
/**
@@ -31,9 +31,9 @@ Object.defineProperty(panic, 'client', {
* @param {Object} res - http response object.
* @return {undefined}
*/
function serve(req, res) {
function serve (req, res) {
if (req.url === '/panic.js') {
res.end(panic.client);
res.end(panic.client);
}
}
@@ -43,17 +43,17 @@ function serve(req, res) {
* @param {Socket} socket - A socket.io websocket.
* @return {undefined}
*/
function upgrade(socket) {
function upgrade (socket) {
socket.on('handshake', function (platform) {
/** Create a new panic client. */
var client = new Client({
socket: socket,
platform: platform,
});
var client = new Client({
socket: socket,
platform: platform,
});
/** Add the new client. */
clients.add(client);
clients.add(client);
});
}
@@ -65,10 +65,10 @@ function upgrade(socket) {
* If none is provided, a server will be created.
* @return {Server} - Either the server passed, or a new server.
*/
function open(server) {
function open (server) {
if (!(server instanceof Server)) {
server = new Server();
server = new Server();
}
/** Handle /panic.js route. */

View File

@@ -1,4 +1,5 @@
/* eslint-env mocha*/
/* eslint-disable require-jsdoc */
/* eslint-env mocha */
'use strict';
var Client = require('../src/Client');
var Emitter = require('events');
@@ -9,155 +10,155 @@ describe('A client', function () {
var client, socket, platform;
beforeEach(function () {
socket = new Emitter();
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);
expect(client.socket).toBe(socket);
});
it('should expose the platform', function () {
expect(client.platform).toBe(platform);
expect(client.platform).toBe(platform);
});
it('should validate the socket', function () {
function fail () {
return new Client({
// Missing "socket".
platform: platform,
});
}
expect(fail).toThrow();
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();
function fail () {
return new Client({
socket: new Emitter(),
// Missing "platform".
});
}
expect(fail).toThrow();
});
describe('"run" call', function () {
var spy;
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');
});
});

View File

@@ -1,4 +1,5 @@
/*globals beforeEach, describe, it*/
/* eslint-disable require-jsdoc */
/* eslint-env mocha */
'use strict';
var mock = require('./mock');
var Client = mock.Client;
@@ -11,56 +12,56 @@ describe('A clientList', function () {
var list, client;
beforeEach(function () {
list = new ClientList();
client = new Client({
name: 'Node.js'
});
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);
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;
})
var fired = false;
list.on('remove', function () {
fired = true;
})
.add(client)
.remove(client);
expect(fired).to.eq(true);
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);
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);
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);
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 () {})
var socket = client.socket;
socket.on('run', function (cb, jobID) {
socket.emit(jobID, {});
});
list.add(client).run(function () {})
.then(function () {
done();
})
@@ -68,12 +69,12 @@ describe('A clientList', function () {
});
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 () {})
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();
@@ -81,156 +82,156 @@ describe('A clientList', function () {
});
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);
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);
});
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);
});
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()
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);
});
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)
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);
});
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 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);
});
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)
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);
});
expect(alice.length).to.eq(1);
expect(bob.length).to.eq(1);
});
});
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;
});
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;
});
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);
});
});
});
@@ -239,42 +240,42 @@ describe('The ClientList constructor', function () {
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);
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]);
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);
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);
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;
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);
expect(sub.filter('Firefox')).to.be.an.instanceof(Sub);
expect(sub.pluck(1)).to.be.an.instanceof(Sub);
});
});

View File

@@ -2,6 +2,11 @@
var Emitter = require('events');
var Client = require('../src/Client');
/**
* Creates a client wrapping a new fake websocket.
* @param {Object} [platform] - A platform.js-style object.
* @return {Client} - Draws from a fake socket instance.
*/
function mock (platform) {
var rand = Math.random();
@@ -11,8 +16,8 @@ function mock (platform) {
socket.id = rand.toString(36).slice(2);
return new Client({
socket: socket,
platform: platform || {},
socket: socket,
platform: platform || {},
});
}