forked from mochajs/mocha
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(Growl): Check notification support _before_ use!
Added (minorly brittle) means to verify prerequisite software is installed. Migrated Growl support code to its own file. This implementation's checks are required to enable Growl; failure will write notice to `stderr`. Other modifications based on discussion from Chad Rickman's PR mochajs#3311. This also checks for errors from Growl callback. Fixes mochajs#3111 Signed-off-by: Paul Roebuck <plroebuck@users.noreply.github.com>
- Loading branch information
Showing
3 changed files
with
211 additions
and
45 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
'use strict'; | ||
/** | ||
* @module Growl | ||
*/ | ||
|
||
/** | ||
* Module dependencies. | ||
*/ | ||
var fs = require('fs'); | ||
var os = require('os'); | ||
var path = require('path'); | ||
|
||
/** | ||
* @summary | ||
* Checks if Growl support seems likely. | ||
* | ||
* @description | ||
* Glosses over the distinction between an unsupported platform | ||
* and one that lacks prerequisite software installations. | ||
* Autofails when run in browser. | ||
* | ||
* @public | ||
* @see {@link https://github.com/tj/node-growl/blob/master/README.md|Prerequisite Installs} | ||
* @see {@link Mocha#growl} | ||
* @return {boolean} whether Growl support can be expected | ||
*/ | ||
exports.isCapable = function() { | ||
return !process.browser && which(getSupportBinaries()) !== ''; | ||
}; | ||
|
||
/** | ||
* Implements desktop notifications as a pseudo-reporter. | ||
* | ||
* @private | ||
* @see {@link Mocha#_growl} | ||
* @param {Object} runner - Runner instance. | ||
*/ | ||
exports.notify = function(runner) { | ||
var sendNotification = function() { | ||
var growl = require('growl'); | ||
var stats = runner.stats; | ||
var msg; | ||
var options; | ||
|
||
if (stats.failures) { | ||
msg = stats.failures + ' of ' + runner.total + ' tests failed'; | ||
options = { | ||
name: 'mocha', | ||
title: 'Failed', | ||
image: image('error') | ||
}; | ||
} else { | ||
msg = stats.passes + ' tests passed in ' + stats.duration + 'ms'; | ||
options = { | ||
name: 'mocha', | ||
title: 'Passed', | ||
image: image('ok') | ||
}; | ||
} | ||
growl(msg, options, onCompletion); | ||
}; | ||
|
||
/** | ||
* Callback for result of attempted Growl notification. | ||
* | ||
* @callback Growl~growlCB | ||
* @param {*} err - Error object, or <code>null</code> if successful. | ||
* @param {string} stdout - <code>stdout</code> from notification delivery | ||
* process. | ||
* @param {string} stderr - <code>stderr</code> from notification delivery | ||
* process. It will include timestamp and executed command with arguments. | ||
*/ | ||
|
||
function onCompletion(err, stdout, stderr) { | ||
if (err) { | ||
if (err.code === 'ENOENT') { | ||
console.error( | ||
'Growl notification support not installed... listener removed!' | ||
); | ||
runner.removeListener('end', sendNotification); | ||
} else { | ||
console.error('growl: ', err.message); | ||
} | ||
} | ||
} | ||
|
||
// :TBD: should this be `.once`? | ||
runner.on('end', sendNotification); | ||
}; | ||
|
||
/** | ||
* Returns Growl image `name` path. | ||
* | ||
* @private | ||
* @param {string} name - Basename of associated Growl image. | ||
* @return {string} Pathname to Growl image | ||
*/ | ||
function image(name) { | ||
return path.join(__dirname, '..', 'assets', 'growl', name + '.png'); | ||
} | ||
|
||
/** | ||
* @summary | ||
* Locate a binary in the user's `PATH`. | ||
* | ||
* @description | ||
* Takes a list of command names and searches the path for each executable | ||
* file that would be run had these commands actually been invoked. | ||
* | ||
* @private | ||
* @param {string[]} binaries - Names of binary files to search for. | ||
* @return {string} absolute path of first binary found, or empty string if none | ||
*/ | ||
function which(binaries) { | ||
var paths = process.env.PATH.split(path.delimiter); | ||
var exists = fs.existsSync || path.existsSync; | ||
|
||
for (var n = 0, blen = binaries.length; n < blen; n++) { | ||
var binary = binaries[n]; | ||
var loc; | ||
for (var i = 0, plen = paths.length; i < plen; i++) { | ||
loc = path.join(paths[i], binary); | ||
if (exists(loc)) { | ||
return loc; | ||
} | ||
} | ||
} | ||
return ''; | ||
} | ||
|
||
/** | ||
* @summary | ||
* Get platform-specific Growl support binaries. | ||
* | ||
* @description | ||
* Somewhat brittle dependency on `growl` package implementation, but it | ||
* rarely changes. | ||
* | ||
* @private | ||
* @see {@link https://github.com/tj/node-growl/blob/master/lib/growl.js#L28-L126|setupCmd} | ||
* @return {string[]} names of Growl support binaries | ||
*/ | ||
function getSupportBinaries() { | ||
switch (os.type()) { | ||
case 'Darwin': | ||
return ['terminal-notifier', 'growlnotify']; | ||
case 'Linux': | ||
return ['notify-send', 'growl']; | ||
case 'Windows_NT': | ||
return ['growlnotify']; | ||
} | ||
return []; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters