1 | # Hubot test helper
|
2 |
|
3 | [![Build Status](https://travis-ci.org/mtsmfm/hubot-test-helper.svg?branch=master)](https://travis-ci.org/mtsmfm/hubot-test-helper)
|
4 |
|
5 | Helper for testing Hubot script.
|
6 |
|
7 | ## Install
|
8 |
|
9 | `npm install hubot-test-helper --save-dev`
|
10 |
|
11 | ## Usage
|
12 |
|
13 | If you have a following hubot script:
|
14 |
|
15 | ```javascript
|
16 | module.exports = robot =>
|
17 | robot.respond(/hi$/i, msg => msg.reply('hi'))
|
18 | ```
|
19 |
|
20 | You can test it like:
|
21 |
|
22 | ```javascript
|
23 | const Helper = require('hubot-test-helper');
|
24 | // helper loads all scripts passed a directory
|
25 | const helper = new Helper('./scripts');
|
26 |
|
27 | // helper loads a specific script if it's a file
|
28 | const scriptHelper = new Helper('./scripts/specific-script.js');
|
29 |
|
30 | const co = require('co');
|
31 | const expect = require('chai').expect;
|
32 |
|
33 | describe('hello-world', function() {
|
34 | beforeEach(function() {
|
35 | this.room = helper.createRoom();
|
36 | });
|
37 | afterEach(function() {
|
38 | this.room.destroy();
|
39 | });
|
40 |
|
41 | context('user says hi to hubot', function() {
|
42 | beforeEach(function() {
|
43 | return co(function*() {
|
44 | yield this.room.user.say('alice', '@hubot hi');
|
45 | yield this.room.user.say('bob', '@hubot hi');
|
46 | }.bind(this));
|
47 | });
|
48 |
|
49 | it('should reply to user', function() {
|
50 | expect(this.room.messages).to.eql([
|
51 | ['alice', '@hubot hi'],
|
52 | ['hubot', '@alice hi'],
|
53 | ['bob', '@hubot hi'],
|
54 | ['hubot', '@bob hi']
|
55 | ]);
|
56 | });
|
57 | });
|
58 | });
|
59 | ```
|
60 |
|
61 | #### HTTPD
|
62 |
|
63 | By default Hubot enables a built in HTTP server. The server continues between
|
64 | tests and so requires it to be shutdown during teardown using `room.destroy()`.
|
65 |
|
66 | This feature can be turned off in tests that don't need it by passing using
|
67 | `helper.createRoom(httpd: false)`.
|
68 |
|
69 | See [the tests](test/httpd-world_test.js) for an example of testing the
|
70 | HTTP server.
|
71 |
|
72 |
|
73 | #### Manual delay
|
74 |
|
75 | Sometimes we can't access callback actions from a script.
|
76 | Just like in real use-case we may have to wait for a bot to finish processing before replying,
|
77 | in testing we may anticipate the delayed reply with a manual time delay.
|
78 |
|
79 | For example we have the following script:
|
80 |
|
81 | ```javascript
|
82 | module.exports = robot =>
|
83 | robot.hear(/(http(?:s?):\/\/(\S*))/i, res => {
|
84 | const url = res.match[1];
|
85 | res.send(`ok1: ${url}`);
|
86 | robot.http(url).get()((err, response, body) => res.send(`ok2: ${url}`));
|
87 | });
|
88 | ```
|
89 |
|
90 | To test the second callback response "ok2: ..." we use the following script:
|
91 |
|
92 | ```javascript
|
93 | const Helper = require('hubot-test-helper');
|
94 | const helper = new Helper('../scripts/http.js');
|
95 |
|
96 | const Promise = require('bluebird');
|
97 | const co = require('co');
|
98 | const expect = require('chai').expect;
|
99 |
|
100 | // test ping
|
101 | describe('http', function() {
|
102 | beforeEach(function() {
|
103 | this.room = helper.createRoom({httpd: false});
|
104 | });
|
105 |
|
106 | // Test case
|
107 | context('user posts link', function() {
|
108 | beforeEach(function() {
|
109 | return co(function*() {
|
110 | yield this.room.user.say('user1', 'http://google.com');
|
111 | // delay one second for the second
|
112 | // callback message to be posted to @room
|
113 | yield new Promise.delay(1000);
|
114 | }.bind(this));
|
115 | });
|
116 |
|
117 | // response
|
118 | it('expects deplayed callback from ok2', function() {
|
119 | console.log(this.room.messages);
|
120 | expect(this.room.messages).to.eql([
|
121 | ['user1', 'http://google.com'],
|
122 | ['hubot', 'ok1: http://google.com'],
|
123 | ['hubot', 'ok2: http://google.com']
|
124 | ]);
|
125 | });
|
126 | });
|
127 | });
|
128 | ```
|
129 |
|
130 | Note that `yield` and *generators* are part of [**ECMA6**](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*), so it may not work on older node.js versions. It will wait for the delay to complete the `beforeEach` before proceeding to the test `it`.
|
131 |
|
132 |
|
133 | #### Testing messages sent to other rooms
|
134 |
|
135 | You can also test messages sent by your script to other rooms through Hubot's `robot.messageRoom(...)` method.
|
136 |
|
137 | Given the following script:
|
138 | ```javascript
|
139 | module.exports = robot =>
|
140 | robot.respond(/announce otherRoom: (.+)$/i, msg => {
|
141 | robot.messageRoom('otherRoom', "@#{msg.envelope.user.name} said: #{msg.msg.match[1]}");
|
142 | })
|
143 | ```
|
144 |
|
145 | you could test the messages sent to other rooms like this:
|
146 | ```javascript
|
147 | const Helper = require('../src/index');
|
148 | const helper = new Helper('../scripts/message-room.js');
|
149 |
|
150 | const expect = require('chai').expect;
|
151 |
|
152 | describe('message-room', function() {
|
153 | beforeEach(function() {
|
154 | this.room = helper.createRoom({name: 'room', httpd: false});
|
155 | });
|
156 |
|
157 | context('user asks hubot to announce something', function() {
|
158 | beforeEach(function() {
|
159 | return co(function*() {
|
160 | yield this.room.user.say('alice', '@hubot announce otherRoom: I love hubot!');
|
161 | }.bind(this));
|
162 | });
|
163 |
|
164 | it('should not post to this channel', function() {
|
165 | expect(this.room.messages).to.eql([
|
166 | ['alice', '@hubot announce otherRoom: I love hubot!']
|
167 | ]);
|
168 | });
|
169 |
|
170 | it('should post to the other channel', function() {
|
171 | expect(this.room.robot.messagesTo['otherRoom']).to.eql([
|
172 | ['hubot', '@alice says: I love hubot!']
|
173 | ]);
|
174 | });
|
175 | });
|
176 | });
|
177 | ```
|
178 |
|
179 |
|
180 | #### Testing events
|
181 |
|
182 | You can also test events emitted by your script. For example, Slack users
|
183 | may want to test the creation of a
|
184 | [message attachment](https://api.slack.com/docs/attachments).
|
185 |
|
186 | Given the following script:
|
187 |
|
188 | ```javascript
|
189 | module.exports = robot =>
|
190 | robot.respond(/check status$/i, msg =>
|
191 | robot.emit('slack.attachment', {
|
192 | message: msg.message,
|
193 | content: {
|
194 | color: "good",
|
195 | text: "It's all good!"
|
196 | }
|
197 | })
|
198 | )
|
199 | ```
|
200 |
|
201 | you could test the emitted event like this:
|
202 |
|
203 | ```javascript
|
204 | const Helper = require('hubot-test-helper');
|
205 | const helper = new Helper('../scripts/status_check.js');
|
206 |
|
207 | const expect = require('chai').expect;
|
208 |
|
209 | describe('status check', function() {
|
210 | beforeEach(function() {
|
211 | this.room = helper.createRoom({httpd: false});
|
212 | });
|
213 |
|
214 | it('should send a slack event', function() {
|
215 | let response = null;
|
216 | this.room.robot.on('slack.attachment', event => response = event.content);
|
217 |
|
218 | this.room.user.say('bob', '@hubot check status').then(() => {
|
219 | expect(response.text).to.eql("It's all good!");
|
220 | });
|
221 | });
|
222 | });
|
223 | ```
|
224 |
|
225 | ## Development
|
226 |
|
227 | ### Requirements
|
228 |
|
229 | - docker
|
230 | - docker-compose
|
231 |
|
232 | ### Setup
|
233 |
|
234 | ```
|
235 | git clone https://github.com/mtsmfm/hubot-test-helper
|
236 | cd hubot-test-helper
|
237 | docker-compose up -d
|
238 | docker-compose exec app bash
|
239 | yarn install
|
240 | ```
|
241 |
|
242 | ### Run test
|
243 |
|
244 | ```
|
245 | yarn run test
|
246 | ```
|
247 |
|
248 | #### Debug
|
249 |
|
250 | ```
|
251 | yarn run test-unit-debug
|
252 | ```
|
253 |
|
254 | Above command will output:
|
255 |
|
256 | ```
|
257 | yarn run v0.18.1
|
258 | $ mocha --inspect --debug-brk --compilers coffee:coffee-script/register test
|
259 | Debugger listening on port 9229.
|
260 | Warning: This is an experimental feature and could change at any time.
|
261 | To start debugging, open the following URL in Chrome:
|
262 | chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/59631086-0a0c-424b-8f5b-8828be123894
|
263 | ```
|
264 |
|
265 | Then open `chrome-devtools://devtools/bundled/inspector.html?experiments=true&v8only=true&ws=127.0.0.1:9229/59631086-0a0c-424b-8f5b-8828be123894` in Chrome.
|