diff --git a/src/ElkClient.test.ts b/src/ElkClient.test.ts index 262d943e..03848413 100644 --- a/src/ElkClient.test.ts +++ b/src/ElkClient.test.ts @@ -235,9 +235,10 @@ describe('ElkClient', () => { describe('when username requested', () => { let authenticatingEmitted = false; + let connectPromise: Promise; beforeEach(() => { - client.connect(); + connectPromise = client.connect(5); client.once('authenticating', () => { authenticatingEmitted = true; }); @@ -245,47 +246,60 @@ describe('ElkClient', () => { client.connection.emit('data', '\r\nUsername: '); }); - test('sends the username', () => { + afterEach(async () => { + // Let the promise reject. + return connectPromise.catch(() => undefined); + }); + + test('sends the username', async () => { expect(mockSocketConnectionInstance.write).toHaveBeenCalledWith('someone\r\n'); }); - test('sets state', () => { + test('sets state', async () => { expect(client.state).toBe(ElkClientState.Authenticating); }); - test('emits "authenticating"', () => { + test('emits "authenticating"', async () => { expect(authenticatingEmitted).toBe(true); }); }); describe('when password requested', () => { - let authenticatingEmitted = false; + let connectPromise: Promise; beforeEach(() => { - client.connect(); - client.once('authenticating', () => { - authenticatingEmitted = true; - }); + connectPromise = client.connect(5); mockSocketConnectionInstance.write.mockClear(); client.connection.emit('data', '\r\nPassword: '); }); - test('sends the password', () => { + afterEach(async () => { + // Let the promise reject. + return connectPromise.catch(() => undefined); + }); + + test('sends the password', async () => { expect(mockSocketConnectionInstance.write).toHaveBeenCalledWith('supersecret\r\n'); }); }); describe('when successful', () => { let authenticatedEmitted = false; + let connectPromise: Promise; beforeEach(() => { - client.connect(); + connectPromise = client.connect(); client.once('authenticated', () => { authenticatedEmitted = true; }); client.connection.emit('data', '\r\nElk-M1XEP: Login successful.\r\n'); }); + afterEach(async () => { + // Let the promise reject. + return connectPromise.catch(() => undefined); + }); + test('sets state', () => { expect(client.state).toBe(ElkClientState.Ready); }); @@ -328,15 +342,21 @@ describe('ElkClient', () => { describe('when OK received', () => { let okEmitted = false; + let connectPromise: Promise; beforeEach(() => { - client.connect(); + connectPromise = client.connect(5); client.once('ok', () => { okEmitted = true; }); client.connection.emit('data', 'OK\r\n'); }); + afterEach(async () => { + // Let the promise reject. + return connectPromise.catch(() => undefined); + }); + test('emits "ok" event', () => { expect(okEmitted).toBe(true); }); @@ -409,6 +429,7 @@ describe('ElkClient', () => { describe('connection disconnects', () => { let client: ElkClient; let disconnectedEmitted = false; + let connectPromise: Promise; beforeEach(() => { client = new ElkClient(); @@ -417,11 +438,16 @@ describe('ElkClient', () => { }); ((client.connection as unknown) as ElkSocketConnectionMock).state = ElkConnectionState.Connected; - client.connect(); + connectPromise = client.connect(); client.connection.emit('data', '\r\nElk-M1XEP: Login successful.\r\n'); client.connection.emit('disconnected'); }); + afterEach(async () => { + // Let the promise reject. + return connectPromise.catch(() => undefined); + }); + test('emits "disconnected"', () => { expect(disconnectedEmitted).toBe(true); }); diff --git a/src/ElkClient.ts b/src/ElkClient.ts index 0a097179..d7ada7d5 100644 --- a/src/ElkClient.ts +++ b/src/ElkClient.ts @@ -130,9 +130,9 @@ class ElkClient extends ElkClientCommands { if (this._connection.state === ElkConnectionState.Disconnecting) { // If we're in the process of closing the connection, wait for it // to close then try to connect. - this._connection.disconnect().then(() => this._connection.connect()); + void this._connection.disconnect().then(() => this._connection.connect()); } else { - this._connection.connect(); + void this._connection.connect(); } }) ) @@ -181,7 +181,7 @@ class ElkClient extends ElkClientCommands { // If we do need to authenticate, this should cause // the control panel to issue a failure message or request // authentication. - this.getVersionNumber(); + void this.getVersionNumber(); } }; @@ -226,13 +226,13 @@ class ElkClient extends ElkClientCommands { 'Username was requested but none was provided.' ) ); - this.disconnect(); + this.disconnect().catch(() => undefined); return; } this._state = ElkClientState.Authenticating; this.emit('authenticating'); - this._connection.write(this.options.username + '\r\n'); + void this._connection.write(this.options.username + '\r\n'); break; } case PASSWORD_REQUEST: { @@ -244,10 +244,10 @@ class ElkClient extends ElkClientCommands { 'Password was requested but none was provided.' ) ); - this.disconnect(); + this.disconnect().catch(() => undefined); return; } - this._connection.write(this.options.password + '\r\n'); + void this._connection.write(this.options.password + '\r\n'); break; } case LOGIN_FAILURE: { @@ -258,7 +258,7 @@ class ElkClient extends ElkClientCommands { 'Login failed, invalid username or password.' ) ); - this.disconnect(); + this.disconnect().catch(() => undefined); return; } case LOGIN_SUCCESSFUL: { diff --git a/src/connection/ElkSocketConnection.test.ts b/src/connection/ElkSocketConnection.test.ts index c49dfe67..9fc3d174 100644 --- a/src/connection/ElkSocketConnection.test.ts +++ b/src/connection/ElkSocketConnection.test.ts @@ -219,20 +219,20 @@ describe('ElkSocketConnection', () => { connection = new ElkSocketConnection(); }); - test('initially sets state to Connecting', () => { - connection.connect(); + test('initially sets state to Connecting', async () => { + expect.assertions(1); + let promise = connection.connect(); expect(connection.state).toBe(ElkConnectionState.Connecting); + await promise; }); - test('emits "connecting" then "connected"', done => { + test('emits "connecting" then "connected"', async () => { expect.assertions(2); const emitSpy = jest.spyOn(connection, 'emit'); const promise = connection.connect(); expect(emitSpy).toHaveBeenCalledWith('connecting'); - promise.then(() => { - expect(emitSpy).toHaveBeenCalledWith('connected'); - done(); - }); + await promise; + expect(emitSpy).toHaveBeenCalledWith('connected'); }); test('resolves and shows state as Connected', async () => { @@ -377,7 +377,7 @@ describe('ElkSocketConnection', () => { const connectPromise = connection.connect(); expect(emitSpy).toHaveBeenCalledWith('connecting'); await connection.disconnect(); - connectPromise.catch(err => {}); // Ignore rejected promise, it's expected. + connectPromise.catch(() => undefined); // Ignore rejected promise, it's expected. expect(emitSpy).toHaveBeenCalledWith('disconnected'); expect(emitSpy).not.toHaveBeenCalledWith('connected'); }); @@ -482,14 +482,20 @@ describe('ElkSocketConnection', () => { }); describe('when connecting', () => { + let connectPromise: Promise; + beforeEach(() => { mockCreateSocketConnectsSuccessfully(); connection = new ElkSocketConnection(); + connectPromise = connection.connect(5); + }); + + afterEach(async () => { + connectPromise.catch(() => undefined); }); it('cancels the connect and resolves', async () => { expect.assertions(2); - connection.connect(); expect(connection.state).toBe(ElkConnectionState.Connecting); try { await connection.write('foo'); @@ -557,7 +563,7 @@ describe('ElkSocketConnection', () => { expect.assertions(2); const connectionEmitSpy = jest.spyOn(connection, 'emit'); await connection.connect(); - const buffer = new Buffer('somedata'); + const buffer = Buffer.from('somedata', 'ascii'); const bufferSpy = jest.spyOn(buffer, 'toString'); socketMock.emit('data', buffer); expect(bufferSpy).toHaveBeenCalled(); @@ -567,18 +573,26 @@ describe('ElkSocketConnection', () => { describe('when an error is emitted', () => { let socketMock: SocketMock; + let testError: Error; + beforeEach(() => { mockCreateSocketConnectsSuccessfully(0, mock => { socketMock = mock; }); connection = new ElkSocketConnection(); + testError = new Error('Something went wrong!'); }); - it('emits it as a string', async () => { - expect.assertions(1); + it('destroys the socket and emits the error', async () => { + expect.assertions(2); + let errorEmitted; await connection.connect(); - socketMock.emit('error', new Error()); + connection.on('error', error => { + errorEmitted = error; + }); + socketMock.emit('error', testError); expect(socketMock.destroy).toHaveBeenCalled(); + expect(errorEmitted).toBe(testError); }); }); diff --git a/src/connection/ElkSocketConnection.ts b/src/connection/ElkSocketConnection.ts index f1cbbb29..fd82eecf 100644 --- a/src/connection/ElkSocketConnection.ts +++ b/src/connection/ElkSocketConnection.ts @@ -162,8 +162,10 @@ class ElkSocketConnection extends EventEmitter implements ElkConnection { // Emitted when an error occurs. The 'close' event will be called // directly following this event. if (this._socket) { - this._socket.destroy(); + this._socket.destroy(error); } + + this.emit('error', error); }; /** @@ -208,21 +210,21 @@ class ElkSocketConnection extends EventEmitter implements ElkConnection { socket = createSocket(this.options); socket.on('connect', connectListener); - socket.on('error', errorListener); + this.on('error', errorListener); this.on('disconnecting', disconnectingListener); this.setSocket(socket); }) ) .catch(error => { socket.removeListener('connect', connectListener); - socket.removeListener('error', errorListener); + this.removeListener('error', errorListener); this.removeListener('disconnecting', disconnectingListener); this.setSocket(undefined); throw error; }) .then(() => { socket.removeListener('connect', connectListener); - socket.removeListener('error', errorListener); + this.removeListener('error', errorListener); this.removeListener('disconnecting', disconnectingListener); return this; });