UNPKG

blueshell

Version:

A Behavior Tree implementation in modern Javascript

915 lines 48 kB
"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