Skip to content

Commit

Permalink
add adaptive cards (and attachment) support (#44)
Browse files Browse the repository at this point in the history
* add adaptiveCardProvider

* add adaptivecards package

* add adaptive cards implementation

* add adaptive cards tests

* update docs

* include ts-node require

* update node version

* update mocha version

* add yarn to circle config
  • Loading branch information
microsoftly committed Dec 7, 2017
1 parent 6b2e716 commit e1be08f
Show file tree
Hide file tree
Showing 8 changed files with 193 additions and 13 deletions.
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ For a more in depth view, check out [the Bot Tester Framework Config doc](https:
import { IAddress, IMessage, Message, Prompts, Session, UniversalBot } from 'botbuilder';
import { expect } from 'chai';
import { BotTester, TestConnector } from 'bot-tester';
import { getAdaptiveCard, getAdaptiveCardAttachment, getAdaptiveCardMessage } from './adaptiveCardProvider';

const connector = new TestConnector();

Expand Down Expand Up @@ -113,6 +114,19 @@ describe('BotTester', () => {
});
```
# Test Adaptive Cards
``` javascript
it('can correctly check against adaptive cards', () => {
bot.dialog('/', (session) => {
session.send(getAdaptiveCardMessage());
});

return new BotTester(bot)
.sendMessageToBot('anything', getAdaptiveCardMessage())
.runTest();
});
```
# Inspect session
```javascript
it('can inspect session state', () => {
Expand Down
11 changes: 10 additions & 1 deletion circle.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
machine:
node:
version: 6.1.0
version: 8.0.0
environment:
PATH: "${PATH}:${HOME}/${CIRCLE_PROJECT_REPONAME}/node_modules/.bin"

dependencies:
override:
- yarn
cache_directories:
- ~/.cache/yarn

test:
override:
- yarn lint && yarn coverage

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"buildTypeDocs": "yarn cleanBuild && rm -rf docs && 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",
"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"
"coverageTests": "nyc node_modules/.bin/_mocha --require source-map-support/register --require node_modules/ts-node/register test/**/*.spec.ts"
},
"nyc": {
"extension": [
Expand Down Expand Up @@ -74,17 +74,18 @@
"@types/chai": "^4.0.1",
"@types/chai-as-promised": "^0.0.31",
"@types/chai-subset": "^1.3.1",
"@types/mocha": "^2.2.41",
"@types/mocha": "^2.2.44",
"@types/node": "^8.0.26",
"adaptivecards": "^1.0.0-beta9",
"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",
"typedoc": "^0.8.0",
"typescript": "^2.4.1"
},
"files": [
Expand Down
50 changes: 44 additions & 6 deletions src/assertionLibraries/ChaiExpectation.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IMessage } from 'botbuilder';
import { IAttachment, IMessage } from 'botbuilder';
import * as chai from 'chai';
import { IExpectation } from './IExpectation';

Expand Down Expand Up @@ -72,20 +72,32 @@ export class ChaiExpectation implements IExpectation {
return;
}

const clone = Object.assign({}, outgoingMessage);
const outgoingMessageClone = Object.assign({}, outgoingMessage);
const expectedResponseClone = Object.assign({}, expectedResponse);

// attachments are compared separately
const outgoingMessageAttachments = outgoingMessageClone.attachments || [];
const expectedResponseAttachments = expectedResponseClone.attachments || [];

outgoingMessageClone.attachments = [];
expectedResponseClone.attachments = [];

// ignore source event (not added to message until after sending)
delete expectedResponse.source;
delete expectedResponseClone.source;

// auto added by prompts, not needed
delete outgoingMessage.inputHint;
delete outgoingMessageClone.inputHint;

// always botbuilder
delete expectedResponse.agent;
delete expectedResponseClone.agent;

try {
expect(clone).to.containSubset(expectedResponse);
expect(outgoingMessageClone).to.containSubset(expectedResponseClone);
matchExists = true;

if (matchExists && expectedResponseAttachments.length) {
matchExists = this.checkForMatchingAttachments(outgoingMessageAttachments, expectedResponseAttachments);
}
} catch (e) {
// continue, no match found
}
Expand All @@ -96,4 +108,30 @@ export class ChaiExpectation implements IExpectation {
to be a subset of one of ${JSON.stringify(expectedResponseCollectionAsIMessage, null, 2)}`);
}
}

private checkForMatchingAttachments(outgoingAttachments: IAttachment[], expectedAttachments: IAttachment[]): boolean {
let matchExists: boolean = false;

outgoingAttachments.forEach((outgoingAttachment: IAttachment) => {
if (matchExists) {
return;
}

expectedAttachments.forEach((expectedAttachment: IAttachment) => {
if (matchExists) {
return;
}

try {
expect(outgoingAttachment).to.containSubset(expectedAttachment);

matchExists = true;
} catch (e) {
// continue
}
});
});

return matchExists;
}
}
36 changes: 36 additions & 0 deletions test/BotTester.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import { BotTester } from './../src/BotTester';
import { TestConnector } from './../src/TestConnector';
import { getAdaptiveCard, getAdaptiveCardAttachment, getAdaptiveCardMessage } from './adaptiveCardProvider';

chai.use(chaiAsPromised);
const expect = chai.expect;
Expand Down Expand Up @@ -85,6 +86,19 @@ describe('BotTester', () => {
});
//```

//# Test Adaptive Cards
//``` javascript
it('can correctly check against adaptive cards', () => {
bot.dialog('/', (session) => {
session.send(getAdaptiveCardMessage());
});

return new BotTester(bot)
.sendMessageToBot('anything', getAdaptiveCardMessage())
.runTest();
});
//```

//# Inspect session
//```javascript
it('can inspect session state', () => {
Expand Down Expand Up @@ -416,5 +430,27 @@ describe('BotTester', () => {
.sendMessageToBot('this could be anything!', 'hello')
.runTest();
});

it('can ensure adaptive cards are present, regardless of order', () => {
bot.dialog('/', (session: Session) => {
session.send(getAdaptiveCardMessage());
});

const matchingCard = getAdaptiveCard();
const nonMatchingCard = getAdaptiveCard();

nonMatchingCard.actions = [{title: 'this is not the correct title', type: 'this is no the correct type'}];

const message1 = getAdaptiveCardMessage(nonMatchingCard);
const message2 = getAdaptiveCardMessage(matchingCard);

message1.attachments.push(getAdaptiveCardAttachment(matchingCard));
message2.attachments.push(getAdaptiveCardAttachment(nonMatchingCard));

return new BotTester(bot)
.sendMessageToBot('anything', message1)
.sendMessageToBot('anything', message2)
.runTest();
});
});
});
31 changes: 31 additions & 0 deletions test/BotTesterFailure.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as chai from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import { BotTester } from './../src/BotTester';
import { TestConnector } from './../src/TestConnector';
import { getAdaptiveCard, getAdaptiveCardAttachment, getAdaptiveCardMessage } from './adaptiveCardProvider';

chai.use(chaiAsPromised);

Expand Down Expand Up @@ -253,4 +254,34 @@ describe('BotTester', () => {
.runTest())
.to.be.rejected.notify(done);
});

it('will fail when there are no matching adaptive cards', (done: Function) => {
bot.dialog('/', (session: Session) => {
session.send(getAdaptiveCardMessage());
});

const card = getAdaptiveCard();

card.actions = [{title: 'this is not the correct title', type: 'this is no the correct type'}];

const message = getAdaptiveCardMessage(card);

expect(new BotTester(bot)
.sendMessageToBot('anything', message)
.runTest()).to.be.rejected.notify(done);
});

it('will fail when there are no attachments when they are expected', (done: Function) => {
bot.dialog('/', (session: Session) => {
session.send('hello');
});

const expectedMessage = getAdaptiveCardMessage();

expectedMessage.text = 'hello';

expect(new BotTester(bot)
.sendMessageToBot('anything', expectedMessage)
.runTest()).to.be.rejected.notify(done);
});
});
47 changes: 47 additions & 0 deletions test/adaptiveCardProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { IAdaptiveCard } from 'adaptivecards';
import { IAttachment, IMessage, Message} from 'botbuilder';

export function getAdaptiveCard() : IAdaptiveCard {
const action = {
type: 'Action.OpenUrl',
url: 'http://adaptivecards.io',
title: 'Learn More'
};

return {
type: 'AdaptiveCard',
version: '1.0',
body: [
{
type: 'TextBlock',
text: 'Hello World!',
size: 'large'
},
{
type: 'TextBlock',
text: '*Sincerely yours,*'
},
{
type: 'TextBlock',
text: 'Adaptive Cards',
separation: 'none'
}
],
actions: [ action ]
};
}

export function getAdaptiveCardAttachment(adaptiveCard?: IAdaptiveCard): IAttachment {
adaptiveCard = adaptiveCard || getAdaptiveCard();

return {
contentType: 'application/vnd.microsoft.card.adaptive',
content: adaptiveCard
};
}

export function getAdaptiveCardMessage(adaptiveCard?: IAdaptiveCard): IMessage {
return new Message()
.addAttachment(getAdaptiveCardAttachment(adaptiveCard))
.toMessage();
}
10 changes: 7 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,9 @@
version "2.0.29"
resolved "https://registry.yarnpkg.com/@types/minimatch/-/minimatch-2.0.29.tgz#5002e14f75e2d71e564281df0431c8c1b4a2a36a"

"@types/mocha@^2.2.41":
version "2.2.42"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.42.tgz#ab769f51d37646b6fe8d4a086a98c285b1fab3f5"
"@types/mocha@^2.2.44":
version "2.2.44"
resolved "https://registry.yarnpkg.com/@types/mocha/-/mocha-2.2.44.tgz#1d4a798e53f35212fd5ad4d04050620171cd5b5e"

"@types/node@*", "@types/node@^8.0.26":
version "8.0.27"
Expand All @@ -86,6 +86,10 @@ abbrev@1.0.x:
version "1.0.9"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135"

adaptivecards@^1.0.0-beta9:
version "1.0.0-beta9"
resolved "https://registry.yarnpkg.com/adaptivecards/-/adaptivecards-1.0.0-beta9.tgz#bc22f61978c58666c8e862c9ad5b4b43b0b61fa8"

ajv@^4.9.1:
version "4.11.8"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536"
Expand Down

0 comments on commit e1be08f

Please sign in to comment.