All files / botbuilder-unit botbuilder-unit.js

100% Statements 87/87
95.45% Branches 21/22
100% Functions 15/15
100% Lines 87/87
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171    2x 2x   2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x   2x                   114x 114x 4x 4x 4x 4x 110x 108x 106x     2x   112x           404x     114x 112x         112x   112x 112x 112x     92x 92x 92x       96x 44x   96x 96x                         112x 112x 112x 112x   112x   112x       498x 498x 498x 498x 446x       112x   112x         112x 110x 92x 2x 2x             112x 112x 310x 310x         1194x 1194x 288x 288x 288x 288x       310x 22x 22x 22x   22x         112x 112x 112x       2x 2x         2x 2x 2x 2x 2x 2x 2x 2x 2x 2x
"use strict";
 
const FINISH_TIMEOUT = 20;
const DEFAULT_TEST_TIMEOUT = 10000;
 
const SrcBasePath = process.env.BOTBUILDERUNIT_USE_INSTRUMENTED_SOURCE || './src';
const MessageFactory = require(SrcBasePath + '/ScriptStepFactory');
const PlainLogReporter = require(SrcBasePath + '/log-reporters/PlainLogReporter');
const EmptyLogReporter = require(SrcBasePath + '/log-reporters/EmptyLogReporter');
const BeautyLogReporter = require(SrcBasePath + '/log-reporters/BeautyLogReporter');
const BotMessage = require(SrcBasePath + '/messages/BotMessage');
const UserMessage = require(SrcBasePath + '/messages/UserMessage');
const SessionMessage = require(SrcBasePath + '/messages/SessionMessage');
const SetDialogMessage = require(SrcBasePath + '/messages/SetDialogMessage');
const TestConnector = require(SrcBasePath + '/TestConnector');
const detectReporter = require(SrcBasePath + '/detectReporter')
 
const builder = require('botbuilder');
 
/**
 * Wraps arrays and functions into instance of UniversalBot
 * Ensure that given bot has connector, otherwise inject TestConnector
 *
 * @param bot
 * @returns {*}
 */
function resolveBot(bot) {
  let isWaterfall = ("function" == typeof bot) || Array.isArray(bot);
  if (isWaterfall) {
    let dialog = bot;
    let connector = new TestConnector();
    bot = new builder.UniversalBot(connector);
    bot.dialog('/', dialog);
  } else if (bot instanceof builder.UniversalBot) {
    if ( !bot.connector()) {
      bot.connector('console', new TestConnector());
    }
  } else {
    throw new Error(`Unknown type of bot/dialog. Error: ${JSON.stringify(bot)}`);
  }
  return bot;
}
 
 
function testBot(bot, messages, options) {
  function getLogReporter() {
    return options.reporter;
  }
 
  bot = resolveBot(bot);
  options = Object.assign({
    timeout: module.exports.config.timeout,
    reporter: module.exports.config.reporter,
    title: ''
  }, options);
  messages = messages.slice(0);
 
  return new Promise(function (resolve, reject) {
    var step = 0;
    var finished = false;
 
    function done() {
      finished = true;
      getLogReporter().scriptFinished();
      resolve();
    }
 
    function fail(err) {
      if (!finished) {
        getLogReporter().error("Script failed with error:", err);
      }
      finished = true;
      reject(err);
    }
 
 
    /**
     * Convert script steps into promises, each step executed after previous
     * botmessage depends on previous step and bot response
     * customessage depends on previous step
     * session message depends on previous step
     * set dialog message depends on previous step
     * user message dependes on previos step
     */
    function startTesting() {
      getLogReporter().newScript(messages, options.title);
      let startTestingAction = null;
      let prevStepFinishedPromise = new Promise((resolve, reject) => {
        startTestingAction = resolve;
      });
      let logReporter = getLogReporter();
 
      messages.forEach((item, i) => {
        // Where:
        // i - step
        // this - is actual config of step
        let scriptStep = MessageFactory(i, item, bot, logReporter, prevStepFinishedPromise);
        messages[i] = scriptStep;
        prevStepFinishedPromise = scriptStep.getStepFinishedPromise()
        prevStepFinishedPromise.then(() => {
          step = i;
        }, fail)
      });
 
      prevStepFinishedPromise.then(done, fail);
 
      startTestingAction();
 
    }
 
    function setupTimeout() {
      if (options.timeout) {
        setTimeout(() => {
          if (!finished) {
            let msg = `Default timeout (${options.timeout}) exceeded`;
            fail(msg);
          }
        }, options.timeout);
      }
    }
 
    function setupReplyReceiver() {
      let extraMessageIndex = messages.length;
      bot.on('send', function (message) {
        let found = false;
        messages.some((step) => {
          //
          // As bot could send multiple messages sequentially, next code tries to
          // secure library from the case when two or more replies will be received by
          // one step
          let botReplyFunc = step.receiveBotReply;
          if (botReplyFunc) {
            step.receiveBotReply = false;
            botReplyFunc.call(step, message);
            found = true;
            return -1;
          }
        })
 
        if (!found) {
          getLogReporter().messageReceived(extraMessageIndex, message)
          getLogReporter().warning(`Warning at EXTRA STEP ${extraMessageIndex}`, 'Ignoring message (Out of Range)');
          extraMessageIndex++;
          // As more messages could appear, I suppose that is better to track them
          setTimeout(done, FINISH_TIMEOUT);
        }
      });
    }
 
    setupTimeout();
    setupReplyReceiver();
    startTesting();
  })
}
 
module.exports = testBot;
module.exports.config = {
  timeout: process.env.BOTBUILDERUNIT_TEST_TIMEOUT ? process.env.BOTBUILDERUNIT_TEST_TIMEOUT : DEFAULT_TEST_TIMEOUT,
  reporter: detectReporter(process.env.BOTBUILDERUNIT_REPORTER)
};
 
module.exports.PlainLogReporter = PlainLogReporter;
module.exports.BeautyLogReporter = BeautyLogReporter;
module.exports.EmptyLogReporter = EmptyLogReporter;
module.exports.ConversationMock = require(SrcBasePath + '/ConversationMock');
module.exports.BotMessage = BotMessage;
module.exports.UserMessage = UserMessage;
module.exports.SetDialogMessage = SetDialogMessage;
module.exports.SessionMessage = SessionMessage;
module.exports.TestConnector = TestConnector;
module.exports.DEFAULT_ADDRESS = SessionMessage.DEFAULT_ADDRESS;