All files / botbuilder-unit botbuilder-unit.js

94.57% Statements 87/92
75% Branches 21/28
94.12% Functions 16/17
94.57% Lines 87/92
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 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188    1x 1x   1x 1x 1x 1x 1x 1x 1x 1x 1x   1x   1x     1x       1x                           41x 41x 2x 2x 2x 2x 39x 39x 38x         41x           136x                 41x 41x         41x   41x 41x 41x     37x 37x 37x       19x 9x   19x 19x                         41x 41x 41x 41x   41x   41x       200x 200x 200x 200x 190x       41x   41x         41x 40x 32x 1x 1x             41x 41x 120x 120x         494x 494x 116x 116x 116x 116x       120x 4x 4x 4x   4x         41x 41x 41x       1x 1x         1x 1x 1x 1x 1x 1x 1x 1x 1x 1x
"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 builder = require('botbuilder');
 
function detectReporter() {
  switch (process.env.BOTBUILDERUNIT_REPORTER) {
    case 'beauty':
      return (new BeautyLogReporter());
    case 'empty' :
      return (new EmptyLogReporter());
    case 'plain' :
    default:
      return (new PlainLogReporter());
  }
}
/**
 * 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 Eif (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;
  }
 
  function callTrigger(check, bot, name, args) {
    if ("function" == typeof check[name]) {
      check[name](bot, args);
    }
  }
 
  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.produce(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()
};
 
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;