Files
panic-server/test/index.js
Jesse Gibson 7425aaac2d Rename .len() to .length, allow subclassing, export client bundle.
Instead of calling a method to find the length of a list, you can use a
property (which is a getter under the hood, doing the same thing as
`.len()`). This is cleaner and more intuitive, aligning itself more with
arrays.
Subclassing is now facilitated by a new method, `.chain`. It creates a
new list instance by calling the constructor property, instead of
statically creating a new ClientList instance. This allows you to
create subclasses that inherit from ClientList, without losing that
inheritance when calling `.filter` or `.pluck` (methods which create new
list instances).
The client bundle is now exported lazily, so when you import panic,
there's a `client` getter which memoizes a fs call for the client code.
This allows compatibility on pre-3.0 versions of npm, where other packages
might not be able to recursively find `panic-client`. Also, since it's a
getter, it doesn't do the file system call until it's needed.
2016-05-28 21:16:23 -06:00

227 lines
5.8 KiB
JavaScript

/*globals beforeEach, describe, it*/
'use strict';
var mock = require('./mock');
var Client = mock.Client;
var ClientList = require('../src/ClientList');
var expect = require('chai').expect;
describe('A clientList', function () {
var list, client;
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 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 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 resolve a promise when all clients finish', function (done) {
client.socket.on('run', function (cb, jobID) {
client.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, '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);
});
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 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 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);
});
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 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);
});
});
});
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);
});
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 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;
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);
});
});