twitter-stream-channels

API Docs for: 1.0.0
Show:

File: mocks/TwitterStreamChannels.js

/**
 * @module twitter-stream-channels
 */

/*
 * This is a very simple mock of TwitterStreamChannels for offline test purposes
 * To test only the init of the StreamChannels object (before any call to twitter)
 */

//in unitTest, we pass a flag --config highSpeed true and then the events will be emitted really faster (not using setTimeout but process.nextTick)
var highSpeed = process.env.highSpeed ? true : false;

var StreamChannels = require('../lib/StreamChannels');
var fs = require('fs');
var EventEmitter = require('events').EventEmitter,
        util = require('util');

//optimization for the unit-tests
var nextLoop = (function(){
  if(highSpeed === true){
    return process.nextTick;
  }
  else{
    return setTimeout;
  }
})();

/*
 * Mock for the twitter api client twit
 * No oAuth request will be made
 * @param {Object} [credentials] @optional (since it's a mock, no credentials needed
 * @param {Array} [credentials.tweets] @optional (by default will load `/mocks/data/tweets.json`)
 * @param {Boolean} [credentials.singleRun=true] @optional if false will loop on the tweets mock array until you call stop (default true)
 * @param {Number} [credentials.tweetDelay=100] @optional delay between each tweets emitted in ms (default 100ms)
 */
var TwitMock = function(credentials){
  //this is internal to the mock to give option to the dev to change the tweet mocks
  credentials = typeof credentials === 'undefined' ? {} : credentials;
  credentials.tweets = typeof credentials.tweets === 'undefined' ? require('./data/tweets.json') : credentials.tweets;
  credentials.singleRun = typeof credentials.singleRun === 'undefined' ? true : credentials.singleRun;
  credentials.tweetDelay = typeof credentials.tweetDelay === 'undefined' ? 100 : credentials.tweetDelay;
  this._tweetsMock = credentials.tweets;
  this._singleRun = credentials.singleRun;
  this._tweetDelay = credentials.tweetDelay;
};

/*
 * Mocking .stream with an empty function so that no call will be made
 */
TwitMock.prototype.stream = function(path, params){
  return (new TwitStreamMock({
    tweets:this._tweetsMock,
    singleRun: this._singleRun,
    tweetDelay: this._tweetDelay
  })).start();
};

/*
 * Mock for the OAuth / stream part (no need to mock all the underlying methods, only the events part)
 * Emits :
 * - connect
 * - connected
 * - tweet : will read the inputTweetsMocks file and emit the tweet found inside
 * @param {Object} [options] @optional (since it's a mock, no credentials needed
 * @param {File} [options.tweets] @optional (by default will load `/mocks/data/tweets.json`)
 * @param {Boolean} [options.singleRun=true] @optional if false will loop on the tweets mock array until you call stop (default true)
 * @param {Number} [options.tweetDelay=100] @optional delay between each tweets emitted in ms (default 100ms)
 */
var TwitStreamMock = function(options){
  
  options = typeof options === 'undefined' ? {} : options;
  options.tweets = typeof options.tweets === 'undefined' ? require('./data/tweets.json') : options.tweets;
  options.singleRun = typeof options.singleRun === 'undefined' ? true : options.singleRun;
  options.tweetDelay = typeof options.tweetDelay === 'undefined' ? 100 : options.tweetDelay;
  
  this.currentTweetIndex = 0;

  EventEmitter.call(this);
  
  //following method are directly attached (on the prototype, they would be erased by EventEmitter's prototype)
  this.start = function(){
    
    //don't do anything if already connected
    if(this.abortedBy === null){
      return this;
    }
    
    var that = this;
    this.abortedBy = null;
  
    var emitTweetCb = function(){
      var that = this;
      nextLoop(function(){
        //only send tweet if the stream hasn't been stopped
        if(that.abortedBy === null){
          //emit the tweet
          if(options.tweets[that.currentTweetIndex]){
            options.tweets[that.currentTweetIndex].$index = that.currentTweetIndex;
            that.emit('tweet',options.tweets[that.currentTweetIndex]);
            that.currentTweetIndex++;
          }
          //case we are at the end of the tweets
          if(that.currentTweetIndex >= options.tweets.length){
            //in singleRun restart from 0
            if(options.singleRun === false){
              that.currentTweetIndex = 0;
            }
            else{
              that._twitterDisconnect();
              return false;//dont recall the callback for the next tick
            }
          }
          emitTweetCb.call(that);
        }
      },1*options.tweetDelay);
    };
    
    nextLoop(function(){
      that.emit('connect');
    },0);
    nextLoop(function(){
      that.emit('connected');
    },1*options.tweetDelay);
    nextLoop(function(){
      emitTweetCb.call(that);
    },2*options.tweetDelay);
    
    return this;
  };
  
  this.stop = function(){
    this.abortedBy = 'twit-client';
    return this;
  };
  
  this._twitterDisconnect = function(){
    this.abortedBy = 'twitter';
    this.emit('disconnect');
    return this;
  };
  
};

util.inherits(TwitStreamMock, EventEmitter);

/**
 * This class mocks {{#crossLink "TwitterStreamChannels"}}TwitterStreamChannels{{/crossLink}} so that you could work offline, on your own data, without connecting to Twitter.
 * 
 * See the examples on [the github repo](https://github.com/topheman/twitter-stream-channels/tree/master/examples)
 * @class TwitterStreamChannelsMock
 * @constructor
 * @param {Object} [options] @optional (since it's a mock, no credentials needed
 * @param {File} [options.tweets] @optional (by default will load `/mocks/data/tweets.json`)
 * @param {Boolean} [options.singleRun=true] @optional if false will loop on the tweets mock array until you call stop (default true)
 * @param {Number} [options.tweetDelay=100] @optional delay between each tweets emitted in ms (default 100ms)
 * @returns {TwitterStreamChannelsMock}
 */
var TwitterStreamChannelsMock = function(credentials){
  this.apiClient = new TwitMock(credentials);
};

/**
 * Returns a mocked version of apiClient, only the .stream() method is mocked (will stream exactly the same tweets as {{#crossLink "TwitterStreamChannelsMock/streamChannels"}}stream.streamChannels(){{/crossLink}})
 * @method getApiClient
 * @returns {TwitMock}
 */
TwitterStreamChannelsMock.prototype.getApiClient = function(){
  return this.apiClient;
};

/**
 * Mocks the {{#crossLink "TwitterStreamChannels/streamChannels"}}TwitterStreamChannels.streamChannels(options){{/crossLink}} method of TwitterStreamChannels to return a StreamChannels object
 * 
 * Will start to emit the tweets you specified in the tweets attributes in the constructor.
 * @method streamChannels
 * @param {Object} options
 * @param {Object|Array} options.track Pass an object describing your channels. If you don't want to use channels, you can pass directly an array of keywords.
 * @param {Boolean} [options.enableChannelsEvents=true] If true, will fire the events like 'channels/channelName'
 * @param {Boolean} [options.enableRootChannelsEvent=true] If true, will fire the event 'channels'
 * @param {Boolean} [options.enableKeywordsEvents=false] If true, will fire the events 'keywords/keywordName' (disabled by default)
 * @return {StreamChannels}
 */
TwitterStreamChannelsMock.prototype.streamChannels = function(options){
  return new StreamChannels(this.apiClient, options);
};

module.exports = TwitterStreamChannelsMock;