test-publish.js 6.31 KB
'use strict';

var PubSub = require('../src/pubsub'),
    TestHelper = require('../test/helper'),
    assert = require('referee').assert,
    refute = require('referee').refute,
    sinon = require('sinon');

describe( 'publish method', function () {
    it('should return false if there are no subscribers', function(){
        var message = TestHelper.getUniqueString();
        assert.equals( PubSub.publish( message ), false );
    });

    it('should return true if there are subscribers to a message', function(){
        var message = TestHelper.getUniqueString(),
            func = function(){ return undefined; };

        PubSub.subscribe( message, func );
        assert( PubSub.publish( message ) );
    });

    it('should return false, when there are no longer any subscribers to a message', function(){
        var message = TestHelper.getUniqueString(),
            func = function(){ return undefined; },
            token = PubSub.subscribe(message, func);

        PubSub.unsubscribe(token);
        assert.equals( PubSub.publish(message), false );
    });

    it('should call all subscribers for a message exactly once', function(){
        var message = TestHelper.getUniqueString(),
            spy1 = sinon.spy(),
            spy2 = sinon.spy();

        PubSub.subscribe( message, spy1 );
        PubSub.subscribe( message, spy2 );

        PubSub.publishSync( message, 'my payload' ); // force sync here, easier to test

        assert( spy1.calledOnce );
        assert( spy2.calledOnce );
    });

    it('should call all ONLY subscribers of the published message', function(){
        var message1 = TestHelper.getUniqueString(),
            message2 = TestHelper.getUniqueString(),
            spy1 = sinon.spy(),
            spy2 = sinon.spy();

        PubSub.subscribe( message1, spy1 );
        PubSub.subscribe( message2, spy2 );

        PubSub.publishSync( message1, 'some payload' );

        // ensure the first subscriber IS called
        assert(	 spy1.called );
        // ensure the second subscriber IS NOT called
        assert.equals( spy2.callCount, 0 );
    });

    it('should call subscribers with message as first argument', function(){
        var message = TestHelper.getUniqueString(),
            spy = sinon.spy();

        PubSub.subscribe( message, spy );
        PubSub.publishSync( message, 'some payload' );

        assert( spy.calledWith( message ) );
    });

    it('should call subscribers with data as second argument', function(){
        var message = TestHelper.getUniqueString(),
            spy = sinon.spy(),
            data = TestHelper.getUniqueString();

        PubSub.subscribe( message, spy );
        PubSub.publishSync( message, data );

        assert( spy.calledWith( message, data ) );
    });

    it('should publish method asyncronously', function( done ){
        var message = TestHelper.getUniqueString(),
            spy = sinon.spy(),
            data = TestHelper.getUniqueString(),
            clock = sinon.useFakeTimers();

        PubSub.subscribe( message, spy );
        PubSub.publish( message, data );

        assert.equals( spy.callCount, 0 );
        clock.tick(1);
        assert.equals( spy.callCount, 1 );

        done();
        clock.restore();
    });

    it('publishSync method should allow syncronous publication', function(){
        var message = TestHelper.getUniqueString(),
            spy = sinon.spy(),
            data = TestHelper.getUniqueString();

        PubSub.subscribe( message, spy );
        PubSub.publishSync( message, data );

        assert.equals( spy.callCount, 1 );
    });

    it('should call all subscribers, even if there are exceptions', function( done ){
        var message = TestHelper.getUniqueString(),
            func1 = function(){
                throw('some error');
            },
            spy1 = sinon.spy(),
            spy2 = sinon.spy(),
            clock = sinon.useFakeTimers();

        PubSub.subscribe( message, func1 );
        PubSub.subscribe( message, spy1 );
        PubSub.subscribe( message, spy2 );

        assert.exception( function(){
            PubSub.publishSync( message, 'some data' );
            clock.tick(1);
        });

        assert( spy1.called );
        assert( spy2.called );

        done();
        clock.restore();
    });

    it('should fail immediately on exceptions when immediateExceptions is true', function(){
        var message = TestHelper.getUniqueString(),
            func1 = function(){
                throw('some error');
            },
            spy1 = sinon.spy(),
            spy2 = sinon.spy();


        PubSub.subscribe( message, func1 );
        PubSub.subscribe( message, spy1 );

        PubSub.immediateExceptions = true;

        assert.exception( function(){
            PubSub.publishSync( message, 'some data' );
        });

        refute( spy1.called );
        refute( spy2.called );

        // make sure we restore PubSub to it's original state
        delete PubSub.immediateExceptions;
    });

    it('should fail immediately on exceptions in namespaces when immediateExceptions is true',  function(){
        var func1 = function(){
                throw('some error');
            },
            spy1 = sinon.spy();

        PubSub.subscribe( 'buy', func1 );
        PubSub.subscribe( 'buy', spy1 );

        PubSub.immediateExceptions = true;

        assert.exception( function(){
            PubSub.publishSync( 'buy.tomatoes', 'some data' );
        });

        refute( spy1.called );

        // make sure we restore PubSub to it's original state
        delete PubSub.immediateExceptions;
    });

    it('should call all subscribers, even when there are unsubscriptions within', function(done){
        var topic = TestHelper.getUniqueString(),
            spy1 = sinon.spy(),
            func1 = function func1(){
                PubSub.unsubscribe(func1);
                spy1();
            },

            spy2 = sinon.spy(),
            func2 = function func2(){
                PubSub.unsubscribe(func2);
                spy2();
            },

            clock = sinon.useFakeTimers();

        PubSub.subscribe(topic, func1);
        PubSub.subscribe(topic, func2);

        PubSub.publish(topic, 'some data');
        clock.tick(1);

        assert(spy1.called, 'expected spy1 to be called');
        assert(spy2.called, 'expected spy2 to be called');

        clock.restore();
        done();
    });
});