mocha-compat 3.6.4 → 10.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +61 -110
  3. package/bin/_mocha +10 -0
  4. package/bin/mocha.js +142 -0
  5. package/browser-entry.js +61 -22
  6. package/lib/browser/highlight-tags.js +39 -0
  7. package/lib/browser/parse-query.js +24 -0
  8. package/lib/{template.html → browser/template.html} +7 -5
  9. package/lib/cli/cli.js +89 -0
  10. package/lib/cli/collect-files.js +137 -0
  11. package/lib/cli/commands.js +14 -0
  12. package/lib/cli/config.js +100 -0
  13. package/lib/cli/index.js +3 -0
  14. package/lib/cli/init.js +36 -0
  15. package/lib/cli/lookup-files.js +150 -0
  16. package/lib/cli/node-flags.js +85 -0
  17. package/lib/cli/one-and-dones.js +69 -0
  18. package/lib/cli/options.js +284 -0
  19. package/lib/cli/run-helpers.js +304 -0
  20. package/lib/cli/run-option-metadata.js +116 -0
  21. package/lib/cli/run.js +380 -0
  22. package/lib/cli/watch-run.js +377 -0
  23. package/lib/context.js +16 -42
  24. package/lib/errors.js +563 -0
  25. package/lib/hook.js +50 -9
  26. package/lib/interfaces/bdd.js +28 -29
  27. package/lib/interfaces/common.js +56 -21
  28. package/lib/interfaces/exports.js +4 -7
  29. package/lib/interfaces/qunit.js +10 -11
  30. package/lib/interfaces/tdd.js +15 -15
  31. package/lib/mocha.js +1073 -292
  32. package/lib/mocharc.json +10 -0
  33. package/lib/nodejs/buffered-worker-pool.js +188 -0
  34. package/lib/nodejs/esm-utils.js +106 -0
  35. package/lib/nodejs/file-unloader.js +15 -0
  36. package/lib/nodejs/parallel-buffered-runner.js +433 -0
  37. package/lib/nodejs/reporters/parallel-buffered.js +165 -0
  38. package/lib/nodejs/serializer.js +414 -0
  39. package/lib/nodejs/worker.js +151 -0
  40. package/lib/pending.js +3 -3
  41. package/lib/plugin-loader.js +286 -0
  42. package/lib/reporters/base.js +305 -205
  43. package/lib/reporters/doc.js +52 -21
  44. package/lib/reporters/dot.js +31 -18
  45. package/lib/reporters/html.js +153 -81
  46. package/lib/reporters/json-stream.js +53 -24
  47. package/lib/reporters/json.js +88 -18
  48. package/lib/reporters/landing.js +38 -16
  49. package/lib/reporters/list.js +34 -19
  50. package/lib/reporters/markdown.js +28 -15
  51. package/lib/reporters/min.js +22 -8
  52. package/lib/reporters/nyan.js +62 -58
  53. package/lib/reporters/progress.js +32 -19
  54. package/lib/reporters/spec.js +41 -23
  55. package/lib/reporters/tap.js +255 -32
  56. package/lib/reporters/xunit.js +89 -39
  57. package/lib/runnable.js +233 -148
  58. package/lib/runner.js +679 -380
  59. package/lib/stats-collector.js +83 -0
  60. package/lib/suite.js +376 -112
  61. package/lib/test.js +82 -21
  62. package/lib/utils.js +364 -477
  63. package/mocha.css +183 -54
  64. package/mocha.js +19840 -15846
  65. package/mocha.js.map +1 -0
  66. package/package.json +163 -336
  67. package/{lib/to-iso-string → vendor/serialize-javascript}/LICENSE +8 -6
  68. package/vendor/serialize-javascript/README.md +10 -0
  69. package/vendor/serialize-javascript/index.js +349 -0
  70. package/bin/_mocha-compat +0 -572
  71. package/bin/mocha-compat +0 -87
  72. package/bin/options.js +0 -41
  73. package/bower.json +0 -38
  74. package/images/error.png +0 -0
  75. package/images/ok.png +0 -0
  76. package/lib/browser/.eslintrc.yaml +0 -4
  77. package/lib/browser/debug.js +0 -7
  78. package/lib/browser/events.js +0 -195
  79. package/lib/browser/progress.js +0 -119
  80. package/lib/browser/tty.js +0 -13
  81. package/lib/ms.js +0 -130
  82. package/lib/to-iso-string/index.js +0 -37
  83. package/vendor/glob/LICENSE +0 -15
  84. package/vendor/glob/README.md +0 -399
  85. package/vendor/glob/common.js +0 -244
  86. package/vendor/glob/glob.js +0 -788
  87. package/vendor/glob/package.json +0 -55
  88. package/vendor/glob/sync.js +0 -486
  89. package/vendor/inflight/LICENSE +0 -15
  90. package/vendor/inflight/README.md +0 -37
  91. package/vendor/inflight/inflight.js +0 -54
  92. package/vendor/inflight/package.json +0 -29
@@ -1,11 +1,18 @@
1
1
  'use strict';
2
-
2
+ /**
3
+ * @module Doc
4
+ */
3
5
  /**
4
6
  * Module dependencies.
5
7
  */
6
8
 
7
9
  var Base = require('./base');
8
10
  var utils = require('../utils');
11
+ var constants = require('../runner').constants;
12
+ var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
13
+ var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
14
+ var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
15
+ var EVENT_SUITE_END = constants.EVENT_SUITE_END;
9
16
 
10
17
  /**
11
18
  * Expose `Doc`.
@@ -14,51 +21,75 @@ var utils = require('../utils');
14
21
  exports = module.exports = Doc;
15
22
 
16
23
  /**
17
- * Initialize a new `Doc` reporter.
24
+ * Constructs a new `Doc` reporter instance.
18
25
  *
19
- * @param {Runner} runner
20
- * @api public
26
+ * @public
27
+ * @class
28
+ * @memberof Mocha.reporters
29
+ * @extends Mocha.reporters.Base
30
+ * @param {Runner} runner - Instance triggers reporter actions.
31
+ * @param {Object} [options] - runner options
21
32
  */
22
- function Doc (runner) {
23
- Base.call(this, runner);
33
+ function Doc(runner, options) {
34
+ Base.call(this, runner, options);
24
35
 
25
36
  var indents = 2;
26
37
 
27
- function indent () {
38
+ function indent() {
28
39
  return Array(indents).join(' ');
29
40
  }
30
41
 
31
- runner.on('suite', function (suite) {
42
+ runner.on(EVENT_SUITE_BEGIN, function (suite) {
32
43
  if (suite.root) {
33
44
  return;
34
45
  }
35
46
  ++indents;
36
- console.log('%s<section class="suite">', indent());
47
+ Base.consoleLog('%s<section class="suite">', indent());
37
48
  ++indents;
38
- console.log('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
39
- console.log('%s<dl>', indent());
49
+ Base.consoleLog('%s<h1>%s</h1>', indent(), utils.escape(suite.title));
50
+ Base.consoleLog('%s<dl>', indent());
40
51
  });
41
52
 
42
- runner.on('suite end', function (suite) {
53
+ runner.on(EVENT_SUITE_END, function (suite) {
43
54
  if (suite.root) {
44
55
  return;
45
56
  }
46
- console.log('%s</dl>', indent());
57
+ Base.consoleLog('%s</dl>', indent());
47
58
  --indents;
48
- console.log('%s</section>', indent());
59
+ Base.consoleLog('%s</section>', indent());
49
60
  --indents;
50
61
  });
51
62
 
52
- runner.on('pass', function (test) {
53
- console.log('%s <dt>%s</dt>', indent(), utils.escape(test.title));
63
+ runner.on(EVENT_TEST_PASS, function (test) {
64
+ Base.consoleLog('%s <dt>%s</dt>', indent(), utils.escape(test.title));
65
+ Base.consoleLog('%s <dt>%s</dt>', indent(), utils.escape(test.file));
54
66
  var code = utils.escape(utils.clean(test.body));
55
- console.log('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
67
+ Base.consoleLog('%s <dd><pre><code>%s</code></pre></dd>', indent(), code);
56
68
  });
57
69
 
58
- runner.on('fail', function (test, err) {
59
- console.log('%s <dt class="error">%s</dt>', indent(), utils.escape(test.title));
70
+ runner.on(EVENT_TEST_FAIL, function (test, err) {
71
+ Base.consoleLog(
72
+ '%s <dt class="error">%s</dt>',
73
+ indent(),
74
+ utils.escape(test.title)
75
+ );
76
+ Base.consoleLog(
77
+ '%s <dt class="error">%s</dt>',
78
+ indent(),
79
+ utils.escape(test.file)
80
+ );
60
81
  var code = utils.escape(utils.clean(test.body));
61
- console.log('%s <dd class="error"><pre><code>%s</code></pre></dd>', indent(), code);
62
- console.log('%s <dd class="error">%s</dd>', indent(), utils.escape(err));
82
+ Base.consoleLog(
83
+ '%s <dd class="error"><pre><code>%s</code></pre></dd>',
84
+ indent(),
85
+ code
86
+ );
87
+ Base.consoleLog(
88
+ '%s <dd class="error">%s</dd>',
89
+ indent(),
90
+ utils.escape(err)
91
+ );
63
92
  });
64
93
  }
94
+
95
+ Doc.description = 'HTML documentation';
@@ -1,12 +1,19 @@
1
1
  'use strict';
2
-
2
+ /**
3
+ * @module Dot
4
+ */
3
5
  /**
4
6
  * Module dependencies.
5
7
  */
6
8
 
7
9
  var Base = require('./base');
8
10
  var inherits = require('../utils').inherits;
9
- var color = Base.color;
11
+ var constants = require('../runner').constants;
12
+ var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
13
+ var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
14
+ var EVENT_RUN_BEGIN = constants.EVENT_RUN_BEGIN;
15
+ var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
16
+ var EVENT_RUN_END = constants.EVENT_RUN_END;
10
17
 
11
18
  /**
12
19
  * Expose `Dot`.
@@ -15,49 +22,53 @@ var color = Base.color;
15
22
  exports = module.exports = Dot;
16
23
 
17
24
  /**
18
- * Initialize a new `Dot` matrix test reporter.
25
+ * Constructs a new `Dot` reporter instance.
19
26
  *
20
- * @api public
21
- * @param {Runner} runner
27
+ * @public
28
+ * @class
29
+ * @memberof Mocha.reporters
30
+ * @extends Mocha.reporters.Base
31
+ * @param {Runner} runner - Instance triggers reporter actions.
32
+ * @param {Object} [options] - runner options
22
33
  */
23
- function Dot (runner) {
24
- Base.call(this, runner);
34
+ function Dot(runner, options) {
35
+ Base.call(this, runner, options);
25
36
 
26
37
  var self = this;
27
- var width = Base.window.width * 0.75 | 0;
38
+ var width = (Base.window.width * 0.75) | 0;
28
39
  var n = -1;
29
40
 
30
- runner.on('start', function () {
41
+ runner.on(EVENT_RUN_BEGIN, function () {
31
42
  process.stdout.write('\n');
32
43
  });
33
44
 
34
- runner.on('pending', function () {
45
+ runner.on(EVENT_TEST_PENDING, function () {
35
46
  if (++n % width === 0) {
36
47
  process.stdout.write('\n ');
37
48
  }
38
- process.stdout.write(color('pending', Base.symbols.comma));
49
+ process.stdout.write(Base.color('pending', Base.symbols.comma));
39
50
  });
40
51
 
41
- runner.on('pass', function (test) {
52
+ runner.on(EVENT_TEST_PASS, function (test) {
42
53
  if (++n % width === 0) {
43
54
  process.stdout.write('\n ');
44
55
  }
45
56
  if (test.speed === 'slow') {
46
- process.stdout.write(color('bright yellow', Base.symbols.dot));
57
+ process.stdout.write(Base.color('bright yellow', Base.symbols.dot));
47
58
  } else {
48
- process.stdout.write(color(test.speed, Base.symbols.dot));
59
+ process.stdout.write(Base.color(test.speed, Base.symbols.dot));
49
60
  }
50
61
  });
51
62
 
52
- runner.on('fail', function () {
63
+ runner.on(EVENT_TEST_FAIL, function () {
53
64
  if (++n % width === 0) {
54
65
  process.stdout.write('\n ');
55
66
  }
56
- process.stdout.write(color('fail', Base.symbols.bang));
67
+ process.stdout.write(Base.color('fail', Base.symbols.bang));
57
68
  });
58
69
 
59
- runner.on('end', function () {
60
- console.log();
70
+ runner.once(EVENT_RUN_END, function () {
71
+ process.stdout.write('\n');
61
72
  self.epilogue();
62
73
  });
63
74
  }
@@ -66,3 +77,5 @@ function Dot (runner) {
66
77
  * Inherit from `Base.prototype`.
67
78
  */
68
79
  inherits(Dot, Base);
80
+
81
+ Dot.description = 'dot matrix representation';
@@ -1,28 +1,29 @@
1
1
  'use strict';
2
2
 
3
3
  /* eslint-env browser */
4
-
4
+ /**
5
+ * @module HTML
6
+ */
5
7
  /**
6
8
  * Module dependencies.
7
9
  */
8
10
 
9
11
  var Base = require('./base');
10
12
  var utils = require('../utils');
11
- var Progress = require('../browser/progress');
12
13
  var escapeRe = require('escape-string-regexp');
14
+ var constants = require('../runner').constants;
15
+ var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
16
+ var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
17
+ var EVENT_SUITE_BEGIN = constants.EVENT_SUITE_BEGIN;
18
+ var EVENT_SUITE_END = constants.EVENT_SUITE_END;
19
+ var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
13
20
  var escape = utils.escape;
14
21
 
15
22
  /**
16
23
  * Save timer references to avoid Sinon interfering (see GH-237).
17
24
  */
18
25
 
19
- /* eslint-disable no-unused-vars, no-native-reassign */
20
26
  var Date = global.Date;
21
- var setTimeout = global.setTimeout;
22
- var setInterval = global.setInterval;
23
- var clearTimeout = global.clearTimeout;
24
- var clearInterval = global.clearInterval;
25
- /* eslint-enable no-unused-vars, no-native-reassign */
26
27
 
27
28
  /**
28
29
  * Expose `HTML`.
@@ -31,11 +32,13 @@ var clearInterval = global.clearInterval;
31
32
  exports = module.exports = HTML;
32
33
 
33
34
  /**
34
- * Stats template.
35
+ * Stats template: Result, progress, passes, failures, and duration.
35
36
  */
36
37
 
37
- var statsTemplate = '<ul id="mocha-stats">' +
38
- '<li class="progress"><canvas width="40" height="40"></canvas></li>' +
38
+ var statsTemplate =
39
+ '<ul id="mocha-stats">' +
40
+ '<li class="result"></li>' +
41
+ '<li class="progress-contain"><progress class="progress-element" max="100" value="0"></progress><svg class="progress-ring"><circle class="ring-flatlight" stroke-dasharray="100%,0%"/><circle class="ring-highlight" stroke-dasharray="0%,100%"/></svg><div class="progress-text">0%</div></li>' +
39
42
  '<li class="passes"><a href="javascript:void(0);">passes:</a> <em>0</em></li>' +
40
43
  '<li class="failures"><a href="javascript:void(0);">failures:</a> <em>0</em></li>' +
41
44
  '<li class="duration">duration: <em>0</em>s</li>' +
@@ -44,41 +47,54 @@ var statsTemplate = '<ul id="mocha-stats">' +
44
47
  var playIcon = '&#x2023;';
45
48
 
46
49
  /**
47
- * Initialize a new `HTML` reporter.
50
+ * Constructs a new `HTML` reporter instance.
48
51
  *
49
- * @api public
50
- * @param {Runner} runner
52
+ * @public
53
+ * @class
54
+ * @memberof Mocha.reporters
55
+ * @extends Mocha.reporters.Base
56
+ * @param {Runner} runner - Instance triggers reporter actions.
57
+ * @param {Object} [options] - runner options
51
58
  */
52
- function HTML (runner) {
53
- Base.call(this, runner);
59
+ function HTML(runner, options) {
60
+ Base.call(this, runner, options);
54
61
 
55
62
  var self = this;
56
63
  var stats = this.stats;
57
64
  var stat = fragment(statsTemplate);
58
65
  var items = stat.getElementsByTagName('li');
59
- var passes = items[1].getElementsByTagName('em')[0];
60
- var passesLink = items[1].getElementsByTagName('a')[0];
61
- var failures = items[2].getElementsByTagName('em')[0];
62
- var failuresLink = items[2].getElementsByTagName('a')[0];
63
- var duration = items[3].getElementsByTagName('em')[0];
64
- var canvas = stat.getElementsByTagName('canvas')[0];
66
+ const resultIndex = 0;
67
+ const progressIndex = 1;
68
+ const passesIndex = 2;
69
+ const failuresIndex = 3;
70
+ const durationIndex = 4;
71
+ /** Stat item containing the root suite pass or fail indicator (hasFailures ? '✖' : '✓') */
72
+ var resultIndicator = items[resultIndex];
73
+ /** Passes text and count */
74
+ const passesStat = items[passesIndex];
75
+ /** Stat item containing the pass count (not the word, just the number) */
76
+ const passesCount = passesStat.getElementsByTagName('em')[0];
77
+ /** Stat item linking to filter to show only passing tests */
78
+ const passesLink = passesStat.getElementsByTagName('a')[0];
79
+ /** Failures text and count */
80
+ const failuresStat = items[failuresIndex];
81
+ /** Stat item containing the failure count (not the word, just the number) */
82
+ const failuresCount = failuresStat.getElementsByTagName('em')[0];
83
+ /** Stat item linking to filter to show only failing tests */
84
+ const failuresLink = failuresStat.getElementsByTagName('a')[0];
85
+ /** Stat item linking to the duration time (not the word or unit, just the number) */
86
+ var duration = items[durationIndex].getElementsByTagName('em')[0];
65
87
  var report = fragment('<ul id="mocha-report"></ul>');
66
88
  var stack = [report];
67
- var progress;
68
- var ctx;
89
+ var progressText = items[progressIndex].getElementsByTagName('div')[0];
90
+ var progressBar = items[progressIndex].getElementsByTagName('progress')[0];
91
+ var progressRing = [
92
+ items[progressIndex].getElementsByClassName('ring-flatlight')[0],
93
+ items[progressIndex].getElementsByClassName('ring-highlight')[0]
94
+ ];
95
+ var progressRingRadius = null; // computed CSS unavailable now, so set later
69
96
  var root = document.getElementById('mocha');
70
97
 
71
- if (canvas.getContext) {
72
- var ratio = window.devicePixelRatio || 1;
73
- canvas.style.width = canvas.width;
74
- canvas.style.height = canvas.height;
75
- canvas.width *= ratio;
76
- canvas.height *= ratio;
77
- ctx = canvas.getContext('2d');
78
- ctx.scale(ratio, ratio);
79
- progress = new Progress();
80
- }
81
-
82
98
  if (!root) {
83
99
  return error('#mocha div missing, add it to your document');
84
100
  }
@@ -87,7 +103,7 @@ function HTML (runner) {
87
103
  on(passesLink, 'click', function (evt) {
88
104
  evt.preventDefault();
89
105
  unhide();
90
- var name = (/pass/).test(report.className) ? '' : ' pass';
106
+ var name = /pass/.test(report.className) ? '' : ' pass';
91
107
  report.className = report.className.replace(/fail|pass/g, '') + name;
92
108
  if (report.className.trim()) {
93
109
  hideSuitesWithout('test pass');
@@ -98,7 +114,7 @@ function HTML (runner) {
98
114
  on(failuresLink, 'click', function (evt) {
99
115
  evt.preventDefault();
100
116
  unhide();
101
- var name = (/fail/).test(report.className) ? '' : ' fail';
117
+ var name = /fail/.test(report.className) ? '' : ' fail';
102
118
  report.className = report.className.replace(/fail|pass/g, '') + name;
103
119
  if (report.className.trim()) {
104
120
  hideSuitesWithout('test fail');
@@ -108,18 +124,18 @@ function HTML (runner) {
108
124
  root.appendChild(stat);
109
125
  root.appendChild(report);
110
126
 
111
- if (progress) {
112
- progress.size(40);
113
- }
114
-
115
- runner.on('suite', function (suite) {
127
+ runner.on(EVENT_SUITE_BEGIN, function (suite) {
116
128
  if (suite.root) {
117
129
  return;
118
130
  }
119
131
 
120
132
  // suite
121
133
  var url = self.suiteURL(suite);
122
- var el = fragment('<li class="suite"><h1><a href="%s">%s</a></h1></li>', url, escape(suite.title));
134
+ var el = fragment(
135
+ '<li class="suite"><h1><a href="%s">%s</a></h1></li>',
136
+ url,
137
+ escape(suite.title)
138
+ );
123
139
 
124
140
  // container
125
141
  stack[0].appendChild(el);
@@ -127,27 +143,43 @@ function HTML (runner) {
127
143
  el.appendChild(stack[0]);
128
144
  });
129
145
 
130
- runner.on('suite end', function (suite) {
146
+ runner.on(EVENT_SUITE_END, function (suite) {
131
147
  if (suite.root) {
148
+ if (stats.failures === 0) {
149
+ text(resultIndicator, '✓');
150
+ stat.className += ' pass';
151
+ }
132
152
  updateStats();
133
153
  return;
134
154
  }
135
155
  stack.shift();
136
156
  });
137
157
 
138
- runner.on('pass', function (test) {
158
+ runner.on(EVENT_TEST_PASS, function (test) {
139
159
  var url = self.testURL(test);
140
- var markup = '<li class="test pass %e"><h2>%e<span class="duration">%ems</span> ' +
141
- '<a href="%s" class="replay">' + playIcon + '</a></h2></li>';
160
+ var markup =
161
+ '<li class="test pass %e"><h2>%e<span class="duration">%ems</span> ' +
162
+ '<a href="%s" class="replay">' +
163
+ playIcon +
164
+ '</a></h2></li>';
142
165
  var el = fragment(markup, test.speed, test.title, test.duration, url);
143
166
  self.addCodeToggle(el, test.body);
144
167
  appendToStack(el);
145
168
  updateStats();
146
169
  });
147
170
 
148
- runner.on('fail', function (test) {
149
- var el = fragment('<li class="test fail"><h2>%e <a href="%e" class="replay">' + playIcon + '</a></h2></li>',
150
- test.title, self.testURL(test));
171
+ runner.on(EVENT_TEST_FAIL, function (test) {
172
+ // Update stat items
173
+ text(resultIndicator, '✖');
174
+ stat.className += ' fail';
175
+
176
+ var el = fragment(
177
+ '<li class="test fail"><h2>%e <a href="%e" class="replay">' +
178
+ playIcon +
179
+ '</a></h2></li>',
180
+ test.title,
181
+ self.testURL(test)
182
+ );
151
183
  var stackString; // Note: Includes leading newline
152
184
  var message = test.err.toString();
153
185
 
@@ -162,7 +194,9 @@ function HTML (runner) {
162
194
  if (indexOfMessage === -1) {
163
195
  stackString = test.err.stack;
164
196
  } else {
165
- stackString = test.err.stack.substr(test.err.message.length + indexOfMessage);
197
+ stackString = test.err.stack.slice(
198
+ test.err.message.length + indexOfMessage
199
+ );
166
200
  }
167
201
  } else if (test.err.sourceURL && test.err.line !== undefined) {
168
202
  // Safari doesn't give you a stack. Let's at least provide a source line.
@@ -172,12 +206,21 @@ function HTML (runner) {
172
206
  stackString = stackString || '';
173
207
 
174
208
  if (test.err.htmlMessage && stackString) {
175
- el.appendChild(fragment('<div class="html-error">%s\n<pre class="error">%e</pre></div>',
176
- test.err.htmlMessage, stackString));
209
+ el.appendChild(
210
+ fragment(
211
+ '<div class="html-error">%s\n<pre class="error">%e</pre></div>',
212
+ test.err.htmlMessage,
213
+ stackString
214
+ )
215
+ );
177
216
  } else if (test.err.htmlMessage) {
178
- el.appendChild(fragment('<div class="html-error">%s</div>', test.err.htmlMessage));
217
+ el.appendChild(
218
+ fragment('<div class="html-error">%s</div>', test.err.htmlMessage)
219
+ );
179
220
  } else {
180
- el.appendChild(fragment('<pre class="error">%e%e</pre>', message, stackString));
221
+ el.appendChild(
222
+ fragment('<pre class="error">%e%e</pre>', message, stackString)
223
+ );
181
224
  }
182
225
 
183
226
  self.addCodeToggle(el, test.body);
@@ -185,30 +228,50 @@ function HTML (runner) {
185
228
  updateStats();
186
229
  });
187
230
 
188
- runner.on('pending', function (test) {
189
- var el = fragment('<li class="test pass pending"><h2>%e</h2></li>', test.title);
231
+ runner.on(EVENT_TEST_PENDING, function (test) {
232
+ var el = fragment(
233
+ '<li class="test pass pending"><h2>%e</h2></li>',
234
+ test.title
235
+ );
190
236
  appendToStack(el);
191
237
  updateStats();
192
238
  });
193
239
 
194
- function appendToStack (el) {
240
+ function appendToStack(el) {
195
241
  // Don't call .appendChild if #mocha-report was already .shift()'ed off the stack.
196
242
  if (stack[0]) {
197
243
  stack[0].appendChild(el);
198
244
  }
199
245
  }
200
246
 
201
- function updateStats () {
202
- // TODO: add to stats
203
- var percent = stats.tests / runner.total * 100 | 0;
204
- if (progress) {
205
- progress.update(percent).draw(ctx);
247
+ function updateStats() {
248
+ var percent = ((stats.tests / runner.total) * 100) | 0;
249
+ progressBar.value = percent;
250
+ if (progressText) {
251
+ // setting a toFixed that is too low, makes small changes to progress not shown
252
+ // setting it too high, makes the progress text longer then it needs to
253
+ // to address this, calculate the toFixed based on the magnitude of total
254
+ var decimalPlaces = Math.ceil(Math.log10(runner.total / 100));
255
+ text(
256
+ progressText,
257
+ percent.toFixed(Math.min(Math.max(decimalPlaces, 0), 100)) + '%'
258
+ );
259
+ }
260
+ if (progressRing) {
261
+ var radius = parseFloat(getComputedStyle(progressRing[0]).getPropertyValue('r'));
262
+ var wholeArc = Math.PI * 2 * radius;
263
+ var highlightArc = percent * (wholeArc / 100);
264
+ // The progress ring is in 2 parts, the flatlight color and highlight color.
265
+ // Rendering both on top of the other, seems to make a 3rd color on the edges.
266
+ // To create 1 whole ring with 2 colors, both parts are inverse of the other.
267
+ progressRing[0].style['stroke-dasharray'] = `0,${highlightArc}px,${wholeArc}px`;
268
+ progressRing[1].style['stroke-dasharray'] = `${highlightArc}px,${wholeArc}px`;
206
269
  }
207
270
 
208
271
  // update stats
209
272
  var ms = new Date() - stats.start;
210
- text(passes, stats.passes);
211
- text(failures, stats.failures);
273
+ text(passesCount, stats.passes);
274
+ text(failuresCount, stats.failures);
212
275
  text(duration, (ms / 1000).toFixed(2));
213
276
  }
214
277
  }
@@ -219,15 +282,20 @@ function HTML (runner) {
219
282
  * @param {string} s
220
283
  * @return {string} A new URL.
221
284
  */
222
- function makeUrl (s) {
285
+ function makeUrl(s) {
223
286
  var search = window.location.search;
224
287
 
225
- // Remove previous grep query parameter if present
288
+ // Remove previous {grep, fgrep, invert} query parameters if present
226
289
  if (search) {
227
- search = search.replace(/[?&]grep=[^&\s]*/g, '').replace(/^&/, '?');
290
+ search = search.replace(/[?&](?:f?grep|invert)=[^&\s]*/g, '').replace(/^&/, '?');
228
291
  }
229
292
 
230
- return window.location.pathname + (search ? search + '&' : '?') + 'grep=' + encodeURIComponent(escapeRe(s));
293
+ return (
294
+ window.location.pathname +
295
+ (search ? search + '&' : '?') +
296
+ 'grep=' +
297
+ encodeURIComponent(s)
298
+ );
231
299
  }
232
300
 
233
301
  /**
@@ -236,7 +304,7 @@ function makeUrl (s) {
236
304
  * @param {Object} [suite]
237
305
  */
238
306
  HTML.prototype.suiteURL = function (suite) {
239
- return makeUrl(suite.fullTitle());
307
+ return makeUrl('^' + escapeRe(suite.fullTitle()) + ' ');
240
308
  };
241
309
 
242
310
  /**
@@ -245,7 +313,7 @@ HTML.prototype.suiteURL = function (suite) {
245
313
  * @param {Object} [test]
246
314
  */
247
315
  HTML.prototype.testURL = function (test) {
248
- return makeUrl(test.fullTitle());
316
+ return makeUrl('^' + escapeRe(test.fullTitle()) + '$');
249
317
  };
250
318
 
251
319
  /**
@@ -271,7 +339,7 @@ HTML.prototype.addCodeToggle = function (el, contents) {
271
339
  *
272
340
  * @param {string} msg
273
341
  */
274
- function error (msg) {
342
+ function error(msg) {
275
343
  document.body.appendChild(fragment('<div id="mocha-error">%s</div>', msg));
276
344
  }
277
345
 
@@ -280,15 +348,17 @@ function error (msg) {
280
348
  *
281
349
  * @param {string} html
282
350
  */
283
- function fragment (html) {
351
+ function fragment(html) {
284
352
  var args = arguments;
285
353
  var div = document.createElement('div');
286
354
  var i = 1;
287
355
 
288
356
  div.innerHTML = html.replace(/%([se])/g, function (_, type) {
289
357
  switch (type) {
290
- case 's': return String(args[i++]);
291
- case 'e': return escape(args[i++]);
358
+ case 's':
359
+ return String(args[i++]);
360
+ case 'e':
361
+ return escape(args[i++]);
292
362
  // no default
293
363
  }
294
364
  });
@@ -302,7 +372,7 @@ function fragment (html) {
302
372
  *
303
373
  * @param {text} classname
304
374
  */
305
- function hideSuitesWithout (classname) {
375
+ function hideSuitesWithout(classname) {
306
376
  var suites = document.getElementsByClassName('suite');
307
377
  for (var i = 0; i < suites.length; i++) {
308
378
  var els = suites[i].getElementsByClassName(classname);
@@ -315,10 +385,10 @@ function hideSuitesWithout (classname) {
315
385
  /**
316
386
  * Unhide .hidden suites.
317
387
  */
318
- function unhide () {
388
+ function unhide() {
319
389
  var els = document.getElementsByClassName('suite hidden');
320
- for (var i = 0; i < els.length; ++i) {
321
- els[i].className = els[i].className.replace('suite hidden', 'suite');
390
+ while (els.length > 0) {
391
+ els[0].className = els[0].className.replace('suite hidden', 'suite');
322
392
  }
323
393
  }
324
394
 
@@ -328,7 +398,7 @@ function unhide () {
328
398
  * @param {HTMLElement} el
329
399
  * @param {string} contents
330
400
  */
331
- function text (el, contents) {
401
+ function text(el, contents) {
332
402
  if (el.textContent) {
333
403
  el.textContent = contents;
334
404
  } else {
@@ -339,10 +409,12 @@ function text (el, contents) {
339
409
  /**
340
410
  * Listen on `event` with callback `fn`.
341
411
  */
342
- function on (el, event, fn) {
412
+ function on(el, event, fn) {
343
413
  if (el.addEventListener) {
344
414
  el.addEventListener(event, fn, false);
345
415
  } else {
346
416
  el.attachEvent('on' + event, fn);
347
417
  }
348
418
  }
419
+
420
+ HTML.browserOnly = true;