hubot 3.3.1 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +2 -0
- package/.envrc +5 -0
- package/.github/workflows/nodejs-macos.yml +26 -0
- package/.github/workflows/nodejs-ubuntu.yml +28 -0
- package/.github/workflows/nodejs-windows.yml +26 -0
- package/README.md +0 -2
- package/bin/hubot.js +1 -5
- package/docs/deploying/windows.md +5 -5
- package/docs/patterns.md +14 -0
- package/es2015.js +1 -1
- package/package.json +7 -7
- package/src/adapters/campfire.js +18 -18
- package/src/adapters/shell.js +7 -5
- package/src/brain.js +8 -8
- package/src/datastore.js +3 -3
- package/src/httpclient.js +312 -0
- package/src/robot.js +18 -15
- package/src/user.js +2 -2
- package/test/adapter_test.js +1 -1
- package/test/brain_test.js +9 -9
- package/test/datastore_test.js +16 -16
- package/test/es2015_test.js +1 -1
- package/test/fixtures/mock-adapter.js +5 -0
- package/test/listener_test.js +2 -2
- package/test/middleware_test.js +18 -17
- package/test/robot_test.js +26 -11
- package/test/user_test.js +2 -2
- package/ROADMAP.md +0 -37
package/test/middleware_test.js
CHANGED
|
@@ -23,7 +23,7 @@ describe('Middleware', function () {
|
|
|
23
23
|
describe('Unit Tests', function () {
|
|
24
24
|
beforeEach(function () {
|
|
25
25
|
// Stub out event emitting
|
|
26
|
-
this.robot = {emit: sinon.spy()}
|
|
26
|
+
this.robot = { emit: sinon.spy() }
|
|
27
27
|
|
|
28
28
|
this.middleware = new Middleware(this.robot)
|
|
29
29
|
})
|
|
@@ -284,7 +284,7 @@ describe('Middleware', function () {
|
|
|
284
284
|
}
|
|
285
285
|
|
|
286
286
|
this.middleware.execute(
|
|
287
|
-
{response: testResponse},
|
|
287
|
+
{ response: testResponse },
|
|
288
288
|
middlewareFinished,
|
|
289
289
|
middlewareFailed
|
|
290
290
|
)
|
|
@@ -351,6 +351,7 @@ describe('Middleware', function () {
|
|
|
351
351
|
warnOnUnregistered: false
|
|
352
352
|
})
|
|
353
353
|
mockery.registerMock('hubot-mock-adapter', require('./fixtures/mock-adapter'))
|
|
354
|
+
process.env.EXPRESS_PORT = 0
|
|
354
355
|
this.robot = new Robot(null, 'mock-adapter', true, 'TestHubot')
|
|
355
356
|
this.robot.run
|
|
356
357
|
|
|
@@ -394,8 +395,8 @@ describe('Middleware', function () {
|
|
|
394
395
|
expect(this.middleware).to.have.been.calledWithMatch(
|
|
395
396
|
sinon.match.has('listener',
|
|
396
397
|
sinon.match.same(this.testListener)), // context
|
|
397
|
-
sinon.match.any,
|
|
398
|
-
sinon.match.any
|
|
398
|
+
sinon.match.any, // next
|
|
399
|
+
sinon.match.any // done
|
|
399
400
|
)
|
|
400
401
|
testDone()
|
|
401
402
|
})
|
|
@@ -406,9 +407,9 @@ describe('Middleware', function () {
|
|
|
406
407
|
expect(this.middleware).to.have.been.calledWithMatch(
|
|
407
408
|
sinon.match.has('listener',
|
|
408
409
|
sinon.match.has('options',
|
|
409
|
-
sinon.match.has('id'))),
|
|
410
|
-
sinon.match.any,
|
|
411
|
-
sinon.match.any
|
|
410
|
+
sinon.match.has('id'))), // context
|
|
411
|
+
sinon.match.any, // next
|
|
412
|
+
sinon.match.any // done
|
|
412
413
|
)
|
|
413
414
|
testDone()
|
|
414
415
|
})
|
|
@@ -423,8 +424,8 @@ describe('Middleware', function () {
|
|
|
423
424
|
sinon.match.instanceOf(Response).and(
|
|
424
425
|
sinon.match.has('message',
|
|
425
426
|
sinon.match.same(this.testMessage)))), // context
|
|
426
|
-
sinon.match.any,
|
|
427
|
-
sinon.match.any
|
|
427
|
+
sinon.match.any, // next
|
|
428
|
+
sinon.match.any // done
|
|
428
429
|
)
|
|
429
430
|
testDone()
|
|
430
431
|
})
|
|
@@ -447,8 +448,8 @@ describe('Middleware', function () {
|
|
|
447
448
|
sinon.match.instanceOf(Response).and(
|
|
448
449
|
sinon.match.has('message',
|
|
449
450
|
sinon.match.same(this.testMessage)))), // context
|
|
450
|
-
sinon.match.any,
|
|
451
|
-
sinon.match.any
|
|
451
|
+
sinon.match.any, // next
|
|
452
|
+
sinon.match.any // done
|
|
452
453
|
)
|
|
453
454
|
testDone()
|
|
454
455
|
})
|
|
@@ -466,11 +467,11 @@ describe('Middleware', function () {
|
|
|
466
467
|
it('is a function with arity one', function (testDone) {
|
|
467
468
|
this.robot.receive(this.testMessage, () => {
|
|
468
469
|
expect(this.middleware).to.have.been.calledWithMatch(
|
|
469
|
-
sinon.match.any,
|
|
470
|
+
sinon.match.any, // context
|
|
470
471
|
sinon.match.func.and(
|
|
471
472
|
sinon.match.has('length',
|
|
472
|
-
sinon.match(1))),
|
|
473
|
-
sinon.match.any
|
|
473
|
+
sinon.match(1))), // next
|
|
474
|
+
sinon.match.any // done
|
|
474
475
|
)
|
|
475
476
|
testDone()
|
|
476
477
|
})
|
|
@@ -487,11 +488,11 @@ describe('Middleware', function () {
|
|
|
487
488
|
it('is a function with arity zero', function (testDone) {
|
|
488
489
|
this.robot.receive(this.testMessage, () => {
|
|
489
490
|
expect(this.middleware).to.have.been.calledWithMatch(
|
|
490
|
-
sinon.match.any,
|
|
491
|
-
sinon.match.any,
|
|
491
|
+
sinon.match.any, // context
|
|
492
|
+
sinon.match.any, // next
|
|
492
493
|
sinon.match.func.and(
|
|
493
494
|
sinon.match.has('length',
|
|
494
|
-
sinon.match(0)))
|
|
495
|
+
sinon.match(0))) // done
|
|
495
496
|
)
|
|
496
497
|
testDone()
|
|
497
498
|
})
|
package/test/robot_test.js
CHANGED
|
@@ -20,6 +20,7 @@ const TopicMessage = require('../src/message').TopicMessage
|
|
|
20
20
|
|
|
21
21
|
// mock `hubot-mock-adapter` module from fixture
|
|
22
22
|
const mockery = require('mockery')
|
|
23
|
+
const path = require('path')
|
|
23
24
|
|
|
24
25
|
describe('Robot', function () {
|
|
25
26
|
beforeEach(function () {
|
|
@@ -28,14 +29,14 @@ describe('Robot', function () {
|
|
|
28
29
|
warnOnUnregistered: false
|
|
29
30
|
})
|
|
30
31
|
mockery.registerMock('hubot-mock-adapter', require('./fixtures/mock-adapter'))
|
|
32
|
+
process.env.EXPRESS_PORT = 0
|
|
31
33
|
this.robot = new Robot(null, 'mock-adapter', true, 'TestHubot')
|
|
32
34
|
this.robot.alias = 'Hubot'
|
|
33
35
|
this.robot.run()
|
|
34
36
|
|
|
35
37
|
// Re-throw AssertionErrors for clearer test failures
|
|
36
38
|
this.robot.on('error', function (name, err, response) {
|
|
37
|
-
if (
|
|
38
|
-
if (err.constructor.name === 'AssertionError') {
|
|
39
|
+
if (err?.constructor.name === 'AssertionError' || name instanceof chai.AssertionError) {
|
|
39
40
|
process.nextTick(function () {
|
|
40
41
|
throw err
|
|
41
42
|
})
|
|
@@ -69,7 +70,7 @@ describe('Robot', function () {
|
|
|
69
70
|
|
|
70
71
|
it('passes options through to the ScopedHttpClient', function () {
|
|
71
72
|
const agent = {}
|
|
72
|
-
const httpClient = this.robot.http('http://localhost', {agent})
|
|
73
|
+
const httpClient = this.robot.http('http://localhost', { agent })
|
|
73
74
|
expect(httpClient.options.agent).to.equal(agent)
|
|
74
75
|
})
|
|
75
76
|
|
|
@@ -79,7 +80,7 @@ describe('Robot', function () {
|
|
|
79
80
|
|
|
80
81
|
it('merges in any global http options', function () {
|
|
81
82
|
const agent = {}
|
|
82
|
-
this.robot.globalHttpOptions = {agent}
|
|
83
|
+
this.robot.globalHttpOptions = { agent }
|
|
83
84
|
const httpClient = this.robot.http('http://localhost')
|
|
84
85
|
expect(httpClient.options.agent).to.equal(agent)
|
|
85
86
|
})
|
|
@@ -87,10 +88,18 @@ describe('Robot', function () {
|
|
|
87
88
|
it('local options override global http options', function () {
|
|
88
89
|
const agentA = {}
|
|
89
90
|
const agentB = {}
|
|
90
|
-
this.robot.globalHttpOptions = {agent: agentA}
|
|
91
|
-
const httpClient = this.robot.http('http://localhost', {agent: agentB})
|
|
91
|
+
this.robot.globalHttpOptions = { agent: agentA }
|
|
92
|
+
const httpClient = this.robot.http('http://localhost', { agent: agentB })
|
|
92
93
|
expect(httpClient.options.agent).to.equal(agentB)
|
|
93
94
|
})
|
|
95
|
+
|
|
96
|
+
it('builds the url correctly from a string', function () {
|
|
97
|
+
const options = this.httpClient.buildOptions('http://localhost:3001')
|
|
98
|
+
expect(options.host).to.equal('localhost:3001')
|
|
99
|
+
expect(options.pathname).to.equal('/')
|
|
100
|
+
expect(options.protocol).to.equal('http:')
|
|
101
|
+
expect(options.port).to.equal('3001')
|
|
102
|
+
})
|
|
94
103
|
})
|
|
95
104
|
|
|
96
105
|
describe('#respondPattern', function () {
|
|
@@ -302,7 +311,7 @@ describe('Robot', function () {
|
|
|
302
311
|
}
|
|
303
312
|
|
|
304
313
|
const listenerSpy =
|
|
305
|
-
{call: sinon.spy()}
|
|
314
|
+
{ call: sinon.spy() }
|
|
306
315
|
|
|
307
316
|
this.robot.listeners = [
|
|
308
317
|
matchingListener,
|
|
@@ -380,7 +389,7 @@ describe('Robot', function () {
|
|
|
380
389
|
this.sandbox.stub(this.robot, 'parseHelp')
|
|
381
390
|
|
|
382
391
|
this.robot.loadFile('./scripts', 'test-script.js')
|
|
383
|
-
expect(module._load).to.have.been.calledWith('scripts
|
|
392
|
+
expect(module._load).to.have.been.calledWith(path.join('scripts', 'test-script'))
|
|
384
393
|
})
|
|
385
394
|
|
|
386
395
|
describe('proper script', function () {
|
|
@@ -399,7 +408,7 @@ describe('Robot', function () {
|
|
|
399
408
|
|
|
400
409
|
it('should parse the script documentation', function () {
|
|
401
410
|
this.robot.loadFile('./scripts', 'test-script.js')
|
|
402
|
-
expect(this.robot.parseHelp).to.have.been.calledWith('scripts
|
|
411
|
+
expect(this.robot.parseHelp).to.have.been.calledWith(path.join('scripts', 'test-script.js'))
|
|
403
412
|
})
|
|
404
413
|
})
|
|
405
414
|
|
|
@@ -412,11 +421,17 @@ describe('Robot', function () {
|
|
|
412
421
|
this.sandbox.stub(this.robot, 'parseHelp')
|
|
413
422
|
})
|
|
414
423
|
|
|
415
|
-
it('logs a warning', function () {
|
|
424
|
+
it('logs a warning for a .js file', function () {
|
|
416
425
|
sinon.stub(this.robot.logger, 'warning')
|
|
417
426
|
this.robot.loadFile('./scripts', 'test-script.js')
|
|
418
427
|
expect(this.robot.logger.warning).to.have.been.called
|
|
419
428
|
})
|
|
429
|
+
|
|
430
|
+
it('logs a warning for a .mjs file', function () {
|
|
431
|
+
sinon.stub(this.robot.logger, 'warning')
|
|
432
|
+
this.robot.loadFile('./scripts', 'test-script.mjs')
|
|
433
|
+
expect(this.robot.logger.warning).to.have.been.called
|
|
434
|
+
})
|
|
420
435
|
})
|
|
421
436
|
|
|
422
437
|
describe('unsupported file extension', function () {
|
|
@@ -1013,7 +1028,7 @@ describe('Robot', function () {
|
|
|
1013
1028
|
})
|
|
1014
1029
|
|
|
1015
1030
|
it('marks plaintext as plaintext', function (testDone) {
|
|
1016
|
-
|
|
1031
|
+
const sendSpy = sinon.spy()
|
|
1017
1032
|
this.robot.adapter.send = sendSpy
|
|
1018
1033
|
this.robot.hear(/^message123$/, response => response.send('foobar, sir, foobar.'))
|
|
1019
1034
|
this.robot.hear(/^message456$/, response => response.play('good luck with that'))
|
package/test/user_test.js
CHANGED
|
@@ -14,14 +14,14 @@ describe('User', () =>
|
|
|
14
14
|
})
|
|
15
15
|
|
|
16
16
|
it('sets attributes passed in', function () {
|
|
17
|
-
const user = new User('hubot', {foo: 1, bar: 2})
|
|
17
|
+
const user = new User('hubot', { foo: 1, bar: 2 })
|
|
18
18
|
|
|
19
19
|
expect(user.foo).to.equal(1)
|
|
20
20
|
expect(user.bar).to.equal(2)
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
it('uses name attribute when passed in, not id', function () {
|
|
24
|
-
const user = new User('hubot', {name: 'tobuh'})
|
|
24
|
+
const user = new User('hubot', { name: 'tobuh' })
|
|
25
25
|
|
|
26
26
|
expect(user.name).to.equal('tobuh')
|
|
27
27
|
})
|
package/ROADMAP.md
DELETED
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# Hubot Roadmap
|
|
2
|
-
|
|
3
|
-
Hubot v3 aims to be a bot framework optimized for developers and developer workflows, with great integration with the most popular chat clients and developer tools, and an active community that is sharing scripts and best practices.
|
|
4
|
-
|
|
5
|
-
This roadmap represents some of priorities for us over the next couple months. Issues or pull requests will be opened to discuss each of these items as they progress.
|
|
6
|
-
|
|
7
|
-
## 1. Return to a “maintained” status
|
|
8
|
-
|
|
9
|
-
- [x] Create a Hubot core team with at least 2 GitHub employees and at least 1 community member. The core team is [@technicalpickles](https://github.com/technicalpickles), [@bkeepers](https://github.com/bkeepers), [@mose](https://github.com/mose), and [@gr2m](https://github.com/gr2m) ([#1323](https://github.com/github/hubot/pull/1323))
|
|
10
|
-
- [ ] Document all maintainer processes (triage, release, etc.)
|
|
11
|
-
- [ ] Create an issue template that addresses common requests
|
|
12
|
-
- [ ] Configure automation ([probot stale](https://github.com/probot/stale), [Greenkeeper](https://greenkeeper.io/), [semantic-release](https://github.com/semantic-release/semantic-release))
|
|
13
|
-
- [ ] Review all [open PRs](https://github.com/github/hubot/pulls) and triage [open issues](https://github.com/github/hubot/issues)
|
|
14
|
-
- [ ] Establish a release process and regular release cadence of the first Tuesday of every month.
|
|
15
|
-
- [ ] Establish a first-responder rotation, which will aim to reduce the average time to first response on all new Issues and PRs to 48 hours.
|
|
16
|
-
|
|
17
|
-
## 2. Modernize the community
|
|
18
|
-
|
|
19
|
-
- [ ] Consolidate all officially supported Hubot projects into a single GitHub organization. This will include github/hubot and a handful of supported scripts, but will not include all community scripts in https://github.com/hubot-scripts ([#1327](https://github.com/github/hubot/issues/1327))
|
|
20
|
-
- [ ] Create a community forum to provide a place for people to ask questions, get help, and share best practices. [Discourse](https://www.discourse.org/) is the obvious choice here.
|
|
21
|
-
- [x] Choose a chat platform for maintainers and contributors, and post notices in various existing places (#hubot on freenode, github/hubot on Gitter). Slack is the obvious choice here. [Join us on Slack](https://hubot-slackin.herokuapp.com/).
|
|
22
|
-
- [x] Add a code of conduct based on http://contributor-covenant.org/ and processes to enforce it in all official spaces. ([#1334](https://github.com/github/hubot/pull/1334))
|
|
23
|
-
- [ ] Publish weekly community updates (blog, newsletter, etc) which highlight recent and upcoming changes, give shoutouts to contributors / maintainers, and maybe mention interesting uses of Hubot
|
|
24
|
-
- [x] Create Hubot Evolution—inspired by [Swift Evolution](https://github.com/apple/swift-evolution)—for proposing user-visible enhancements. ([hubotio/evolution#1](https://github.com/hubotio/evolution/pull/1))
|
|
25
|
-
|
|
26
|
-
## 3. Modernize the project
|
|
27
|
-
|
|
28
|
-
Each of these proposals will go through the [Hubot Evolution](https://github.com/hubotio/evolution) process.
|
|
29
|
-
|
|
30
|
-
- Translate from CoffeeScript to JavaScript and update to modern versions of Node.js and NPM (or Yarn)
|
|
31
|
-
- Revisit new bot generator (yeoman has a ton of dependencies, some of which can be error prone on windows)
|
|
32
|
-
- Support for running multiple adapters and archetypes (chat, deployment, CI, github, etc)
|
|
33
|
-
- Merge with [@probot](https://github.com/probot) and build out first class GitHub integration.
|
|
34
|
-
- Introduce "Commands”, an explicit interface for registering commands (like Slack’s slash commands) as an alternative to regular expressions
|
|
35
|
-
- Publish a ChatOps RPC spec and implement support for Hubot acting as both a client and a server.
|
|
36
|
-
- Support rich messages and interactions on platforms that support it
|
|
37
|
-
- Publish a public script directory backed by NPM
|