diff --git a/.eslintrc.yml b/.eslintrc.yml index 2d4721b8ed..77037f48b3 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -24,6 +24,7 @@ overrides: - bin/* - lib/cli/**/*.js - test/node-unit/**/*.js + - lib/growl.js parserOptions: ecmaVersion: 6 env: diff --git a/lib/growl.js b/lib/growl.js index ee3d23232a..eae2281823 100644 --- a/lib/growl.js +++ b/lib/growl.js @@ -5,9 +5,9 @@ * @module Growl */ -var fs = require('fs'); -var os = require('os'); -var path = require('path'); +const os = require('os'); +const path = require('path'); +const {sync: which} = require('which'); /** * @summary @@ -23,8 +23,14 @@ var path = require('path'); * @see {@link Mocha#isGrowlCapable} * @return {boolean} whether Growl notification support can be expected */ -exports.isCapable = function() { - return !process.browser && which(getSupportBinaries()) !== ''; +exports.isCapable = () => { + if (!process.browser) { + return getSupportBinaries().reduce( + (acc, binary) => acc || Boolean(which(binary, {nothrow: true})), + false + ); + } + return false; }; /** @@ -34,11 +40,10 @@ exports.isCapable = function() { * @see {@link Mocha#_growl} * @param {Runner} runner - Runner instance. */ -exports.notify = function(runner) { - var sendNotification = function() { +exports.notify = runner => { + runner.once('end', () => { display(runner); - }; - runner.once('end', sendNotification); + }); }; /** @@ -47,35 +52,35 @@ exports.notify = function(runner) { * @private * @param {Runner} runner - Runner instance. */ -function display(runner) { - var growl = require('growl'); - var stats = runner.stats; - var symbol = { +const display = runner => { + const growl = require('growl'); + const stats = runner.stats; + const symbol = { cross: '\u274C', tick: '\u2705' }; - var _message; - var message; - var title; + let _message; + let message; + let title; if (stats.failures) { - _message = stats.failures + ' of ' + runner.total + ' tests failed'; - message = symbol.cross + ' ' + _message; + _message = `${stats.failures} of ${runner.total} tests failed`; + message = `${symbol.cross} ${_message}`; title = 'Failed'; } else { - _message = stats.passes + ' tests passed in ' + stats.duration + 'ms'; - message = symbol.tick + ' ' + _message; + _message = `${stats.passes} tests passed in ${stats.duration}ms`; + message = `${symbol.tick} ${_message}`; title = 'Passed'; } // Send notification - var options = { + const options = { image: logo(), name: 'mocha', - title: title + title }; growl(message, options, onCompletion); -} +}; /** * @summary @@ -88,15 +93,11 @@ function display(runner) { * @private * @callback Growl~growlCB * @param {*} err - Error object, or null if successful. - * @param {string} stdout - stdout from notification delivery - * process. - * @param {string} stderr - stderr from notification delivery - * process. It will include timestamp and executed command with arguments. */ -function onCompletion(err, stdout, stderr) { +function onCompletion(err) { if (err) { // As notifications are tangential to our purpose, just log the error. - var message = + const message = err.code === 'ENOENT' ? 'prerequisite software not found' : err.message; console.error('notification error:', message); } @@ -108,36 +109,9 @@ function onCompletion(err, stdout, stderr) { * @private * @return {string} Pathname of Mocha logo */ -function logo() { +const logo = () => { return path.join(__dirname, '..', 'assets', 'mocha-logo-96.png'); -} - -/** - * @summary - * Locates a binary in the user's `PATH` environment variable. - * - * @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); - - for (var n = 0, blen = binaries.length; n < blen; n++) { - var binary = binaries[n]; - for (var i = 0, plen = paths.length; i < plen; i++) { - var loc = path.join(paths[i], binary); - if (fs.existsSync(loc)) { - return loc; - } - } - } - return ''; -} +}; /** * @summary @@ -151,11 +125,11 @@ function which(binaries) { * @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() { - var binaries = { +const getSupportBinaries = () => { + const binaries = { Darwin: ['terminal-notifier', 'growlnotify'], Linux: ['notify-send', 'growl'], Windows_NT: ['growlnotify.exe'] }; return binaries[os.type()] || []; -} +}; diff --git a/package-lock.json b/package-lock.json index 3de8118bc2..ff5e41de3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13263,15 +13263,6 @@ "semver": "^5.1.0" } }, - "package-json-versionify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/package-json-versionify/-/package-json-versionify-1.0.4.tgz", - "integrity": "sha1-WGBYepRIc6a35tJujlH/siMVvxc=", - "dev": true, - "requires": { - "browserify-package-json": "^1.0.0" - } - }, "pako": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.6.tgz", @@ -17850,9 +17841,9 @@ } }, "which": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.0.tgz", - "integrity": "sha512-xcJpopdamTuY5duC/KnTTNBraPK54YwpenP4lzxU8H91GudWpFv38u0CKjclE1Wi2EH2EDz5LRcHcKbCIzqGyg==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "requires": { "isexe": "^2.0.0" } diff --git a/package.json b/package.json index 96a2e48636..d7ca05579e 100644 --- a/package.json +++ b/package.json @@ -488,6 +488,7 @@ "object.assign": "4.1.0", "strip-json-comments": "2.0.1", "supports-color": "^6.0.0", + "which": "1.3.1", "wide-align": "1.1.3", "yargs": "12.0.5", "yargs-parser": "11.1.1",