import * as Promise from 'bluebird' ; import { IAddress , IMessage , Message , Session , UniversalBot } from 'botbuilder' ; import { ignoreEndOfConversationEventFilter , ignoreTypingEventFilter } from './builtInMessageFilters' ; import { getConfig , IConfig , MessageFilter } from './config' ; import { ExpectedMessage , PossibleExpectedMessageCollections , PossibleExpectedMessageType } from './ExpectedMessage' ; import { botToUserMessageCheckerFunction , MessageService } from './MessageService' ; import { SessionService } from './SessionService' ; export type checkSessionFunction = ( s : Session ) => void ; type TestStep = ( ) => Promise < any > ; /**
 * methods that the Test builder can call to edit/modify the options. These can be called until any of the IBotTester method are called.
 */ export interface IOptionsModifier { param filter addMessageFilter ( filter ) : BotTester ; /**
     * sets the timeout time that the BotTester will wait for any particular message before failing
     */ setTimeout ( milliseconds ) : BotTester ; /**
     * adds prebuilt filter to ignore typing event
     */ ignoreTypingEvent ( ) : BotTester ; /**
     * adds prebuilt filter to ignore endOfConversationEvent
     */ ignoreEndOfConversationEvent ( ) : BotTester ; } /**
 * Test builder/runner suite. After any of these are called, no functions in IConfigModified should be accessible
 */ export interface IBotTester { /**
     * executes each test step serially.
     */ runTest ( ) : Promise < { } > ; param sessionCheckerFunction param address checkSession ( sessionCheckerFunction , address ? ) : IBotTester ; param msg param expectedResponses sendMessageToBot ( msg , ... expectedResponses ) : IBotTester ; /**
     * same as sendMessageToBot, but the order of responses is not checked. This will cause the test to hang until all messages it expects
     * are returned
     */ sendMessageToBotIgnoringResponseOrder ( msg , ... expectedResponses ) : IBotTester ; param msg sendMessageToBotAndExpectSaveWithNoResponse ( msg ) : IBotTester ; param fn then ( fn ) : IBotTester ; param delayInMiliseconds wait ( delayInMilliseconds ) : IBotTester ; } /**
 * Test builder and runner for botbuilder bots
 */ export class BotTester implements IBotTester , IOptionsModifier { private bot : UniversalBot ; private sessionLoader : SessionService ; private messageService : MessageService ; private testSteps : TestStep [ ] ; private config : IConfig ; constructor ( bot , options ? ) { this . config = Object . assign ( { } , getConfig ( ) , options ) ; this . config . messageFilters = this . config . messageFilters . slice ( ) ; this . bot = bot ; this . sessionLoader = new SessionService ( bot ) ; this . testSteps = [ ] as TestStep [ ] ; } public addMessageFilter ( messageFilter ) { this . config . messageFilters . push ( messageFilter ) ; return this ; } public setTimeout ( milliseconds ) { this . config . timeout = milliseconds ; return this ; } public ignoreEndOfConversationEvent ( ) { this . config . ignoreEndOfConversationEvent = true ; return this ; } public ignoreTypingEvent ( ) { this . config . ignoreTypingEvent = true ; return this ; } /**
     * Initializes the MessegeService here to allow config changes to accumulate
     */ public runTest ( ) { if ( this . config . ignoreTypingEvent ) { this . config . messageFilters . push ( ignoreTypingEventFilter ) ; } if ( this . config . ignoreEndOfConversationEvent ) { this . config . messageFilters . push ( ignoreEndOfConversationEventFilter ) ; } this . messageService = new MessageService ( this . bot , this . config ) ; return Promise . mapSeries ( this . testSteps , ( fn ) => fn ( ) ) ; } public checkSession ( sessionCheckerFunction , address ? ) { const runSessionChecker = ( ) => this . sessionLoader . getSession ( address || this . config . defaultAddress ) . then ( sessionCheckerFunction ) ; this . testSteps . push ( runSessionChecker ) ; return this ; } public sendMessageToBot ( msg , ... expectedResponses ) { const message = this . convertToIMessage ( msg ) ; expectedResponses = expectedResponses . filter ( ( expectedResponse ) => expectedResponse ) ; return this . sendMessageToBotInternal ( message , expectedResponses ) ; } public sendMessageToBotIgnoringResponseOrder ( msg , ... expectedResponses ) { const message = this . convertToIMessage ( msg ) ; expectedResponses = expectedResponses . filter ( ( expectedResponse ) => expectedResponse ) ; return this . sendMessageToBotInternal ( message , expectedResponses , true ) ; } public sendMessageToBotAndExpectSaveWithNoResponse ( msg ) { const message = this . convertToIMessage ( msg ) ; return this . sendMessageToBotInternal ( message , [ this . sessionLoader . getInternalSaveMessage ( message . address ) ] ) ; } public then ( fn ) { this . testSteps . push ( ( ) => Promise . method ( fn ) ( ) ) ; return this ; } public wait ( delayInMilliseconds ) { this . testSteps . push ( ( ) => Promise . delay ( delayInMilliseconds ) ) ; return this ; } private convertToIMessage ( msg ) { if ( typeof ( msg ) === 'string' ) { return new Message ( ) . text ( msg as string ) . address ( this . config . defaultAddress ) . toMessage ( ) ; } return msg ; } param message param expectedResponses private sendMessageToBotInternal ( message , expectedResponses , ignoreOrder = false ) { let expectedMessages = [ ] ; if ( ! expectedResponses ) { expectedMessages = [ ] ; } else if ( ! ( expectedResponses instanceof Array ) ) { expectedMessages = [ new ExpectedMessage ( this . config , expectedResponses ) ] ; } else if ( expectedResponses instanceof Array ) { if ( expectedResponses . length > 0 ) { expectedMessages = ( expectedResponses as PossibleExpectedMessageCollections [ ] ) . map ( ( currentExpectedResponseCollection ) => new ExpectedMessage ( this . config , currentExpectedResponseCollection ) ) ; } } this . testSteps . push ( ( ) => this . messageService . sendMessageToBot ( message , expectedMessages , ignoreOrder ) ) ; return this ; } }