hubot-rainforest
Version:
Hubot plugin that interfaces with Rainforest (https://www.rainforestqa.com/)
154 lines (124 loc) • 5.51 kB
text/coffeescript
# Description:
# Interfaces with Rainforest QA to report status
#
# Commands:
# hubot rf subscribe - Subscribe the current channel to test-run status updates
# hubot rf unsubscribe - Remove this room from the subscription list
# hubot rf status - Query the last 5 test runs
# hubot rf subscriptions - List the rooms that are subscribed to status updates
# hubot rf tags - List the tags currently in use
# hubot rf run (tag) [, tag, ...] - Run a tagged set of tests
TREES = ':palm_tree::palm_tree:'
rainforestToken = process.env.HUBOT_RAINFOREST_TOKEN
# Don't report transitions into these states to the chat rooms
ignored_states = [
'queued',
'in_progress',
]
module.exports = (robot) ->
unless rainforestToken?
console.log "Specify HUBOT_RAINFOREST_TOKEN to enable Rainforest commands"
return
sendToRooms = (msg) ->
for room of robot.brain.data.rainforestRooms
robot.messageRoom room, msg
compareByCreationDateReversed = (a,b) ->
return a.created_at - b.created_at
decorateState = (state, result) ->
switch state.toLowerCase()
when 'complete' then "#{result} " + switch result
when 'passed' then ":white_check_mark:"
when 'failed' then ":x:"
when 'aborted' then "(aborted)"
else state
runReport = (run) ->
state = switch run.state.toLowerCase()
when 'complete' then "#{run.result} " + switch run.result
when 'passed' then ":white_check_mark:"
when 'failed' then ":x:"
when 'aborted' then "(aborted)"
else run.state
"#{TREES} Run is *#{state}*\n" +
("\t#{x}" for x in run.sample_test_titles).join('\n') +
"\n\thttps://app.rainforestqa.com/runs/#{run.id}/tests"
app = new RainforestApp robot
testNames = {}
refreshTestNames = ->
app.getAllTests (tests) ->
for test in tests
testNames[test.id] = test.title
updateStateAndReport = ->
app.getRuns 10, (results) ->
return unless results?
oldruns = robot.brain.data.rainforestRuns
for run in results
oldrunstate = oldruns[run.id]
if oldrunstate != run.state and run.state not in ignored_states
sendToRooms runReport(run)
robot.brain.data.rainforestRuns[run.id] = run.state
robot.brain.data.rainforest ?= {}
robot.brain.data.rainforestRooms ?= {}
robot.brain.data.rainforestRuns ?= {}
# Refresh test names every 5 minutes
setInterval refreshTestNames, 5*60*1000
refreshTestNames()
# Check the feed every 10 seconds
setInterval updateStateAndReport, 10*1000
updateStateAndReport()
robot.respond /rf subscriptions/i, (msg) ->
rooms = [k for k of robot.brain.data.rainforestRooms]
msg.send "#{TREES} Rooms receiving Rainforest updates: [#{rooms.join ', '}]"
robot.respond /rf subscribe/i, (msg) ->
msg.send "#{TREES} Subscribing #{msg.envelope.room}"
robot.brain.data.rainforestRooms[msg.envelope.room] = true
robot.respond /rf unsubscribe/i, (msg) ->
msg.send "#{TREES} Unsubscribing #{msg.envelope.room}"
delete robot.brain.data.rainforestRooms[msg.envelope.room]
robot.respond /rf stat(us)?/i, (msg) ->
app.getRuns 5, (res) ->
for run in res
msg.send runReport(run)
, msg
robot.respond /rf tags/, (msg) ->
app.getAllTags (json) ->
msg.send "#{TREES} " + (
"#{t.name} (#{t.count} test#{if t.count!=1 then 's' else ''})" for t in json
).join ' '
, msg
robot.respond /rf run (.*)/, (msg) ->
tags = (x.trim() for x in msg.match[1].split(','))
unless tags?
return msg.reply "Please specify a tag. I won't run _all_ the tests for you."
app.startRun tags, (resp) ->
if resp.error
msg.reply "Rainforest error for input [#{tags}]: #{resp.error}"
msg.send "#{TREES} https://app.rainforestqa.com/runs/#{resp.id}/tests"
, msg
class RainforestApp
constructor: (@robot) ->
requester: (endpoint) ->
@robot.http("https://app.rainforestqa.com/api/1/#{endpoint}")
.header('Accept', 'application/json')
.header('Content-type', 'application/json')
.header('CLIENT_TOKEN', rainforestToken)
get: (endpoint, msg, callback) ->
@requester(endpoint).get() (err, res, body) =>
try
json = JSON.parse body
catch error
msg.reply "Rainforest error: #{error} / #{res} / #{body}" if msg?
callback json
post: (endpoint, data, msg, callback) =>
unless typeof data == "string"
data = JSON.stringify data
@requester(endpoint).post(data) (err, res, body) =>
try
json = JSON.parse body
catch error
msg.reply "Rainforest error: #{error} / #{res} / #{body}" if msg?
callback json
getAllTags: (callback, msg) -> @get 'tests/tags', msg, callback
getAllTests: (callback, msg) -> @get 'tests?page_size=1000', msg, callback
getRuns: (n, callback, msg) -> @get "runs?page_size=#{n}", msg, callback
# tags is an array of strings
startRun: (tags, callback, msg) -> @post 'runs', {tags: tags}, msg, callback