blueshell
Version:
A Behavior Tree implementation in modern Javascript
915 lines • 48 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("mocha");
const events_1 = require("events");
const chai_1 = require("chai");
const sinon = __importStar(require("sinon"));
const ws_1 = __importDefault(require("ws"));
const nodes_1 = require("../../lib/nodes");
const nodeManager_1 = require("../../lib/utils/nodeManager");
const nodeManagerHelper_1 = require("../../lib/utils/nodeManagerHelper");
class WebSocketServerMock extends events_1.EventEmitter {
close() {
// no-op
return;
}
}
class WebSocketClientMock extends events_1.EventEmitter {
send(data) {
this.lastResponse = JSON.parse(data);
this.emit('messageHandled', this.lastResponse);
}
}
class SequenceWithSetter extends nodes_1.Sequence {
set setterOnlyProp(foo) {
// no-op
return;
}
get foo() {
return 'foo';
}
set foo(f) {
// no-op
return;
}
}
describe('nodeManager', function () {
let nodeManager;
beforeEach(function () {
// reset the singleton
nodeManager_1.NodeManager.reset();
nodeManager = nodeManager_1.NodeManager.getInstance();
});
afterEach(function () {
sinon.restore();
});
describe('reset the singleton', function () {
it('should find node', function () {
const testNode = new nodes_1.Action('testNode');
nodeManager.addNode(testNode);
const node = nodeManager.getNode('testNode');
chai_1.assert.equal(node, testNode);
});
it('should not find node', function () {
const node = nodeManager.getNode('testNode');
chai_1.assert.isUndefined(node);
});
});
describe('add/get/remove node', function () {
it('should handle a node at the root', function () {
const rootNode = new nodes_1.Action('testNode');
nodeManager.addNode(rootNode);
chai_1.assert.equal(nodeManager.getNode('testNode'), rootNode);
nodeManager.removeNode(rootNode);
chai_1.assert.isUndefined(nodeManager.getNode('testNode'));
});
it('should handle a node with a deep path', function () {
const childNode = new nodes_1.Action('childTestNode');
new nodes_1.Sequence('rootTestNode', [childNode]);
nodeManager.addNode(childNode);
chai_1.assert.equal(nodeManager.getNode('rootTestNode_childTestNode'), childNode);
chai_1.assert.isUndefined(nodeManager.getNode('rootTestNode'));
nodeManager.removeNode(childNode);
chai_1.assert.isUndefined(nodeManager.getNode('rootTestNode_childTestNode'));
});
it('should throw if duplicate node added', function () {
const rootNode = new nodes_1.Action('testNode');
nodeManager.addNode(rootNode);
chai_1.assert.equal(nodeManager.getNode('testNode'), rootNode);
try {
nodeManager.addNode(rootNode);
(0, chai_1.assert)(false, 'addNode should throw when adding duplicate node');
}
catch (err) {
chai_1.assert.instanceOf(err, nodeManager_1.DuplicateNodeAdded);
}
nodeManager.removeNode(rootNode);
chai_1.assert.isUndefined(nodeManager.getNode('testNode'));
});
it('should implicitly add and remove all children nodes', function () {
const childNode = new nodes_1.Action('childTestNode');
const rootNode = new nodes_1.Sequence('rootTestNode', [childNode]);
nodeManager.addNode(rootNode);
chai_1.assert.equal(nodeManager.getNode('rootTestNode'), rootNode);
chai_1.assert.equal(nodeManager.getNode('rootTestNode_childTestNode'), childNode);
nodeManager.removeNode(rootNode);
chai_1.assert.isUndefined(nodeManager.getNode('rootTestNode'));
chai_1.assert.isUndefined(nodeManager.getNode('rootTestNode_childTestNode'));
});
});
describe('websocket api', function () {
let session;
let serverStub;
let serverMock;
let clientMock;
let serverCloseStub;
let clientSendSpy;
let removeBreakpointHelperStub;
let setBreakpointHelperStub;
beforeEach(function () {
serverMock = new WebSocketServerMock();
clientMock = new WebSocketClientMock();
serverStub = sinon.stub(ws_1.default, 'Server').returns(serverMock);
serverCloseStub = sinon.stub(serverMock, 'close');
clientSendSpy = sinon.spy(clientMock, 'send');
removeBreakpointHelperStub = sinon
.stub(nodeManagerHelper_1.RuntimeWrappers, 'removeBreakpointFromFunction')
.callThrough();
setBreakpointHelperStub = sinon
.stub(nodeManagerHelper_1.RuntimeWrappers, 'setBreakpointOnFunctionCall')
.callThrough();
session = nodeManager['session'];
nodeManager.runServer();
sinon.assert.calledWith(serverStub, {
host: '127.0.0.1',
port: 8990,
});
serverMock.emit('connection', clientMock);
});
afterEach(function () {
session.disconnect();
sinon.reset();
});
describe('getMethodsForNode', function () {
it('should get all methods and properties for a node', async function () {
const rootNode = new nodes_1.Action('testNode');
nodeManager.addNode(rootNode);
clientMock.emit('message', JSON.stringify({
request: 'getMethodsForNode',
nodePath: 'testNode',
}));
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'getMethodsForNode',
success: true,
nodePath: 'testNode',
listOfMethods: nodeManagerHelper_1.Utils.getMethodInfoForObject(rootNode),
nodeName: 'testNode',
nodeParent: '',
}));
});
it('should get all methods and properties for a child node', async function () {
const childNode = new nodes_1.Action('childTestNode');
const rootNode = new nodes_1.Sequence('rootTestNode', [childNode]);
nodeManager.addNode(rootNode);
clientMock.emit('message', JSON.stringify({
request: 'getMethodsForNode',
nodePath: 'rootTestNode_childTestNode',
}));
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'getMethodsForNode',
success: true,
nodePath: 'rootTestNode_childTestNode',
listOfMethods: nodeManagerHelper_1.Utils.getMethodInfoForObject(childNode),
nodeName: 'childTestNode',
nodeParent: 'rootTestNode',
}));
});
it('should not return success if node not found', async function () {
clientMock.emit('message', JSON.stringify({
request: 'getMethodsForNode',
nodePath: 'testNode',
}));
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'getMethodsForNode',
success: false,
nodePath: 'testNode',
}));
});
});
describe('breakpoints', function () {
let childNode;
let rootNode;
const rootNodeName = 'rootTestNode';
const childNodeName = 'childTestNode';
const childNodePath = `${rootNodeName}_${childNodeName}`;
const condition = "this.name === 'foo'";
beforeEach(function () {
childNode = new nodes_1.Action(childNodeName);
rootNode = new SequenceWithSetter(rootNodeName, [childNode]);
nodeManager.addNode(rootNode);
});
describe('placeBreakpoint', function () {
it('should place a breakpoint with no additional condition', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
const bps = new Map();
bps.set(childNodePath, {
nodePath: childNodePath,
condition: '',
nodeName: childNodeName,
nodeParent: rootNodeName,
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${childNodePath}')`, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition: '',
success: true,
}));
});
it('should place a breakpoint with an additional condition', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition,
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
const bps = new Map();
bps.set(childNodePath, {
nodePath: childNodePath,
condition,
nodeName: childNodeName,
nodeParent: rootNodeName,
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${childNodePath}' && ${condition})`, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition,
success: true,
}));
});
it('should place a breakpoint on a getter', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'get latched',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
const bps = new Map();
bps.set(rootNodeName, {
nodePath: rootNodeName,
condition: '',
nodeName: rootNodeName,
nodeParent: '',
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${rootNodeName}')`, {
methodInfo: {
className: 'Composite',
methodName: 'get latched',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'get latched',
nodeName: rootNodeName,
nodeParent: '',
condition: '',
success: true,
}));
});
it('should place a breakpoint on a getter with a getter/setter defined', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'get foo',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
const bps = new Map();
bps.set(rootNodeName, {
nodePath: rootNodeName,
condition: '',
nodeName: rootNodeName,
nodeParent: '',
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${rootNodeName}')`, {
methodInfo: {
className: 'SequenceWithSetter',
methodName: 'get foo',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'get foo',
nodeName: rootNodeName,
nodeParent: '',
condition: '',
success: true,
}));
});
it('should place a breakpoint on a setter', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'set setterOnlyProp',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
const bps = new Map();
bps.set(rootNodeName, {
nodePath: rootNodeName,
condition: '',
nodeName: rootNodeName,
nodeParent: '',
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${rootNodeName}')`, {
methodInfo: {
className: 'SequenceWithSetter',
methodName: 'set setterOnlyProp',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'set setterOnlyProp',
nodeName: rootNodeName,
nodeParent: '',
condition: '',
success: true,
}));
});
it('should place a 2nd breakpoint on the same function of another instance, same class', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'handleEvent',
condition,
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.called(removeBreakpointHelperStub);
const bps = new Map();
bps.set(childNodePath, {
nodePath: childNodePath,
condition: '',
nodeName: childNodeName,
nodeParent: rootNodeName,
});
bps.set(rootNodeName, {
nodePath: rootNodeName,
condition,
nodeName: rootNodeName,
nodeParent: '',
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${childNodePath}') || (this.path === '${rootNodeName}' && ${condition})`, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'handleEvent',
nodeName: rootNodeName,
nodeParent: '',
condition,
success: true,
}));
});
it('should place a 2nd breakpoint on the same function of another instance, different class', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: '_beforeEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: '_beforeEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
const bps = new Map();
bps.set(rootNodeName, {
nodePath: rootNodeName,
condition: '',
nodeName: rootNodeName,
nodeParent: '',
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${rootNodeName}')`, {
methodInfo: {
className: 'Composite',
methodName: '_beforeEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: '_beforeEvent',
nodeName: rootNodeName,
nodeParent: '',
condition: '',
success: true,
}));
});
it('should update the condition on an existing breakpoint', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// test adding the condition
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition,
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.called(removeBreakpointHelperStub);
const bps = new Map();
bps.set(childNodePath, {
nodePath: childNodePath,
condition,
nodeName: childNodeName,
nodeParent: rootNodeName,
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${childNodePath}' && ${condition})`, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition,
success: true,
}));
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// test removing the condition
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.called(removeBreakpointHelperStub);
bps.get(childNodePath).condition = '';
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${childNodePath}')`, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition: '',
success: true,
}));
});
describe('placeBreakpoint error cases', function () {
it('should fail to place a breakpoint for node that does not exist', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: 'foo',
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.notCalled(setBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
methodName: 'handleEvent',
condition: '',
success: false,
}));
});
it('should fail to place a breakpoint for a method that does not exist on the node', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'foo',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.notCalled(setBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'foo',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition: '',
success: false,
}));
});
it('should fail to place the same breakpoint a second time with no condition', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// try setting the breakpoint again
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.notCalled(setBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition: '',
success: false,
}));
});
it('should fail to place the same breakpoint a second time with the same condition', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition,
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// try setting the breakpoint again
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition,
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.notCalled(setBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNodeName,
nodeParent: rootNodeName,
condition,
success: false,
}));
});
});
});
describe('removeBreakpoint', function () {
it('should remove a breakpoint when only one is set on the class/method', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// remove the breakpoint
clientMock.emit('message', JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
const bps = new Map();
bps.set(childNodePath, {
nodePath: childNodePath,
condition: '',
nodeName: childNodeName,
nodeParent: rootNodeName,
});
sinon.assert.calledWith(removeBreakpointHelperStub, sinon.match.object, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: new Map(),
});
sinon.assert.notCalled(setBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
success: true,
}));
});
it('should remove a breakpoint for the specified node when more than one is set on the class/method', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// remove one of the two breakpoints (one on the root node)
clientMock.emit('message', JSON.stringify({
request: 'removeBreakpoint',
nodePath: rootNodeName,
methodName: 'handleEvent',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
const bps = new Map();
bps.set(childNodePath, {
nodePath: childNodePath,
condition: '',
nodeName: childNodeName,
nodeParent: rootNodeName,
});
sinon.assert.calledWith(removeBreakpointHelperStub, sinon.match.object, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(setBreakpointHelperStub, sinon.match.object, sinon.match.string, `(this.path === '${childNodePath}')`, {
methodInfo: {
className: 'Base',
methodName: 'handleEvent',
},
breakpointId: sinon.match.string,
breakpoints: bps,
});
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'removeBreakpoint',
nodePath: rootNodeName,
methodName: 'handleEvent',
success: true,
}));
});
describe('removeBreakpoint error cases', function () {
it('should fail to remove a breakpoint on a node that does not exist', async function () {
clientMock.emit('message', JSON.stringify({
request: 'removeBreakpoint',
nodePath: 'foo',
methodName: 'handleEvent',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'removeBreakpoint',
methodName: 'handleEvent',
success: false,
}));
});
it('should fail to remove a breakpoint on a method that does not exist on the node', async function () {
clientMock.emit('message', JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'foo',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'foo',
success: false,
}));
});
it('should fail to remove a breakpoint that is set, but not for this node', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: rootNodeName,
methodName: 'handleEvent',
condition: '',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
removeBreakpointHelperStub.resetHistory();
setBreakpointHelperStub.resetHistory();
clientSendSpy.resetHistory();
// try to remove a different breakpoint
clientMock.emit('message', JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
success: false,
}));
});
it('should fail to remove a breakpoint that is not set at all', async function () {
// remove the breakpoint
clientMock.emit('message', JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
}));
// this is required to get over the promises that happen in the async callback when we emit message above
await events_1.EventEmitter.once(clientMock, 'messageHandled');
sinon.assert.notCalled(removeBreakpointHelperStub);
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'removeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
success: false,
}));
});
});
});
describe('client reconnect', function () {
it('should send any existing breakpoints back to the client when the client reconnects', async function () {
clientMock.emit('message', JSON.stringify({
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
condition: '',
}));
const placeBreakpointRespObj = (await events_1.EventEmitter.once(clientMock, 'messageHandled'))[0];
chai_1.assert.deepEqual(placeBreakpointRespObj, {
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNode.name,
nodeParent: childNode.parent,
condition: '',
success: true,
});
const reconnectingClient = new WebSocketClientMock();
serverMock.emit('connection', reconnectingClient);
// we should get the same breakpoint we set before with the first client
chai_1.assert.deepEqual(reconnectingClient.lastResponse, {
request: 'placeBreakpoint',
nodePath: childNodePath,
methodName: 'handleEvent',
nodeName: childNode.name,
nodeParent: childNode.parent,
condition: '',
success: true,
});
});
});
});
describe('unknown request', function () {
it('should report error if api function does not exist', async function () {
clientMock.emit('message', JSON.stringify({
request: 'foobar',
}));
sinon.assert.calledWith(clientSendSpy, JSON.stringify({
request: 'foobar',
success: false,
err: new nodeManager_1.APIFunctionNotFound('foobar').message,
}));
});
});
});
});
//# sourceMappingURL=nodeManager.test.js.map