0

I'm playing around with Node, Socket.IO and BDD by creating a chat application. During one of the tests, I get a timeout error stating:

Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

The affected test is

it('#must be able to receive a message', function(done)
{
    chatterServer.on('chatterMessage', function(data)
    {
        console.log('Incoming message!');
        expect(data).to.have.property('message');
        expect(data.message).to.be('Hello, world!');
        done();
    });

    console.log('Sending message!');
    chatterClient.send('chatterMessage', { message: 'Hello, world!' });
    console.log('Sent!');
});

I found that the cause of this issue is that the chatterMessage event is not being caught by the server. Whilst I did specify it.

The console's output is:

Sending message!
Sent!
Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.

I'm probably doing something wrong. I'm not too familiar with Node and Socket.IO, so I'm sorry if this question is very obvious.

I looked around Google with the search terms 'socket.io server not receiving from client', but from what I found, nothing helped me to solve my issue so far.

I did however try the solution in this question, but that didn't fix it for me.

I'm using Mocha and expect.js

The complete test is:

var util = require('util');
var Chatter = require('../src/index');
var ChatterServer = Chatter.Server;
var ChatterClient = Chatter.Client;
var express = require('express');
var expect = require('expect.js');
var socketIO = require('socket.io');
var socketIOClient = require('socket.io-client');

var host = 'http://localhost';
var port = 8080;

describe('Chatter', function()
{
    'use strict';

    var chatterServer;
    var chatterClient;
    var server;

    before(function()
    {
        var app = express();

        server = app.listen(port);
    });

    beforeEach(function()
    {
        chatterServer = new ChatterServer(socketIO(server));
        chatterClient = new ChatterClient(socketIOClient, util.format('%s:%s', host, port.toString()));
    });

    ...

    it('#must be able to receive a message', function(done)
    {
        chatterServer.on('chatterMessage', function(data)
        {
            console.log('Incoming message!');
            expect(data).to.have.property('message');
            expect(data.message).to.be('Hello, world!');
            done();
        });

        console.log('Sending message!');
        chatterClient.send('chatterMessage', { message: 'Hello, world!' });
        console.log('Sent!');
    });
});

My Client (ChatterClient) is:

(function()
{
    'use strict';

    function Client(socketIO, url)
    {
        this.socketIO = socketIO(url);
    }

    Client.prototype.send = function(event, data)
    {
        this.socketIO.emit(event, data);
    };

    Client.prototype.on = function(event, callback)
    {
        this.socketIO.on(event, callback);
    };

    if (module !== undefined && module.hasOwnProperty('exports')) {
        module.exports = Client;
    } else {
        window.Chatter = {
            Client: Client,
        };
    }
}());

The Server (ChatterServer) is:

(function()
{
    'use strict';

    function Server(socketIO)
    {
        this.socketIO = socketIO;
        this.connectedUsers = {};

        this.on('connection', (function(user)
        {
            var userID = user.client.id;

            this.connectedUsers[userID] = user;

            user.emit('chatterConnectionAcknowledged', { id: userID });
        }).bind(this));
    }

    Server.prototype.on = function(event, handler)
    {
        this.socketIO.on(event, handler);
    };

    module.exports = Server;

}());
Community
  • 1
  • 1
MisterBla
  • 2,355
  • 1
  • 18
  • 29

1 Answers1

1

You need to change your code in two sides.

First side, you will need to listen incoming socket connections on the socketIO object. (see the emphasized code below)

    //.. some code

    function Server(socketIO)
    {
        this.socketIO = socketIO;
        this.connectedUsers = {};

        this.socketIO.on('connection', (function(user)
        {
            var userID = user.client.id;

            this.connectedUsers[userID] = user;

            user.emit('chatterConnectionAcknowledged', { id: userID });
        }).bind(this));
    }

    //.. some code

Second side, when you are adding new events to listen on the server, you need to bind those events to the sockets since they are ones that are going to listen when events are emitted from the socket clients.

Server.prototype.on = function (event, handler) {
  Object.keys(this.connectedUsers).map(function (key) {
    this.connectedUsers[key].on(event, handler);
  }.bind(this));
};
Wilson
  • 9,006
  • 3
  • 42
  • 46
  • I just tried this, but the issue stays. On the first side, I can understand that change. I had it like that before, but changes it to `this.on` since it would hook the same way, but using the Server's method instead. I know I'm creating some overhead with this, but I don't think that's that much of an issue. Your second change, even before trying, I have to disagree with, since it would hook that event on the client, not the server (which is the intended effect). Besides, the *server* isn't receiving the event to begin with. I'm wondering why that is. – MisterBla Jun 15 '15 at 16:06
  • @TheSerenin Please review this [gist](https://gist.github.com/wilsonbalderrama/9f87787a201a55d99cc6), I tried to replicate your code on these files, hope that it is the same as yours, this example is working. Regarding the change on the **second side**, I suggest you to code your example using raw socketio without creating the abstraction (ChatterServer, ChatterClient), so that you can understand why I am doing in this way. – Wilson Jun 15 '15 at 17:19
  • I'm mainly using abstraction so that it supports dependency injection. This is (at this moment) mainly used for the Client, as during testing, the client uses the socket.io-client package, whereas when it'll be on the web, it'll use the socket.io global object. I reviewed your gist and to me it really doesn't seem much different to what I'm doing. I'm hosting the code on GitHub, find the repo here: https://github.com/TheSerenin/chatter – MisterBla Jun 15 '15 at 17:32
  • @TheSerenin if you run the code of my gist, you will see that it is running ok. – Wilson Jun 15 '15 at 18:51
  • @TheSerenin I have created a [pull request](https://github.com/TheSerenin/chatter/pulls) on your repository applying these changes and other minor changes as well regarding the unit test, please check it out. – Wilson Jun 15 '15 at 19:35