Skip to content

Commit

Permalink
added expected message received order agnostic method (#23)
Browse files Browse the repository at this point in the history
* bump version for v3.0.0

* added mocha options for coverage running

* added coverage

* added tests for ignore order

* added docs to readme

* remove all .only
  • Loading branch information
microsoftly authored Sep 7, 2017
1 parent 49950e6 commit 7312ba3
Show file tree
Hide file tree
Showing 10 changed files with 1,469 additions and 132 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
node_modules
dist
yarn-error.log
yarn-error.log
.coveralls.yml
.nyc_output
coverage
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,3 +342,18 @@ describe('BotTester', () => {
.runTest();
});
```
# can check messages while ignoring order
``` javascript
it('can accept messages without expectations for order', () => {
bot.dialog('/', (session) => {
session.send('hi');
session.send('there');
session.send('how are you?');
});

return new BotTester(bot)
.sendMessageToBotIgnoringResponseOrder('anything', 'how are you?', 'hi', 'there')
.runTest();
});
```
25 changes: 23 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "bot-tester",
"version": "2.0.2",
"version": "3.0.0",
"description": "Easily test bots made with the bot builder framework",
"main": "dist/BotTester.js",
"types": "dist/BotTester.d.ts",
Expand Down Expand Up @@ -41,7 +41,24 @@
"watch": "tsc -w",
"test": "node_modules/mocha/bin/_mocha --require node_modules/ts-node/register test/*.spec.ts",
"buildTypeDocs": "yarn cleanBuild && typedoc --excludeNotExported --out docs --hideGenerator src/BotTester.ts --module commonjs --gaID UA-105358964-1 --readme none && touch docs/.nojekyll",
"ci": "npm run lint && npm run test"
"ci": "npm run lint && npm run test",
"coverage": "npm run coverageTests && nyc report --reporter=text-lcov | node_modules/.bin/coveralls",
"coverageTests": "nyc node_modules/.bin/_mocha --require source-map-support/register test/**/*.spec.ts"
},
"nyc": {
"extension": [
".ts",
".tsx"
],
"exclude": [
"**/*.d.ts",
"**/*.js",
"**/*.map"
],
"reporter": [
"html"
],
"all": true
},
"dependencies": {
"bluebird": "^3.5.0",
Expand All @@ -60,7 +77,11 @@
"@types/mocha": "^2.2.41",
"@types/node": "^8.0.26",
"chai-as-promised": "^7.1.1",
"coveralls": "^2.13.1",
"istanbul": "^0.4.5",
"mocha": "^3.4.2",
"nyc": "^11.2.1",
"source-map-support": "^0.4.17",
"ts-node": "^3.1.0",
"typedoc": "^0.8.0",
"tslint": "^5.7.0",
Expand Down
19 changes: 16 additions & 3 deletions src/BotTester.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,19 @@ export class BotTester {
return this.sendMessageToBotInternal(message, expectedResponses);
}

public sendMessageToBotIgnoringResponseOrder(
msg: IMessage | string,
// currently only supports string RegExp IMessage
...expectedResponses: (PossibleExpectedMessageType | PossibleExpectedMessageType[])[]
): BotTester {
const message = this.convertToIMessage(msg);

// possible that expected responses may be undefined. Remove them
expectedResponses = expectedResponses.filter((expectedResponse: {}) => expectedResponse);

return this.sendMessageToBotInternal(message, expectedResponses, true);
}

/**
* sends a message to the bot. This should be used whenever session.save() is used without sending a reply to the user. This exists due
* to a limitation in the current implementation of the botbuilder framework
Expand Down Expand Up @@ -138,8 +151,8 @@ export class BotTester {
private sendMessageToBotInternal(
message: IMessage,
// currently only supports string RegExp IMessage
expectedResponses: (PossibleExpectedMessageType | PossibleExpectedMessageType[])[]
//PossibleExpectedMessageCollections | PossibleExpectedMessageCollections[]
expectedResponses: (PossibleExpectedMessageType | PossibleExpectedMessageType[])[],
ignoreOrder: boolean = false
): BotTester {
let expectedMessages: ExpectedMessage[] = [];

Expand All @@ -156,7 +169,7 @@ export class BotTester {
}
}

this.testSteps.push(() => this.messageService.sendMessageToBot(message, expectedMessages));
this.testSteps.push(() => this.messageService.sendMessageToBot(message, expectedMessages, ignoreOrder));

return this;
}
Expand Down
5 changes: 3 additions & 2 deletions src/MessageService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ export class MessageService {
*/
public sendMessageToBot(
message: IMessage,
expectedResponses: ExpectedMessage[]
expectedResponses: ExpectedMessage[],
ignoreOrder: boolean = false
): Promise<void> {
const outgoingMessageComparator = new OutgoingMessageComparator(expectedResponses);
const outgoingMessageComparator = new OutgoingMessageComparator(expectedResponses, ignoreOrder);
const responsesFullyProcessedPromise = this.setBotToUserMessageChecker(expectedResponses, outgoingMessageComparator);

const receiveMessagePromise = new Promise<void>((res: () => void, rej: (e: Error) => void) => {
Expand Down
47 changes: 41 additions & 6 deletions src/OutgoingMessageComparator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import { ExpectedMessage } from './ExpectedMessage';
* Manages the comparisons against expected messages and outgoingMessages that the BotTester framework intercepts
*/
export class OutgoingMessageComparator {
private readonly expectedMessages: ExpectedMessage[];
private expectedMessages: ExpectedMessage[];
private readonly ignoreOrder: boolean;

constructor(expectedMessages: ExpectedMessage[]) {
constructor(expectedMessages: ExpectedMessage[], ignoreOrder: boolean) {
this.expectedMessages = expectedMessages;
this.ignoreOrder = ignoreOrder;
}

/**
* compares the current outgoing message against the current expected message
*/
public compareOutgoingMessageToExpectedResponses(outgoingMessage: IMessage): void {
const nextMessage = this.dequeueNextExpectedMessage();

if (nextMessage) {
nextMessage.checkForMessageMatch(outgoingMessage);
if (this.ignoreOrder) {
this.compareOutgoingMessageToExpectedResponsesWithoutOrder(outgoingMessage);
} else {
this.compareOutgoingMessageToExpectedResponsesInOrder(outgoingMessage);
}
}

Expand All @@ -33,6 +35,39 @@ export class OutgoingMessageComparator {
return `timedout while waiting to receive ${this.expectedMessages[0].toString()}`;
}

private compareOutgoingMessageToExpectedResponsesInOrder(outgoingMessage: IMessage): void {
const nextMessage = this.dequeueNextExpectedMessage();

if (nextMessage) {
nextMessage.checkForMessageMatch(outgoingMessage);
}
}

private compareOutgoingMessageToExpectedResponsesWithoutOrder(outgoingMessage: IMessage): void {
const originalItemCount = this.expectedMessages.length;

let matchingMessageFound: boolean = false;
this.expectedMessages = this.expectedMessages.filter((expectedMessage: ExpectedMessage, i: number) => {
if (matchingMessageFound) {
return true;
}

try {
expectedMessage.checkForMessageMatch(outgoingMessage);

matchingMessageFound = true;

return false;
} catch (e) {
return true;
}
});

if (originalItemCount === this.expectedMessages.length) {
throw new Error(`${JSON.stringify(outgoingMessage, null, 2)} did not match any of ${this.expectedMessages.map((t: ExpectedMessage) => t.toString())}`);
}
}

/**
* gets the next expected message and removes it from the expectedMessages
*/
Expand Down
20 changes: 17 additions & 3 deletions test/BotTester.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//```javascript
import 'mocha'
import { ConsoleConnector, IAddress, IMessage, Message, Prompts, Session, UniversalBot } from 'botbuilder';
import { expect } from 'chai';
import 'mocha'
import { BotTester } from './../src/BotTester';

const connector = new ConsoleConnector();
Expand Down Expand Up @@ -307,9 +307,8 @@ describe('BotTester', () => {
bot.dialog('/', (session) => {
// send only numbers for this test case ....
if (afterDelayTime - beforeDelayTime >= delay) {
session.send('i waited some time');
session.send('i waited some time');
}

});

return new BotTester(bot)
Expand All @@ -321,6 +320,21 @@ describe('BotTester', () => {
});
//```

//# can check messages while ignoring order
//``` javascript
it('can accept messages without expectations for order', () => {
bot.dialog('/', (session) => {
session.send('hi');
session.send('there');
session.send('how are you?');
});

return new BotTester(bot)
.sendMessageToBotIgnoringResponseOrder('anything', 'how are you?', 'hi', 'there')
.runTest();
});
//```

describe('Cases not for docs', () => {
it('can handle undefined expectedResponses', () => {
bot.dialog('/', (session: Session) => {
Expand Down
12 changes: 12 additions & 0 deletions test/BotTesterFailure.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,16 @@ describe('BotTester', () => {
.sendMessageToBot('hey', 'hi there')
.runTest()).to.be.rejected.notify(done);
});

it('will fail random order if response is not in collection', (done: Function) => {
bot.dialog('/', (session: Session) => {
session.send('hi');
session.send('there');
session.send('how are you?');
});

expect(new BotTester(bot)
.sendMessageToBotIgnoringResponseOrder('anything', 'NOPE?', 'hi', 'there')
.runTest()).to.be.rejected.notify(done);
});
});
3 changes: 3 additions & 0 deletions test/mocha.opts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--require ts-node/register
--recursive
test/*.spec.ts
Loading

0 comments on commit 7312ba3

Please sign in to comment.