Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Growl: Web notifications & Desktop prerequisite software check #3542

Merged
merged 49 commits into from
Dec 23, 2018
Merged
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
e2204c1
feat(Growl): Check notification support _before_ use!
plroebuck Nov 1, 2018
b9e557f
docs(growl.js): Correct access level of `notify` method [skip ci]
plroebuck Nov 1, 2018
1902023
docs(growl.js,mocha.js): Minor changes to improve specificity [skip ci]
plroebuck Nov 1, 2018
08ec0a8
docs(mocha.js): Remove unneeded JSDoc tag [skip ci]
plroebuck Nov 1, 2018
ddeca7e
refactor(growl.js): Simplify completion callback error reporting
plroebuck Nov 1, 2018
9b0907a
refactor(growl.js): Change event listener style
plroebuck Nov 1, 2018
64c09eb
test(unit/mocha.spec.js): Convert `context` to `describe`
plroebuck Nov 4, 2018
4773475
refactor(lib/growl.js): Convert `switch` to KV lookup
plroebuck Nov 4, 2018
8a16c9a
refactor(lib/growl.js): Remove `path.exists` anachronism
plroebuck Nov 4, 2018
da25205
docs(docs/index.md): Add Growl section to user guide [skip travis][sk…
plroebuck Nov 10, 2018
f81efdc
Merge branch 'master' into growl
plroebuck Nov 10, 2018
a5df791
docs(docs/index.md): Make markdownlint happy
plroebuck Nov 10, 2018
cfb1182
Merge branch 'growl' of https://github.com/plroebuck/mocha into growl
plroebuck Nov 10, 2018
1271f8b
test(unit/mocha.spec.js): Minor tweaks to Growl-related tests
plroebuck Nov 18, 2018
c303f63
docs(docs/index.md): Updates to user guide for desktop notification s…
plroebuck Nov 18, 2018
20dddf0
feat(lib/browser/growl.js): Added web notification support for `Mocha…
plroebuck Nov 18, 2018
f1c81af
feat(lib/growl.js): Updated notification appearance
plroebuck Nov 18, 2018
fb9b696
refactor(lib/mocha.js): Change `Mocha#growl` notification support err…
plroebuck Nov 18, 2018
c9a8d9b
build(browserify): Updated browserify settings to bundle web notifica…
plroebuck Nov 18, 2018
519f2cd
docs(growl): Fix JSDoc module header
plroebuck Nov 19, 2018
b4ea1de
docs(docs/index.md): Unescape left tag delimmiters in HTML codefence
plroebuck Nov 19, 2018
7e7a67c
docs(user guide): Minor wordsmithing [skip travis][skip appveyor]
plroebuck Nov 19, 2018
528437a
docs(user guide): Make HTML examples more similar
plroebuck Nov 19, 2018
a1dce65
ci(appveyor.yml): Change URL for GrowlForWindows (GfW)
plroebuck Nov 23, 2018
3fe39c1
Merge branch 'master' into growl
plroebuck Nov 25, 2018
81c21da
Merge branch 'master' into growl
plroebuck Dec 1, 2018
63ca450
build(scripts/package-json-cullify.js): Renamed browserify-related sc…
plroebuck Dec 6, 2018
52b3f03
refactor(browser/growl.js): Adapt code to recommendations following r…
plroebuck Dec 6, 2018
8616d25
Merge branch 'growl' of https://github.com/plroebuck/mocha into growl
plroebuck Dec 6, 2018
2d73c2a
docs(docs/index.md): Update browser usage links to reference upcoming…
plroebuck Dec 6, 2018
f1a8db1
docs(browser/growl.js): Update JSDoc with minor tweaks
plroebuck Dec 13, 2018
621a604
refactor(browser/growl.js): Flatten `isPermitted` ask() return value
plroebuck Dec 13, 2018
372fab6
Merge branch 'master' into growl
plroebuck Dec 13, 2018
1f06d4e
Merge branch 'master' into growl
plroebuck Dec 14, 2018
8f72bfb
docs(docs/index.md): Update "unpkg.com" links to reference latest moc…
plroebuck Dec 15, 2018
fd7ea35
fix(lib/growl.js): Add file extension to Windows binary
plroebuck Dec 19, 2018
ba00cf8
chore(assets/mocha-logo-96.png): Add new resolution of PNG logo [ci s…
plroebuck Dec 22, 2018
e33da70
Merge branch 'growl' of https://github.com/plroebuck/mocha into growl
plroebuck Dec 22, 2018
7348df2
refactor(lib/growl.js): Use 96px resolution logo for desktop notifica…
plroebuck Dec 22, 2018
a64c15b
refactor(browser/growl.js): Hardcode language and direction
plroebuck Dec 22, 2018
30c9cd4
fix(browser/growl.js): Use notification logo
plroebuck Dec 22, 2018
ad2fad5
fix(package-scripts.js): Remove quotes around `dedefine` script
plroebuck Dec 22, 2018
dd8bc02
build(package.json): Add new property "notifyLogo"
plroebuck Dec 22, 2018
dbb2dfa
refactor(browser/growl.js): Remove notification tag generation
plroebuck Dec 22, 2018
0f2f5b3
Merge branch 'master' into growl
plroebuck Dec 22, 2018
20d1733
fix(browser/growl.js): Save timer references to avoid Sinon interfering
plroebuck Dec 23, 2018
0688e93
Merge branch 'growl' of https://github.com/plroebuck/mocha into growl
plroebuck Dec 23, 2018
5baeb00
docs(docs/index.md): Minor changes to documentation [ci skip]
plroebuck Dec 23, 2018
dd7bc9c
docs(browser/growl.js): Removed unnecessary JSDoc tag
plroebuck Dec 23, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 89 additions & 10 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Mocha is a feature-rich JavaScript test framework running on [Node.js](https://n
- [async test timeout support](#delayed-root-suite)
- [test retry support](#retry-tests)
- [test-specific timeouts](#test-level)
- [growl notification support](#mochaopts)
- [Growl support](#desktop-notification-support)
- [reports test durations](#test-duration)
- [highlights slow tests](#dot-matrix)
- [file watcher support](#min)
Expand Down Expand Up @@ -70,6 +70,7 @@ Mocha is a feature-rich JavaScript test framework running on [Node.js](https://n
- [Interfaces](#interfaces)
- [Reporters](#reporters)
- [Running Mocha in the Browser](#running-mocha-in-the-browser)
- [Desktop Notification Support](#desktop-notification-support)
- [Configuring Mocha (Node.js)](#configuring-mocha-nodejs)
- [`mocha.opts`](#mochaopts)
- [The `test/` Directory](#the-test-directory)
Expand Down Expand Up @@ -1487,20 +1488,22 @@ A typical setup might look something like the following, where we call `mocha.se
<head>
<meta charset="utf-8">
<title>Mocha Tests</title>
<link href="https://unpkg.com/mocha@5.2.0/mocha.css" rel="stylesheet" />
<link href="https://unpkg.com/mocha@6.0.0/mocha.css" rel="stylesheet" />
</head>
<body>
<div id="mocha"></div>

<script src="https://unpkg.com/chai/chai.js"></script>
<script src="https://unpkg.com/mocha@5.2.0/mocha.js"></script>
<script src="https://unpkg.com/mocha@6.0.0/mocha.js"></script>
plroebuck marked this conversation as resolved.
Show resolved Hide resolved

<script>mocha.setup('bdd')</script>
<script class="mocha-init">
mocha.setup('bdd');
mocha.checkLeaks();
</script>
<script src="test.array.js"></script>
<script src="test.object.js"></script>
<script src="test.xhr.js"></script>
<script>
mocha.checkLeaks();
<script class="mocha-exec">
mocha.run();
</script>
</body>
Expand Down Expand Up @@ -1547,6 +1550,69 @@ The "HTML" reporter is what you see when running Mocha in the browser. It looks

[Mochawesome](https://www.npmjs.com/package/mochawesome) is a great alternative to the default HTML reporter.

## Desktop Notification Support

Desktop notifications allow asynchronous communication of events without
forcing you to react to a notification immediately. Their appearance
and specific functionality vary across platforms. They typically disappear
automatically after a short delay, but their content is often stored in some
manner that allows you to access past notifications.

[Growl][] was an early notification system implementation for OS X and Windows,
boneskull marked this conversation as resolved.
Show resolved Hide resolved
hence, the name of Mocha's `--growl` option.

Once enabled, when your root suite completes test execution, a desktop
notification should appear informing you whether your tests passed or failed.

### Node-based notifications

In order to use desktop notifications with the command-line interface (CLI),
you **must** first install some platform-specific prerequisite software.
Instructions for doing so can be found [here](https://github.com/mochajs/mocha/wiki/Growl-Notifications).

Enable Mocha's desktop notifications as follows:

```bash
plroebuck marked this conversation as resolved.
Show resolved Hide resolved
$ mocha --growl
```

### Browser-based notifications

Web notification support is being made available for current versions of
modern browsers. Ensure your browser version supports both
[promises](https://caniuse.com/#feat=promises) and
[web notifications](https://caniuse.com/#feat=notifications). As the
Notification API evolved over time, **do not expect** the minimum possible
browser version to necessarily work.

Enable Mocha's web notifications with a slight modification to your
client-side mocha HTML. Add a call to `mocha.growl()` prior to running your
tests as shown below:

```html
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://unpkg.com/[email protected]/mocha.css" />
</head>
<body>
<div id="mocha"></div>
<script src="https://unpkg.com/[email protected]/mocha.js"></script>
plroebuck marked this conversation as resolved.
Show resolved Hide resolved
<script class="mocha-init">
mocha.setup('bdd');
mocha.growl(); // <-- Enables web notifications
</script>
<script src="tests.js"></script>
<script class="mocha-exec">
mocha.run();
</script>
</body>
</html>
```

## Configuring Mocha (Node.js)

> *New in v6.0.0*
Expand Down Expand Up @@ -1631,11 +1697,11 @@ For example, suppose you have the following `mocha.opts` file:

The settings above will default the reporter to `dot`, require the `should`
library, and use `bdd` as the interface. With this, you may then invoke `mocha`
with additional arguments, here enabling [Growl](http://growl.info/) support,
and changing the reporter to `list`:
with additional arguments, here changing the reporter to `list` and setting the
slow threshold to half a second:

```sh
$ mocha --reporter list --growl
$ mocha --reporter list --slow 500
```

To ignore your `mocha.opts`, use the `--no-opts` option.
Expand Down Expand Up @@ -1727,7 +1793,20 @@ $ REPORTER=nyan npm test

## More Information

In addition to chatting with us on [Gitter], for additional information such as using spies, mocking, and shared behaviours be sure to check out the [Mocha Wiki](https://github.com/mochajs/mocha/wiki) on GitHub. For discussions join the [Google Group](https://groups.google.com/group/mochajs). For a running example of Mocha, view [example/tests.html](example/tests.html). For the JavaScript API, view the [API documentation](api/) or the [source](https://github.com/mochajs/mocha/blob/master/lib/mocha.js#L51).
In addition to chatting with us on [Gitter][Gitter-mocha], for additional information such as using
spies, mocking, and shared behaviours be sure to check out the [Mocha Wiki][Mocha-wiki] on GitHub.
For discussions join the [Google Group][Google-mocha]. For a running example of Mocha, view
[example/tests.html](example/tests.html). For the JavaScript API, view the [API documentation](api/)
or the [source](https://github.com/mochajs/mocha/blob/master/lib/mocha.js).

[//]: # (Cross reference section)

[Gitter-mocha]: https://gitter.im/mochajs/mocha
[Google-mocha]: https://groups.google.com/group/mochajs
[Growl]: http://growl.info/
[Growl-install]: https://github.com/mochajs/mocha/wiki/Growl-Notifications
[Mocha-website]: https://mochajs.org/
[Mocha-wiki]: https://github.com/mochajs/mocha/wiki

plroebuck marked this conversation as resolved.
Show resolved Hide resolved
<!-- AUTO-GENERATED-CONTENT:START (manifest:template=[Gitter]: ${gitter}) -->
[Gitter]: https://gitter.im/mochajs/mocha
Expand Down
206 changes: 204 additions & 2 deletions lib/browser/growl.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,207 @@
'use strict';

// just stub out growl
/**
* Web Notifications module.
* @module Growl
*/

module.exports = require('../utils').noop;
/**
* @summary
* Checks if browser notification support exists.
*
* @public
* @see {@link https://caniuse.com/#feat=notifications|Browser support (notifications)}
* @see {@link https://caniuse.com/#feat=promises|Browser support (promises)}
* @see {@link Mocha#growl}
* @see {@link Mocha#isGrowlCapable}
* @return {boolean} whether browser notification support exists
*/
exports.isCapable = function() {
var hasNotificationSupport = 'Notification' in window;
var hasPromiseSupport = typeof Promise === 'function';
return process.browser && hasNotificationSupport && hasPromiseSupport;
};

/**
* Implements browser notifications as a pseudo-reporter.
*
* @public
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/notification|Notification API}
* @see {@link https://developers.google.com/web/fundamentals/push-notifications/display-a-notification|Displaying a Notification}
* @see {@link Growl#isPermitted}
* @see {@link Mocha#_growl}
* @param {Runner} runner - Runner instance.
*/
exports.notify = function(runner) {
var promise = isPermitted();

/**
* Attempt notification.
*/
var sendNotification = function() {
// If user hasn't responded yet... "No notification for you!" (Seinfeld)
Promise.race([promise, Promise.resolve(undefined)])
plroebuck marked this conversation as resolved.
Show resolved Hide resolved
.then(canNotify)
.then(function() {
display(runner);
})
.catch(notPermitted);
};

runner.once('end', sendNotification);
};

/**
* Checks if browser notification is permitted by user.
*
* @private
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/Notification/permission|Notification.permission}
* @see {@link Mocha#growl}
* @see {@link Mocha#isGrowlPermitted}
* @returns {Promise<boolean>} promise determining if browser notification
* permissible when fulfilled.
*/
function isPermitted() {
var permitted = {
granted: function allow() {
return Promise.resolve(true);
plroebuck marked this conversation as resolved.
Show resolved Hide resolved
},
denied: function deny() {
return Promise.resolve(false);
},
default: function ask() {
return Notification.requestPermission().then(function(permission) {
return permission === 'granted';
});
}
};

return permitted[Notification.permission]();
}

/**
* @summary
* Determines if notification should proceed.
*
* @description
* Notification shall <strong>not</strong> proceed unless `value` is true.
*
* `value` will equal one of:
* <ul>
* <li><code>true</code> (from `isPermitted`)</li>
* <li><code>false</code> (from `isPermitted`)</li>
* <li><code>undefined</code> (from `Promise.race`)</li>
* </ul>
*
* @private
* @param {boolean|undefined} value - Determines if notification permissible.
* @returns {Promise<undefined>} Notification can proceed
*/
function canNotify(value) {
if (!value) {
var why = value === false ? 'blocked' : 'unacknowledged';
var reason = 'not permitted by user (' + why + ')';
return Promise.reject(new Error(reason));
}
return Promise.resolve();
}

/**
* Displays the notification.
*
* @private
* @param {Runner} runner - Runner instance.
*/
function display(runner) {
var stats = runner.stats;
var symbol = {
cross: '\u274C',
tick: '\u2705'
};
var logo = require('../../package').logo;
plroebuck marked this conversation as resolved.
Show resolved Hide resolved
var lang = 'en-US';
var _message;
var message;
var title;

if (stats.failures) {
_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;
title = 'Passed';
}

// Send notification
var options = {
badge: logo,
body: message,
icon: logo,
lang: lang,
name: 'mocha',
requireInteraction: false,
tag: generateTag(),
timestamp: Date.now()
};
var notification = new Notification(title, options);

// Autoclose after brief delay (makes various browsers act same)
var FORCE_DURATION = 4000;
setTimeout(notification.close.bind(notification), FORCE_DURATION);
}

/**
* As notifications are tangential to our purpose, just log the error.
*
* @private
* @param {Error} err - Why notification didn't happen.
*/
function notPermitted(err) {
console.error('notification error:', err.message);
}

/**
* Used to help distinguish notifications within a root suite run.
* @type {number}
*/
var ntags = 0;
plroebuck marked this conversation as resolved.
Show resolved Hide resolved

/**
* @summary
* Generates a datestamped, run-unique tag for each notification.
*
* @description
* Use of a tag allows notifications that represent the same conceptual event
* to specify as much. This implementation doesn't really bother.
*
* @private
* @returns {string} tag unique within a root suite execution
* @example
*
* generateTag();
* // => "mocha_results-20181117-1"
*/
function generateTag() {
return ['mocha_results', datestamp(), ++ntags].join('-');
plroebuck marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Generates a datestamp.
*
* @private
* @returns {string} tag in YYYYMMDD format
* @example
*
* datestamp();
* // => "20181117"
*/
function datestamp() {
var now = new Date(Date.now());
var year = now.getFullYear();
var month = now.getMonth() + 1;
var day = now.getDate();

return '' + year + (month < 10 ? '0' : '') + month + day;
}
Loading