'use strict'; /** * Desktop Notifications module. * @module Growl */ const os = require('os'); const path = require('path'); const {sync: which} = require('which'); const {EVENT_RUN_END} = require('./runner').constants; /** * @summary * Checks if Growl notification support seems likely. * * @description * Glosses over the distinction between an unsupported platform * and one that lacks prerequisite software installations. * * @public * @see {@link https://github.com/tj/node-growl/blob/master/README.md|Prerequisite Installs} * @see {@link Mocha#growl} * @see {@link Mocha#isGrowlCapable} * @return {boolean} whether Growl notification support can be expected */ exports.isCapable = () => { if (!process.browser) { return getSupportBinaries().reduce( (acc, binary) => acc || Boolean(which(binary, {nothrow: true})), false ); } return false; }; /** * Implements desktop notifications as a pseudo-reporter. * * @public * @see {@link Mocha#_growl} * @param {Runner} runner - Runner instance. */ exports.notify = runner => { runner.once(EVENT_RUN_END, () => { display(runner); }); }; /** * Displays the notification. * * @private * @param {Runner} runner - Runner instance. */ const display = runner => { const growl = require('growl'); const stats = runner.stats; const symbol = { cross: '\u274C', tick: '\u2705' }; let _message; let message; let title; if (stats.failures) { _message = `${stats.failures} of ${stats.tests} tests failed`; message = `${symbol.cross} ${_message}`; title = 'Failed'; } else { _message = `${stats.passes} tests passed in ${stats.duration}ms`; message = `${symbol.tick} ${_message}`; title = 'Passed'; } // Send notification const options = { image: logo(), name: 'mocha', title }; growl(message, options, onCompletion); }; /** * @summary * Callback for result of attempted Growl notification. * * @description * Despite its appearance, this is not an Error-first * callback -- all parameters are populated regardless of success. * * @private * @callback Growl~growlCB * @param {*} err - Error object, or null if successful. */ function onCompletion(err) { if (err) { // As notifications are tangential to our purpose, just log the error. const message = err.code === 'ENOENT' ? 'prerequisite software not found' : err.message; console.error('notification error:', message); } } /** * Returns Mocha logo image path. * * @private * @return {string} Pathname of Mocha logo */ const logo = () => { return path.join(__dirname, '..', 'assets', 'mocha-logo-96.png'); }; /** * @summary * Gets 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 */ const getSupportBinaries = () => { const binaries = { Darwin: ['terminal-notifier', 'growlnotify'], Linux: ['notify-send', 'growl'], Windows_NT: ['growlnotify.exe'] }; return binaries[os.type()] || []; };