iTerm coprocess reporting result of (Mocha) tests run via nodemon
See this gist:
#!/usr/bin/python | |
# -*- coding: utf-8 -*- | |
# Inspired by https://gist.github.com/ddbeck/1421861 | |
# About: This coprocess produces an OSX notification with results of Mocha tests run via nodemon | |
# # Installation: | |
# 1. Make this script executable | |
# 2. Register it: iTerm - Profiles - Open Profiles... - [Edit Profiles...] - <select your profile> - | |
# - Advanced - Triggers - [Edit] - add a new trigger: | |
# Regular Expression: [nodemon] starting `npm .*(run )?test.*` | |
# Action: Run Coprocess | |
# Parameters: /path/to/iterm-notify-test-failure.sh | |
# 3. Troubleshooting tips: | |
# * Verify the script can be run from the command line | |
# * Instead waiting for Mocha to run, use e.g. `echo "npm test"` to trigger it | |
# * Print into a file to produce debugging log or/and use e.g. the `say` osx command to track what is being processed | |
import subprocess | |
import sys | |
import re | |
## Example input from a Mocha run: | |
# 28 Apr 10:12:26 - [nodemon] starting `npm --silent run test:server` | |
# ... | |
# CacheItem fetching from a source | |
# ✓ should return the in-progress-call promise if expired and requireNonExpired set | |
# 1) should refresh its value even though valid when forceRefresh=true and return it | |
# lookup | |
# ✓ should return a promise | |
# | |
# | |
# 2 passing (12ms) | |
# 1 failing | |
# | |
# 1) CacheItem fetching from a source should refresh its value even though valid when forceRefresh=true and return it: | |
# | |
# AssertionError: expected 2 to deeply equal 23 | |
# + expected - actual | |
# ... | |
# 28 Apr 10:12:36 - [nodemon] clean exit - waiting for changes before restart | |
def incoming(): | |
while True: | |
yield raw_input() | |
def notify(title_end = 'FAILED', msg = ''): | |
subprocess.call([ | |
'osascript', | |
"-e", | |
('display notification "%s" with title "Tests %s"' % | |
(msg, title_end)) | |
]) | |
def main(): | |
title_end = 'FAILED: ' | |
msg = '' | |
failingCountPattern = re.compile('.*(\d+) failing') | |
# Failed test name example: ' [2K[0G [31m 1) Stack should do something [0m' | |
# (the [2k etc. are Bash color escape/sequences, the ' ' is actually the control char \033) | |
failedTestPattern = re.compile('(\033\\[\w+| \\W)+(\\d+\\) .*)\033\\[\w+') # TODO The end color seq. is not ignored | |
for line in incoming(): | |
failingCountMatch = failingCountPattern.match(line) | |
failedTestMatch = failedTestPattern.match(line) | |
if failingCountMatch: # ex: " 1 failing" | |
title_end += failingCountMatch.group(1) | |
if failedTestPattern.match(line): | |
msg += failedTestMatch.group(2) + ': ' # Notifications display only one line so just the 1st error is displayed | |
if 'AssertionError' in line: | |
msg += line + '\n' | |
if '[nodemon] app crashed' in line: | |
notify(title_end = title_end, msg = msg) | |
sys.exit(0) | |
if '[nodemon] clean exit' in line: | |
notify("OK"); | |
sys.exit(0) | |
if __name__ == '__main__': | |
main() |