2014-03-05 05:03:31 +08:00
|
|
|
"use strict";
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-05-08 23:57:20 +08:00
|
|
|
var Device = source('device'),
|
|
|
|
Connection = source('connection'),
|
2014-06-06 03:11:37 +08:00
|
|
|
Robot = source("robot"),
|
|
|
|
Utils = source('utils');
|
2014-05-07 10:18:13 +08:00
|
|
|
|
2014-05-08 23:57:20 +08:00
|
|
|
describe("Robot", function() {
|
2014-10-01 03:22:00 +08:00
|
|
|
var work, extraFunction, robot;
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-10-01 03:22:00 +08:00
|
|
|
beforeEach(function() {
|
|
|
|
work = spy();
|
|
|
|
extraFunction = spy();
|
2014-02-28 09:39:02 +08:00
|
|
|
|
2014-10-01 03:22:00 +08:00
|
|
|
robot = new Robot({
|
|
|
|
name: "Robby",
|
|
|
|
work: work,
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-10-01 03:22:00 +08:00
|
|
|
extraFunction: extraFunction,
|
|
|
|
extraValue: "Hello World",
|
|
|
|
|
|
|
|
master: { master: true }
|
|
|
|
});
|
2013-10-25 05:25:42 +08:00
|
|
|
});
|
|
|
|
|
2014-03-05 05:03:31 +08:00
|
|
|
describe("constructor", function() {
|
|
|
|
describe("name", function() {
|
|
|
|
context("if provided", function() {
|
|
|
|
it("is set to the passed value", function() {
|
|
|
|
expect(robot.name).to.be.eql("Robby")
|
|
|
|
});
|
|
|
|
});
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-03-05 05:03:31 +08:00
|
|
|
context("if not provided", function() {
|
2014-07-05 00:49:29 +08:00
|
|
|
beforeEach(function() {
|
2014-05-08 23:57:20 +08:00
|
|
|
stub(Robot, 'randomName').returns("New Robot");
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-07-05 00:49:29 +08:00
|
|
|
afterEach(function() {
|
2014-05-08 23:57:20 +08:00
|
|
|
Robot.randomName.restore();
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-03-05 05:03:31 +08:00
|
|
|
it("is set to a random name", function() {
|
2014-05-08 23:57:20 +08:00
|
|
|
var bot = new Robot({});
|
2014-03-05 05:03:31 +08:00
|
|
|
expect(bot.name).to.be.eql("New Robot");
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets @master to the passed Master object", function() {
|
|
|
|
expect(robot.master).to.be.eql({ master: true });
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets @connections to an empty object by default", function() {
|
|
|
|
expect(robot.connections).to.be.eql({});
|
|
|
|
});
|
2014-02-28 06:52:25 +08:00
|
|
|
|
2014-03-05 05:03:31 +08:00
|
|
|
it("sets @devices to an empty object by default", function() {
|
|
|
|
expect(robot.devices).to.be.eql({});
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets @work to the passed work function", function() {
|
|
|
|
expect(robot.work).to.be.eql(work);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets other obj params as values on the robot", function() {
|
|
|
|
expect(robot.extraFunction).to.be.eql(extraFunction);
|
|
|
|
expect(robot.extraValue).to.be.eql("Hello World");
|
2014-02-28 06:52:25 +08:00
|
|
|
});
|
2014-07-12 01:40:45 +08:00
|
|
|
|
|
|
|
context("if there are devices but no connections", function() {
|
|
|
|
it('throws an error', function() {
|
|
|
|
var fn = function() {
|
|
|
|
return new Robot({
|
|
|
|
name: 'BrokenBot',
|
|
|
|
device: { name: 'ping', driver: 'ping' }
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
expect(fn).to.throw(Error, "No connections specified");
|
|
|
|
});
|
|
|
|
});
|
2014-08-06 09:41:57 +08:00
|
|
|
|
|
|
|
context("if no commands are provided", function() {
|
|
|
|
var robot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
robot = new Robot({
|
|
|
|
name: 'NewBot',
|
|
|
|
otherThings: { more: 'details' },
|
|
|
|
sayHello: function() { return "Hello!" }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets #commands to the additionally provided functions", function() {
|
|
|
|
expect(robot.commands).to.be.eql({ sayHello: robot.sayHello });
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context("if a commands function is provided", function() {
|
|
|
|
var robot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
robot = new Robot({
|
|
|
|
name: 'NewBot',
|
|
|
|
|
|
|
|
sayHello: function() { return this.name + " says hello" },
|
|
|
|
|
|
|
|
commands: function() {
|
|
|
|
return {
|
|
|
|
say_hello: this.sayHello
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets #commands to the returned object", function() {
|
|
|
|
expect(robot.commands.say_hello).to.be.eql(robot.sayHello);
|
|
|
|
});
|
|
|
|
|
|
|
|
context("if the function doesn't return an object", function() {
|
|
|
|
var fn;
|
|
|
|
beforeEach(function() {
|
|
|
|
fn = function() {
|
|
|
|
new Robot({
|
|
|
|
name: 'NewBot',
|
|
|
|
|
|
|
|
commands: function() {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
it("throws an error", function() {
|
2014-10-28 04:40:38 +08:00
|
|
|
expect(fn).to.throw(Error, "#commands function must return an object");
|
2014-08-06 09:41:57 +08:00
|
|
|
});
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
context("if a commands object is provided", function() {
|
|
|
|
var robot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
robot = new Robot({
|
|
|
|
name: 'NewBot',
|
|
|
|
|
|
|
|
sayHello: function() { return this.name + " says hello" },
|
|
|
|
|
|
|
|
commands: {
|
|
|
|
say_hello: function() {}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it("sets #commands to the provided object", function() {
|
|
|
|
expect(robot.commands.say_hello).to.be.a('function');
|
|
|
|
});
|
|
|
|
});
|
2014-10-28 04:40:38 +08:00
|
|
|
|
|
|
|
context("arbitrary arguments", function() {
|
|
|
|
beforeEach(function() {
|
|
|
|
robot = new Robot({
|
|
|
|
name: 'NewBot',
|
|
|
|
|
|
|
|
hiThere: 'hi there',
|
|
|
|
|
|
|
|
sayHi: function() {
|
|
|
|
return 'hi';
|
|
|
|
},
|
|
|
|
|
|
|
|
start: "start"
|
|
|
|
})
|
|
|
|
});
|
|
|
|
|
|
|
|
it("passes them through if they don't conflict with built-ins", function() {
|
|
|
|
expect(robot.hiThere).to.be.eql("hi there");
|
|
|
|
expect(robot.sayHi()).to.be.eql("hi");
|
|
|
|
});
|
|
|
|
|
|
|
|
it("doesn't work if they conflict with built-in properties", function() {
|
|
|
|
expect(robot.start).to.be.a('function');
|
|
|
|
});
|
|
|
|
});
|
2014-02-28 06:52:25 +08:00
|
|
|
});
|
2014-03-01 12:27:05 +08:00
|
|
|
|
2014-06-05 07:33:20 +08:00
|
|
|
describe("all work and no play", function() {
|
|
|
|
var play = spy();
|
|
|
|
|
|
|
|
var playBot = new Robot({
|
|
|
|
play: play
|
|
|
|
});
|
|
|
|
|
|
|
|
it('makes Jack a dull boy', function() {
|
|
|
|
expect(playBot.work).to.be.eql(play);
|
|
|
|
})
|
2014-08-06 09:41:57 +08:00
|
|
|
});
|
2014-06-05 07:33:20 +08:00
|
|
|
|
2014-06-07 05:11:35 +08:00
|
|
|
describe("#toJSON", function() {
|
2014-05-08 23:57:20 +08:00
|
|
|
var bot = new Robot({
|
2014-03-01 12:27:05 +08:00
|
|
|
connection: { name: 'loopback', adaptor: 'loopback' },
|
|
|
|
device: { name: 'ping', driver: 'ping' }
|
|
|
|
});
|
|
|
|
|
2014-06-07 05:11:35 +08:00
|
|
|
var json = bot.toJSON();
|
2014-03-01 12:27:05 +08:00
|
|
|
|
|
|
|
it("returns an object", function() {
|
2014-06-07 05:11:35 +08:00
|
|
|
expect(json).to.be.a('object');
|
2014-03-01 12:27:05 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it("contains the robot's name", function() {
|
2014-06-07 05:11:35 +08:00
|
|
|
expect(json.name).to.eql(bot.name);
|
2014-03-01 12:27:05 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it("contains the robot's commands", function() {
|
2014-08-06 09:41:57 +08:00
|
|
|
expect(json.commands).to.eql(Object.keys(bot.commands));
|
2014-03-01 12:27:05 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it("contains the robot's devices", function() {
|
2014-07-30 09:49:35 +08:00
|
|
|
expect(json.devices).to.eql([bot.devices.ping]);
|
2014-03-01 12:27:05 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
it("contains the robot's connections", function() {
|
2014-07-30 09:49:35 +08:00
|
|
|
expect(json.connections).to.eql([bot.connections.loopback]);
|
2014-03-01 12:27:05 +08:00
|
|
|
});
|
|
|
|
});
|
2014-03-05 05:03:31 +08:00
|
|
|
|
|
|
|
describe("initConnections", function() {
|
2014-09-05 05:51:44 +08:00
|
|
|
var bot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
bot = new Robot();
|
|
|
|
});
|
|
|
|
|
2014-03-05 05:03:31 +08:00
|
|
|
context("when not passed anything", function() {
|
|
|
|
it("returns immediately", function() {
|
2014-09-05 05:51:44 +08:00
|
|
|
expect(bot.initConnections()).to.be.eql(undefined);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context("when passed a connection object", function() {
|
|
|
|
it("instantiates a new connection with the provided object", function() {
|
|
|
|
var connection = { name: 'loopback', adaptor: 'loopback' };
|
2014-09-05 05:51:44 +08:00
|
|
|
bot.initConnections(connection);
|
|
|
|
expect(bot.connections['loopback']).to.be.instanceOf(Connection);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context("when passed an array of connection objects", function() {
|
|
|
|
it("instantiates a new connection with each of the provided objects", function() {
|
|
|
|
var connections = [{ name: 'loopback', adaptor: 'loopback' }]
|
2014-09-05 05:51:44 +08:00
|
|
|
bot.initConnections(connections);
|
|
|
|
expect(bot.connections['loopback']).to.be.instanceOf(Connection);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("avoids name collisions collisions", function() {
|
|
|
|
bot.initConnections([
|
|
|
|
{ name: 'loopback', adaptor: 'loopback' },
|
|
|
|
{ name: 'loopback', adaptor: 'loopback' }
|
|
|
|
]);
|
|
|
|
|
|
|
|
var keys = Object.keys(bot.connections);
|
|
|
|
expect(keys).to.be.eql(["loopback", "loopback-1"]);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("initDevices", function() {
|
2014-09-05 05:51:44 +08:00
|
|
|
var bot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
2014-11-01 04:48:05 +08:00
|
|
|
bot = new Robot({
|
|
|
|
connection: { name: 'loopback', adaptor: 'loopback' }
|
|
|
|
});
|
2014-09-05 05:51:44 +08:00
|
|
|
});
|
|
|
|
|
2014-03-05 05:03:31 +08:00
|
|
|
context("when not passed anything", function() {
|
|
|
|
it("returns immediately", function() {
|
2014-09-05 05:51:44 +08:00
|
|
|
expect(bot.initDevices()).to.be.eql(undefined);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context("when passed a connection object", function() {
|
2014-09-05 05:51:44 +08:00
|
|
|
afterEach(function() { bot.devices = {}; });
|
2014-03-05 05:03:31 +08:00
|
|
|
|
2014-05-07 10:18:13 +08:00
|
|
|
it("instantiates a new device with the provided object", function() {
|
2014-03-05 05:03:31 +08:00
|
|
|
var device = { name: 'ping', driver: 'ping' };
|
2014-09-05 05:51:44 +08:00
|
|
|
bot.initDevices(device);
|
|
|
|
expect(bot.devices['ping']).to.be.instanceOf(Device);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
context("when passed an array of device objects", function() {
|
2014-09-05 05:51:44 +08:00
|
|
|
afterEach(function() { bot.devices = {}; });
|
2014-03-05 05:03:31 +08:00
|
|
|
|
|
|
|
it("instantiates a new device with each of the provided objects", function() {
|
|
|
|
var devices = [{ name: 'ping', driver: 'ping' }]
|
2014-09-05 05:51:44 +08:00
|
|
|
bot.initDevices(devices);
|
|
|
|
|
|
|
|
expect(bot.devices['ping']).to.be.instanceOf(Device);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("avoids name collisions collisions", function() {
|
|
|
|
bot.initDevices([
|
|
|
|
{ name: 'ping', driver: 'ping' },
|
|
|
|
{ name: 'ping', driver: 'ping' }
|
|
|
|
]);
|
2014-05-07 10:18:13 +08:00
|
|
|
|
2014-09-05 05:51:44 +08:00
|
|
|
var keys = Object.keys(bot.devices);
|
|
|
|
expect(keys).to.be.eql(["ping", "ping-1"]);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("#start", function() {
|
2014-07-05 00:49:29 +08:00
|
|
|
beforeEach(function() {
|
2014-03-05 05:03:31 +08:00
|
|
|
stub(robot, 'startConnections').callsArg(0);
|
|
|
|
stub(robot, 'startDevices').callsArg(0);
|
|
|
|
stub(robot, 'emit').returns(null);
|
|
|
|
|
|
|
|
robot.start();
|
|
|
|
});
|
|
|
|
|
2014-07-05 00:49:29 +08:00
|
|
|
afterEach(function() {
|
2014-03-05 05:03:31 +08:00
|
|
|
robot.startConnections.restore();
|
|
|
|
robot.startDevices.restore();
|
|
|
|
robot.emit.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
it("starts the robot's connections", function() {
|
|
|
|
expect(robot.startConnections).to.be.called;
|
|
|
|
});
|
|
|
|
|
|
|
|
it("starts the robot's devices", function() {
|
|
|
|
expect(robot.startDevices).to.be.called;
|
|
|
|
});
|
|
|
|
|
|
|
|
it("starts the robot's work", function() {
|
|
|
|
expect(robot.work).to.be.called;
|
|
|
|
});
|
|
|
|
|
2014-09-30 01:27:52 +08:00
|
|
|
it("emits the 'ready' event", function() {
|
|
|
|
expect(robot.emit).to.be.calledWith("ready", robot)
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
2014-09-09 05:14:11 +08:00
|
|
|
|
|
|
|
it("returns the robot", function() {
|
|
|
|
expect(robot.start()).to.be.eql(robot);
|
|
|
|
});
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
describe("#startConnections", function() {
|
|
|
|
var bot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
2014-05-08 23:57:20 +08:00
|
|
|
bot = new Robot({
|
2014-03-05 05:03:31 +08:00
|
|
|
connections: [
|
|
|
|
{ name: 'alpha', adaptor: 'loopback' },
|
|
|
|
{ name: 'bravo', adaptor: 'loopback' }
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
stub(bot.connections.alpha, 'connect').returns(true);
|
|
|
|
stub(bot.connections.bravo, 'connect').returns(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("runs #connect on each connection", function() {
|
|
|
|
bot.startConnections();
|
|
|
|
|
|
|
|
expect(bot.connections.alpha.connect).to.be.called;
|
|
|
|
expect(bot.connections.bravo.connect).to.be.called;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("#startDevices", function() {
|
|
|
|
var bot;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
2014-05-08 23:57:20 +08:00
|
|
|
bot = new Robot({
|
2014-07-12 01:40:45 +08:00
|
|
|
connection: [{ name: 'loopback', adaptor: 'loopback' }],
|
2014-03-05 05:03:31 +08:00
|
|
|
devices: [
|
|
|
|
{ name: 'alpha', driver: 'ping' },
|
|
|
|
{ name: 'bravo', driver: 'ping' }
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
stub(bot.devices.alpha, 'start').returns(true);
|
|
|
|
stub(bot.devices.bravo, 'start').returns(true);
|
|
|
|
});
|
|
|
|
|
|
|
|
it("runs #start on each device", function() {
|
|
|
|
bot.startDevices();
|
|
|
|
|
|
|
|
expect(bot.devices.alpha.start).to.be.called;
|
|
|
|
expect(bot.devices.bravo.start).to.be.called;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2014-03-12 01:44:11 +08:00
|
|
|
describe("#halt", function() {
|
2014-07-05 00:49:29 +08:00
|
|
|
var bot, device, connection;
|
|
|
|
|
|
|
|
beforeEach(function() {
|
|
|
|
bot = new Robot({
|
|
|
|
device: { name: 'ping', driver: 'ping' },
|
|
|
|
connection: { name: 'loopback', adaptor: 'loopback' }
|
|
|
|
});
|
2014-03-05 05:03:31 +08:00
|
|
|
|
2014-07-05 00:49:29 +08:00
|
|
|
device = bot.devices.ping;
|
|
|
|
connection = bot.connections.loopback;
|
2014-03-05 05:03:31 +08:00
|
|
|
|
2014-09-05 01:18:18 +08:00
|
|
|
stub(device, 'halt').yields(true);
|
|
|
|
stub(connection, 'disconnect').yields(true);
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
|
2014-07-05 00:49:29 +08:00
|
|
|
afterEach(function() {
|
2014-03-12 01:44:11 +08:00
|
|
|
device.halt.restore();
|
2014-09-03 11:26:26 +08:00
|
|
|
connection.disconnect.restore();
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
|
2014-03-12 01:44:11 +08:00
|
|
|
it("calls #halt on all devices and connections", function() {
|
|
|
|
bot.halt();
|
2014-03-05 05:03:31 +08:00
|
|
|
|
2014-03-12 01:44:11 +08:00
|
|
|
expect(device.halt).to.be.called;
|
2014-09-03 11:26:26 +08:00
|
|
|
expect(connection.disconnect).to.be.called;
|
2014-03-05 05:03:31 +08:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe("#toString", function() {
|
|
|
|
it("returns basic information about the robot", function() {
|
|
|
|
expect(robot.toString()).to.be.eql("[Robot name='Robby']");
|
|
|
|
});
|
|
|
|
});
|
2014-02-28 06:52:25 +08:00
|
|
|
});
|