const EventEmitter = require('events'); const SenderInterface = require('../robot/SenderInterface'); const TelnetSender = require('../robot/TelnetSenderGRBL'); describe('Sender Interface and TelnetSenderGRBL implementation', () => { test('TelnetSenderGRBL implements the required sender interface methods', () => { const sender = new TelnetSender('test.test', 2300, 'x', 'y', 'z'); expect(sender).toBeInstanceOf(SenderInterface); expect(typeof sender.connect).toBe('function'); expect(typeof sender.send).toBe('function'); expect(typeof sender.getStatus).toBe('function'); expect(typeof sender.disconnect).toBe('function'); }); test('test mode sender rejects invalid send payloads and supports idempotent disconnect', () => { const sender = new TelnetSender('test.test', 2300, 'x', 'y', 'z'); expect(sender.getStatus()).toMatchObject({ state: 'connected' }); sender.tSocket = null; expect(sender.send('HELLO')).toBe(false); expect(sender.send('')).toBe(false); sender.disconnect(); expect(sender.getStatus()).toMatchObject({ state: 'disconnected' }); sender.disconnect(); expect(sender.getStatus()).toMatchObject({ state: 'disconnected' }); }); test('sender reconnects on connection failure and eventually connects', async () => { let connectAttempts = 0; const netMock = { createConnection: () => { const socket = new EventEmitter(); socket.end = jest.fn(); process.nextTick(() => { connectAttempts += 1; if (connectAttempts === 1) { socket.emit('error', new Error('connection failed')); } else { socket.emit('connect'); } }); return socket; } }; class DummyTelnetSocket { constructor(connection) { this.connection = connection; } on(event, listener) { this.connection.on(event, listener); return this; } write(txt) { this.connection.written = txt; } } const sender = new TelnetSender('localhost', 2300, 'x', 'y', 'z', null, null, null, null, { netModule: netMock, TelnetSocketClass: DummyTelnetSocket, setTimeoutFn: (fn) => fn(), autoConnect: false, reconnectDelay: 1, maxReconnectDelay: 2 }); const result = await sender.connect(); expect(connectAttempts).toBe(2); expect(result.getStatus()).toMatchObject({ state: 'connected', url: 'localhost' }); expect(result.getStatus().reconnectTimer).toBe(false); }); test('test mode sender has connected status and can send/disconnect', async () => { const sender = new TelnetSender('test.test', 2300, 'x', 'y', 'z'); expect(sender.getStatus()).toMatchObject({ state: 'connected', url: 'test.test', isTestMode: true }); expect(sender.send('HELLO')).toBe(true); expect(sender.tSocket.written).toBe('HELLO\r\n'); sender.disconnect(); expect(sender.getStatus()).toMatchObject({ state: 'disconnected' }); await expect(sender.connect()).resolves.toBe(sender); expect(sender.getStatus()).toMatchObject({ state: 'connected' }); }); });