UNPKG

33.4 kBJavaScriptView Raw
1'use strict'
2
3/* global describe, beforeEach, it, afterEach */
4/* eslint-disable no-unused-expressions */
5
6// Assertions and Stubbing
7const chai = require('chai')
8const sinon = require('sinon')
9chai.use(require('sinon-chai'))
10
11const expect = chai.expect
12
13// Hubot classes
14const Robot = require('../src/robot')
15const CatchAllMessage = require('../src/message').CatchAllMessage
16const EnterMessage = require('../src/message').EnterMessage
17const LeaveMessage = require('../src/message').LeaveMessage
18const TextMessage = require('../src/message').TextMessage
19const TopicMessage = require('../src/message').TopicMessage
20
21// mock `hubot-mock-adapter` module from fixture
22const mockery = require('mockery')
23
24describe('Robot', function () {
25 beforeEach(function () {
26 mockery.enable({
27 warnOnReplace: false,
28 warnOnUnregistered: false
29 })
30 mockery.registerMock('hubot-mock-adapter', require('./fixtures/mock-adapter'))
31 this.robot = new Robot(null, 'mock-adapter', true, 'TestHubot')
32 this.robot.alias = 'Hubot'
33 this.robot.run()
34
35 // Re-throw AssertionErrors for clearer test failures
36 this.robot.on('error', function (name, err, response) {
37 if ((err != null ? err.constructor : undefined) == null) { }
38 if (err.constructor.name === 'AssertionError') {
39 process.nextTick(function () {
40 throw err
41 })
42 }
43 })
44
45 this.user = this.robot.brain.userForId('1', {
46 name: 'hubottester',
47 room: '#mocha'
48 })
49 })
50
51 afterEach(function () {
52 mockery.disable()
53 this.robot.shutdown()
54 })
55
56 describe('Unit Tests', function () {
57 describe('#http', function () {
58 beforeEach(function () {
59 const url = 'http://localhost'
60 this.httpClient = this.robot.http(url)
61 })
62
63 it('creates a new ScopedHttpClient', function () {
64 // 'instanceOf' check doesn't work here due to the design of
65 // ScopedHttpClient
66 expect(this.httpClient).to.have.property('get')
67 expect(this.httpClient).to.have.property('post')
68 })
69
70 it('passes options through to the ScopedHttpClient', function () {
71 const agent = {}
72 const httpClient = this.robot.http('http://localhost', {agent})
73 expect(httpClient.options.agent).to.equal(agent)
74 })
75
76 it('sets a sane user agent', function () {
77 expect(this.httpClient.options.headers['User-Agent']).to.contain('Hubot')
78 })
79
80 it('merges in any global http options', function () {
81 const agent = {}
82 this.robot.globalHttpOptions = {agent}
83 const httpClient = this.robot.http('http://localhost')
84 expect(httpClient.options.agent).to.equal(agent)
85 })
86
87 it('local options override global http options', function () {
88 const agentA = {}
89 const agentB = {}
90 this.robot.globalHttpOptions = {agent: agentA}
91 const httpClient = this.robot.http('http://localhost', {agent: agentB})
92 expect(httpClient.options.agent).to.equal(agentB)
93 })
94 })
95
96 describe('#respondPattern', function () {
97 it('matches messages starting with robot\'s name', function () {
98 const testMessage = this.robot.name + 'message123'
99 const testRegex = /(.*)/
100
101 const pattern = this.robot.respondPattern(testRegex)
102 expect(testMessage).to.match(pattern)
103 const match = testMessage.match(pattern)[1]
104 expect(match).to.equal('message123')
105 })
106
107 it('matches messages starting with robot\'s alias', function () {
108 const testMessage = this.robot.alias + 'message123'
109 const testRegex = /(.*)/
110
111 const pattern = this.robot.respondPattern(testRegex)
112 expect(testMessage).to.match(pattern)
113 const match = testMessage.match(pattern)[1]
114 expect(match).to.equal('message123')
115 })
116
117 it('does not match unaddressed messages', function () {
118 const testMessage = 'message123'
119 const testRegex = /(.*)/
120
121 const pattern = this.robot.respondPattern(testRegex)
122 expect(testMessage).to.not.match(pattern)
123 })
124
125 it('matches properly when name is substring of alias', function () {
126 this.robot.name = 'Meg'
127 this.robot.alias = 'Megan'
128 const testMessage1 = this.robot.name + ' message123'
129 const testMessage2 = this.robot.alias + ' message123'
130 const testRegex = /(.*)/
131
132 const pattern = this.robot.respondPattern(testRegex)
133
134 expect(testMessage1).to.match(pattern)
135 const match1 = testMessage1.match(pattern)[1]
136 expect(match1).to.equal('message123')
137
138 expect(testMessage2).to.match(pattern)
139 const match2 = testMessage2.match(pattern)[1]
140 expect(match2).to.equal('message123')
141 })
142
143 it('matches properly when alias is substring of name', function () {
144 this.robot.name = 'Megan'
145 this.robot.alias = 'Meg'
146 const testMessage1 = this.robot.name + ' message123'
147 const testMessage2 = this.robot.alias + ' message123'
148 const testRegex = /(.*)/
149
150 const pattern = this.robot.respondPattern(testRegex)
151
152 expect(testMessage1).to.match(pattern)
153 const match1 = testMessage1.match(pattern)[1]
154 expect(match1).to.equal('message123')
155
156 expect(testMessage2).to.match(pattern)
157 const match2 = testMessage2.match(pattern)[1]
158 expect(match2).to.equal('message123')
159 })
160 })
161
162 describe('#listen', () =>
163 it('registers a new listener directly', function () {
164 expect(this.robot.listeners).to.have.length(0)
165 this.robot.listen(function () {}, function () {})
166 expect(this.robot.listeners).to.have.length(1)
167 })
168 )
169
170 describe('#hear', () =>
171 it('registers a new listener directly', function () {
172 expect(this.robot.listeners).to.have.length(0)
173 this.robot.hear(/.*/, function () {})
174 expect(this.robot.listeners).to.have.length(1)
175 })
176 )
177
178 describe('#respond', () =>
179 it('registers a new listener using hear', function () {
180 sinon.spy(this.robot, 'hear')
181 this.robot.respond(/.*/, function () {})
182 expect(this.robot.hear).to.have.been.called
183 })
184 )
185
186 describe('#enter', () =>
187 it('registers a new listener using listen', function () {
188 sinon.spy(this.robot, 'listen')
189 this.robot.enter(function () {})
190 expect(this.robot.listen).to.have.been.called
191 })
192 )
193
194 describe('#leave', () =>
195 it('registers a new listener using listen', function () {
196 sinon.spy(this.robot, 'listen')
197 this.robot.leave(function () {})
198 expect(this.robot.listen).to.have.been.called
199 })
200 )
201
202 describe('#topic', () =>
203 it('registers a new listener using listen', function () {
204 sinon.spy(this.robot, 'listen')
205 this.robot.topic(function () {})
206 expect(this.robot.listen).to.have.been.called
207 })
208 )
209
210 describe('#catchAll', () =>
211 it('registers a new listener using listen', function () {
212 sinon.spy(this.robot, 'listen')
213 this.robot.catchAll(function () {})
214 expect(this.robot.listen).to.have.been.called
215 })
216 )
217
218 describe('#receive', function () {
219 it('calls all registered listeners', function (done) {
220 // Need to use a real Message so that the CatchAllMessage constructor works
221 const testMessage = new TextMessage(this.user, 'message123')
222
223 const listener = {
224 call (response, middleware, cb) {
225 cb()
226 }
227 }
228 sinon.spy(listener, 'call')
229
230 this.robot.listeners = [
231 listener,
232 listener,
233 listener,
234 listener
235 ]
236
237 this.robot.receive(testMessage, function () {
238 // When no listeners match, each listener is called twice: once with
239 // the original message and once with a CatchAll message
240 expect(listener.call).to.have.callCount(8)
241 done()
242 })
243 })
244
245 it('sends a CatchAllMessage if no listener matches', function (done) {
246 // Testing for recursion with a new CatchAllMessage that wraps the
247 // original message
248
249 const testMessage = new TextMessage(this.user, 'message123')
250 this.robot.listeners = []
251
252 // Replace @robot.receive so we can catch when the functions recurses
253 const oldReceive = this.robot.receive
254 this.robot.receive = function (message, cb) {
255 expect(message).to.be.instanceof(CatchAllMessage)
256 expect(message.message).to.be.equal(testMessage)
257 cb()
258 }
259 sinon.spy(this.robot, 'receive')
260
261 // Call the original receive method that we want to test
262 oldReceive.call(this.robot, testMessage, () => {
263 expect(this.robot.receive).to.have.been.called
264 done()
265 })
266 })
267
268 it('does not trigger a CatchAllMessage if a listener matches', function (done) {
269 const testMessage = new TextMessage(this.user, 'message123')
270
271 const matchingListener = {
272 call (message, middleware, doesMatch) {
273 // indicate that the message matched the listener
274 doesMatch(true)
275 }
276 }
277
278 // Replace @robot.receive so we can catch if the functions recurses
279 const oldReceive = this.robot.receive
280 this.robot.receive = sinon.spy()
281
282 this.robot.listeners = [
283 matchingListener
284 ]
285
286 // Call the original receive method that we want to test
287 oldReceive.call(this.robot, testMessage, done)
288
289 // Ensure the function did not recurse
290 expect(this.robot.receive).to.not.have.been.called
291 })
292
293 it('stops processing if a listener marks the message as done', function (done) {
294 const testMessage = new TextMessage(this.user, 'message123')
295
296 const matchingListener = {
297 call (message, middleware, doesMatch) {
298 message.done = true
299 // Listener must have matched
300 doesMatch(true)
301 }
302 }
303
304 const listenerSpy =
305 {call: sinon.spy()}
306
307 this.robot.listeners = [
308 matchingListener,
309 listenerSpy
310 ]
311
312 this.robot.receive(testMessage, function () {
313 expect(listenerSpy.call).to.not.have.been.called
314 done()
315 })
316 })
317
318 it('gracefully handles listener uncaughtExceptions (move on to next listener)', function (done) {
319 const testMessage = {}
320 const theError = new Error()
321
322 const badListener = {
323 call () {
324 throw theError
325 }
326 }
327
328 let goodListenerCalled = false
329 const goodListener = {
330 call (_, middleware, doesMatch) {
331 goodListenerCalled = true
332 doesMatch(true)
333 }
334 }
335
336 this.robot.listeners = [
337 badListener,
338 goodListener
339 ]
340
341 this.robot.emit = function (name, err, response) {
342 expect(name).to.equal('error')
343 expect(err).to.equal(theError)
344 expect(response.message).to.equal(testMessage)
345 }
346 sinon.spy(this.robot, 'emit')
347
348 this.robot.receive(testMessage, () => {
349 expect(this.robot.emit).to.have.been.called
350 expect(goodListenerCalled).to.be.ok
351 done()
352 })
353 })
354
355 it('executes the callback after the function returns when there are no listeners', function (done) {
356 const testMessage = new TextMessage(this.user, 'message123')
357 let finished = false
358 this.robot.receive(testMessage, function () {
359 expect(finished).to.be.ok
360 done()
361 })
362 finished = true
363 })
364 })
365
366 describe('#loadFile', function () {
367 beforeEach(function () {
368 this.sandbox = sinon.sandbox.create()
369 })
370
371 afterEach(function () {
372 this.sandbox.restore()
373 })
374
375 it('should require the specified file', function () {
376 const module = require('module')
377
378 const script = sinon.spy(function (robot) {})
379 this.sandbox.stub(module, '_load').returns(script)
380 this.sandbox.stub(this.robot, 'parseHelp')
381
382 this.robot.loadFile('./scripts', 'test-script.js')
383 expect(module._load).to.have.been.calledWith('scripts/test-script')
384 })
385
386 describe('proper script', function () {
387 beforeEach(function () {
388 const module = require('module')
389
390 this.script = sinon.spy(function (robot) {})
391 this.sandbox.stub(module, '_load').returns(this.script)
392 this.sandbox.stub(this.robot, 'parseHelp')
393 })
394
395 it('should call the script with the Robot', function () {
396 this.robot.loadFile('./scripts', 'test-script.js')
397 expect(this.script).to.have.been.calledWith(this.robot)
398 })
399
400 it('should parse the script documentation', function () {
401 this.robot.loadFile('./scripts', 'test-script.js')
402 expect(this.robot.parseHelp).to.have.been.calledWith('scripts/test-script.js')
403 })
404 })
405
406 describe('non-Function script', function () {
407 beforeEach(function () {
408 const module = require('module')
409
410 this.script = {}
411 this.sandbox.stub(module, '_load').returns(this.script)
412 this.sandbox.stub(this.robot, 'parseHelp')
413 })
414
415 it('logs a warning', function () {
416 sinon.stub(this.robot.logger, 'warning')
417 this.robot.loadFile('./scripts', 'test-script.js')
418 expect(this.robot.logger.warning).to.have.been.called
419 })
420 })
421
422 describe('unsupported file extension', function () {
423 beforeEach(function () {
424 const module = require('module')
425
426 this.script = sinon.spy(function (robot) {})
427 this.sandbox.stub(module, '_load').returns(this.script)
428 this.sandbox.stub(this.robot, 'parseHelp')
429 })
430
431 it('should not be loaded by the Robot', function () {
432 this.robot.loadFile('./scripts', 'unsupported.yml')
433 expect(this.script).to.not.have.been.calledWith(this.robot)
434 })
435 })
436 })
437 })
438
439 describe('Listener Registration', function () {
440 describe('#listen', () =>
441 it('forwards the matcher, options, and callback to Listener', function () {
442 const callback = sinon.spy()
443 const matcher = sinon.spy()
444 const options = {}
445
446 this.robot.listen(matcher, options, callback)
447 const testListener = this.robot.listeners[0]
448
449 expect(testListener.matcher).to.equal(matcher)
450 expect(testListener.callback).to.equal(callback)
451 expect(testListener.options).to.equal(options)
452 })
453 )
454
455 describe('#hear', function () {
456 it('matches TextMessages', function () {
457 const callback = sinon.spy()
458 const testMessage = new TextMessage(this.user, 'message123')
459 const testRegex = /^message123$/
460
461 this.robot.hear(testRegex, callback)
462 const testListener = this.robot.listeners[0]
463 const result = testListener.matcher(testMessage)
464
465 expect(result).to.be.ok
466 })
467
468 it('does not match EnterMessages', function () {
469 const callback = sinon.spy()
470 const testMessage = new EnterMessage(this.user)
471 const testRegex = /.*/
472
473 this.robot.hear(testRegex, callback)
474 const testListener = this.robot.listeners[0]
475 const result = testListener.matcher(testMessage)
476
477 expect(result).to.not.be.ok
478 })
479 })
480
481 describe('#respond', function () {
482 it('matches TextMessages addressed to the robot', function () {
483 const callback = sinon.spy()
484 const testMessage = new TextMessage(this.user, 'TestHubot message123')
485 const testRegex = /message123$/
486
487 this.robot.respond(testRegex, callback)
488 const testListener = this.robot.listeners[0]
489 const result = testListener.matcher(testMessage)
490
491 expect(result).to.be.ok
492 })
493
494 it('does not match EnterMessages', function () {
495 const callback = sinon.spy()
496 const testMessage = new EnterMessage(this.user)
497 const testRegex = /.*/
498
499 this.robot.respond(testRegex, callback)
500 const testListener = this.robot.listeners[0]
501 const result = testListener.matcher(testMessage)
502
503 expect(result).to.not.be.ok
504 })
505 })
506
507 describe('#enter', function () {
508 it('matches EnterMessages', function () {
509 const callback = sinon.spy()
510 const testMessage = new EnterMessage(this.user)
511
512 this.robot.enter(callback)
513 const testListener = this.robot.listeners[0]
514 const result = testListener.matcher(testMessage)
515
516 expect(result).to.be.ok
517 })
518
519 it('does not match TextMessages', function () {
520 const callback = sinon.spy()
521 const testMessage = new TextMessage(this.user, 'message123')
522
523 this.robot.enter(callback)
524 const testListener = this.robot.listeners[0]
525 const result = testListener.matcher(testMessage)
526
527 expect(result).to.not.be.ok
528 })
529 })
530
531 describe('#leave', function () {
532 it('matches LeaveMessages', function () {
533 const callback = sinon.spy()
534 const testMessage = new LeaveMessage(this.user)
535
536 this.robot.leave(callback)
537 const testListener = this.robot.listeners[0]
538 const result = testListener.matcher(testMessage)
539
540 expect(result).to.be.ok
541 })
542
543 it('does not match TextMessages', function () {
544 const callback = sinon.spy()
545 const testMessage = new TextMessage(this.user, 'message123')
546
547 this.robot.leave(callback)
548 const testListener = this.robot.listeners[0]
549 const result = testListener.matcher(testMessage)
550
551 expect(result).to.not.be.ok
552 })
553 })
554
555 describe('#topic', function () {
556 it('matches TopicMessages', function () {
557 const callback = sinon.spy()
558 const testMessage = new TopicMessage(this.user)
559
560 this.robot.topic(callback)
561 const testListener = this.robot.listeners[0]
562 const result = testListener.matcher(testMessage)
563
564 expect(result).to.be.ok
565 })
566
567 it('does not match TextMessages', function () {
568 const callback = sinon.spy()
569 const testMessage = new TextMessage(this.user, 'message123')
570
571 this.robot.topic(callback)
572 const testListener = this.robot.listeners[0]
573 const result = testListener.matcher(testMessage)
574
575 expect(result).to.not.be.ok
576 })
577 })
578
579 describe('#catchAll', function () {
580 it('matches CatchAllMessages', function () {
581 const callback = sinon.spy()
582 const testMessage = new CatchAllMessage(new TextMessage(this.user, 'message123'))
583
584 this.robot.catchAll(callback)
585 const testListener = this.robot.listeners[0]
586 const result = testListener.matcher(testMessage)
587
588 expect(result).to.be.ok
589 })
590
591 it('does not match TextMessages', function () {
592 const callback = sinon.spy()
593 const testMessage = new TextMessage(this.user, 'message123')
594
595 this.robot.catchAll(callback)
596 const testListener = this.robot.listeners[0]
597 const result = testListener.matcher(testMessage)
598
599 expect(result).to.not.be.ok
600 })
601 })
602 })
603
604 describe('Message Processing', function () {
605 it('calls a matching listener', function (done) {
606 const testMessage = new TextMessage(this.user, 'message123')
607 this.robot.hear(/^message123$/, function (response) {
608 expect(response.message).to.equal(testMessage)
609 done()
610 })
611 this.robot.receive(testMessage)
612 })
613
614 it('calls multiple matching listeners', function (done) {
615 const testMessage = new TextMessage(this.user, 'message123')
616
617 let listenersCalled = 0
618 const listenerCallback = function (response) {
619 expect(response.message).to.equal(testMessage)
620 listenersCalled++
621 }
622
623 this.robot.hear(/^message123$/, listenerCallback)
624 this.robot.hear(/^message123$/, listenerCallback)
625
626 this.robot.receive(testMessage, function () {
627 expect(listenersCalled).to.equal(2)
628 done()
629 })
630 })
631
632 it('calls the catch-all listener if no listeners match', function (done) {
633 const testMessage = new TextMessage(this.user, 'message123')
634
635 const listenerCallback = sinon.spy()
636 this.robot.hear(/^no-matches$/, listenerCallback)
637
638 this.robot.catchAll(function (response) {
639 expect(listenerCallback).to.not.have.been.called
640 expect(response.message).to.equal(testMessage)
641 done()
642 })
643
644 this.robot.receive(testMessage)
645 })
646
647 it('does not call the catch-all listener if any listener matched', function (done) {
648 const testMessage = new TextMessage(this.user, 'message123')
649
650 const listenerCallback = sinon.spy()
651 this.robot.hear(/^message123$/, listenerCallback)
652
653 const catchAllCallback = sinon.spy()
654 this.robot.catchAll(catchAllCallback)
655
656 this.robot.receive(testMessage, function () {
657 expect(listenerCallback).to.have.been.called.once
658 expect(catchAllCallback).to.not.have.been.called
659 done()
660 })
661 })
662
663 it('stops processing if message.finish() is called synchronously', function (done) {
664 const testMessage = new TextMessage(this.user, 'message123')
665
666 this.robot.hear(/^message123$/, response => response.message.finish())
667
668 const listenerCallback = sinon.spy()
669 this.robot.hear(/^message123$/, listenerCallback)
670
671 this.robot.receive(testMessage, function () {
672 expect(listenerCallback).to.not.have.been.called
673 done()
674 })
675 })
676
677 it('calls non-TextListener objects', function (done) {
678 const testMessage = new EnterMessage(this.user)
679
680 this.robot.enter(function (response) {
681 expect(response.message).to.equal(testMessage)
682 done()
683 })
684
685 this.robot.receive(testMessage)
686 })
687
688 it('gracefully handles listener uncaughtExceptions (move on to next listener)', function (done) {
689 const testMessage = new TextMessage(this.user, 'message123')
690 const theError = new Error()
691
692 this.robot.hear(/^message123$/, function () {
693 throw theError
694 })
695
696 let goodListenerCalled = false
697 this.robot.hear(/^message123$/, () => {
698 goodListenerCalled = true
699 })
700
701 this.robot.emit = function (name, err, response) {
702 expect(name).to.equal('error')
703 expect(err).to.equal(theError)
704 expect(response.message).to.equal(testMessage)
705 }
706 sinon.spy(this.robot, 'emit')
707
708 this.robot.receive(testMessage, () => {
709 expect(this.robot.emit).to.have.been.called
710 expect(goodListenerCalled).to.be.ok
711 done()
712 })
713 })
714
715 describe('Listener Middleware', function () {
716 it('allows listener callback execution', function (testDone) {
717 const listenerCallback = sinon.spy()
718 this.robot.hear(/^message123$/, listenerCallback)
719 this.robot.listenerMiddleware((context, next, done) =>
720 // Allow Listener callback execution
721 next(done)
722 )
723
724 const testMessage = new TextMessage(this.user, 'message123')
725 this.robot.receive(testMessage, function () {
726 expect(listenerCallback).to.have.been.called
727 testDone()
728 })
729 })
730
731 it('can block listener callback execution', function (testDone) {
732 const listenerCallback = sinon.spy()
733 this.robot.hear(/^message123$/, listenerCallback)
734 this.robot.listenerMiddleware((context, next, done) =>
735 // Block Listener callback execution
736 done()
737 )
738
739 const testMessage = new TextMessage(this.user, 'message123')
740 this.robot.receive(testMessage, function () {
741 expect(listenerCallback).to.not.have.been.called
742 testDone()
743 })
744 })
745
746 it('receives the correct arguments', function (testDone) {
747 this.robot.hear(/^message123$/, function () {})
748 const testListener = this.robot.listeners[0]
749 const testMessage = new TextMessage(this.user, 'message123')
750
751 this.robot.listenerMiddleware((context, next, done) => {
752 // Escape middleware error handling for clearer test failures
753 process.nextTick(() => {
754 expect(context.listener).to.equal(testListener)
755 expect(context.response.message).to.equal(testMessage)
756 expect(next).to.be.a('function')
757 expect(done).to.be.a('function')
758 testDone()
759 })
760 })
761
762 this.robot.receive(testMessage)
763 })
764
765 it('executes middleware in order of definition', function (testDone) {
766 const execution = []
767
768 const testMiddlewareA = function (context, next, done) {
769 execution.push('middlewareA')
770 next(function () {
771 execution.push('doneA')
772 done()
773 })
774 }
775
776 const testMiddlewareB = function (context, next, done) {
777 execution.push('middlewareB')
778 next(function () {
779 execution.push('doneB')
780 done()
781 })
782 }
783
784 this.robot.listenerMiddleware(testMiddlewareA)
785 this.robot.listenerMiddleware(testMiddlewareB)
786
787 this.robot.hear(/^message123$/, () => execution.push('listener'))
788
789 const testMessage = new TextMessage(this.user, 'message123')
790 this.robot.receive(testMessage, function () {
791 expect(execution).to.deep.equal([
792 'middlewareA',
793 'middlewareB',
794 'listener',
795 'doneB',
796 'doneA'
797 ])
798 testDone()
799 })
800 })
801 })
802
803 describe('Receive Middleware', function () {
804 it('fires for all messages, including non-matching ones', function (testDone) {
805 const middlewareSpy = sinon.spy()
806 const listenerCallback = sinon.spy()
807 this.robot.hear(/^message123$/, listenerCallback)
808 this.robot.receiveMiddleware(function (context, next, done) {
809 middlewareSpy()
810 next(done)
811 })
812
813 const testMessage = new TextMessage(this.user, 'not message 123')
814
815 this.robot.receive(testMessage, function () {
816 expect(listenerCallback).to.not.have.been.called
817 expect(middlewareSpy).to.have.been.called
818 testDone()
819 })
820 })
821
822 it('can block listener execution', function (testDone) {
823 const middlewareSpy = sinon.spy()
824 const listenerCallback = sinon.spy()
825 this.robot.hear(/^message123$/, listenerCallback)
826 this.robot.receiveMiddleware(function (context, next, done) {
827 // Block Listener callback execution
828 middlewareSpy()
829 done()
830 })
831
832 const testMessage = new TextMessage(this.user, 'message123')
833 this.robot.receive(testMessage, function () {
834 expect(listenerCallback).to.not.have.been.called
835 expect(middlewareSpy).to.have.been.called
836 testDone()
837 })
838 })
839
840 it('receives the correct arguments', function (testDone) {
841 this.robot.hear(/^message123$/, function () {})
842 const testMessage = new TextMessage(this.user, 'message123')
843
844 this.robot.receiveMiddleware(function (context, next, done) {
845 // Escape middleware error handling for clearer test failures
846 expect(context.response.message).to.equal(testMessage)
847 expect(next).to.be.a('function')
848 expect(done).to.be.a('function')
849 testDone()
850 next(done)
851 })
852
853 this.robot.receive(testMessage)
854 })
855
856 it('executes receive middleware in order of definition', function (testDone) {
857 const execution = []
858
859 const testMiddlewareA = function (context, next, done) {
860 execution.push('middlewareA')
861 next(function () {
862 execution.push('doneA')
863 done()
864 })
865 }
866
867 const testMiddlewareB = function (context, next, done) {
868 execution.push('middlewareB')
869 next(function () {
870 execution.push('doneB')
871 done()
872 })
873 }
874
875 this.robot.receiveMiddleware(testMiddlewareA)
876 this.robot.receiveMiddleware(testMiddlewareB)
877
878 this.robot.hear(/^message123$/, () => execution.push('listener'))
879
880 const testMessage = new TextMessage(this.user, 'message123')
881 this.robot.receive(testMessage, function () {
882 expect(execution).to.deep.equal([
883 'middlewareA',
884 'middlewareB',
885 'listener',
886 'doneB',
887 'doneA'
888 ])
889 testDone()
890 })
891 })
892
893 it('allows editing the message portion of the given response', function (testDone) {
894 const testMiddlewareA = function (context, next, done) {
895 context.response.message.text = 'foobar'
896 next()
897 }
898
899 const testMiddlewareB = function (context, next, done) {
900 // Subsequent middleware should see the modified message
901 expect(context.response.message.text).to.equal('foobar')
902 next()
903 }
904
905 this.robot.receiveMiddleware(testMiddlewareA)
906 this.robot.receiveMiddleware(testMiddlewareB)
907
908 const testCallback = sinon.spy()
909 // We'll never get to this if testMiddlewareA has not modified the message.
910 this.robot.hear(/^foobar$/, testCallback)
911
912 const testMessage = new TextMessage(this.user, 'message123')
913 this.robot.receive(testMessage, function () {
914 expect(testCallback).to.have.been.called
915 testDone()
916 })
917 })
918 })
919
920 describe('Response Middleware', function () {
921 it('executes response middleware in order', function (testDone) {
922 let sendSpy
923 this.robot.adapter.send = (sendSpy = sinon.spy())
924 this.robot.hear(/^message123$/, response => response.send('foobar, sir, foobar.'))
925
926 this.robot.responseMiddleware(function (context, next, done) {
927 context.strings[0] = context.strings[0].replace(/foobar/g, 'barfoo')
928 next()
929 })
930
931 this.robot.responseMiddleware(function (context, next, done) {
932 context.strings[0] = context.strings[0].replace(/barfoo/g, 'replaced bar-foo')
933 next()
934 })
935
936 const testMessage = new TextMessage(this.user, 'message123')
937 this.robot.receive(testMessage, function () {
938 expect(sendSpy.getCall(0).args[1]).to.equal('replaced bar-foo, sir, replaced bar-foo.')
939 testDone()
940 })
941 })
942
943 it('allows replacing outgoing strings', function (testDone) {
944 let sendSpy
945 this.robot.adapter.send = (sendSpy = sinon.spy())
946 this.robot.hear(/^message123$/, response => response.send('foobar, sir, foobar.'))
947
948 this.robot.responseMiddleware(function (context, next, done) {
949 context.strings = ['whatever I want.']
950 next()
951 })
952
953 const testMessage = new TextMessage(this.user, 'message123')
954 this.robot.receive(testMessage, function () {
955 expect(sendSpy.getCall(0).args[1]).to.deep.equal('whatever I want.')
956 testDone()
957 })
958 })
959
960 it('marks plaintext as plaintext', function (testDone) {
961 let sendSpy = sinon.spy()
962 this.robot.adapter.send = sendSpy
963 this.robot.hear(/^message123$/, response => response.send('foobar, sir, foobar.'))
964 this.robot.hear(/^message456$/, response => response.play('good luck with that'))
965
966 let method
967 let plaintext
968 this.robot.responseMiddleware(function (context, next, done) {
969 method = context.method
970 plaintext = context.plaintext
971 next(done)
972 })
973
974 const testMessage = new TextMessage(this.user, 'message123')
975
976 this.robot.receive(testMessage, () => {
977 expect(plaintext).to.equal(true)
978 expect(method).to.equal('send')
979 const testMessage2 = new TextMessage(this.user, 'message456')
980 this.robot.receive(testMessage2, function () {
981 expect(plaintext).to.equal(undefined)
982 expect(method).to.equal('play')
983 testDone()
984 })
985 })
986 })
987
988 it('does not send trailing functions to middleware', function (testDone) {
989 let sendSpy
990 this.robot.adapter.send = (sendSpy = sinon.spy())
991 let asserted = false
992 const postSendCallback = function () {}
993 this.robot.hear(/^message123$/, response => response.send('foobar, sir, foobar.', postSendCallback))
994
995 this.robot.responseMiddleware(function (context, next, done) {
996 // We don't send the callback function to middleware, so it's not here.
997 expect(context.strings).to.deep.equal(['foobar, sir, foobar.'])
998 expect(context.method).to.equal('send')
999 asserted = true
1000 next()
1001 })
1002
1003 const testMessage = new TextMessage(this.user, 'message123')
1004 this.robot.receive(testMessage, function () {
1005 expect(asserted).to.equal(true)
1006 expect(sendSpy.getCall(0).args[1]).to.equal('foobar, sir, foobar.')
1007 expect(sendSpy.getCall(0).args[2]).to.equal(postSendCallback)
1008 testDone()
1009 })
1010 })
1011 })
1012 })
1013})