From c66b34e7e1ee5b9b7054ecccc73f589ead2f8543 Mon Sep 17 00:00:00 2001 From: Pavel Zubkou Date: Tue, 11 Aug 2015 17:29:16 +0300 Subject: [PATCH 1/6] Set safe CSS for progress indicator which does not conflict with Bootstrap --- mocha.css | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mocha.css b/mocha.css index 3b82ae915c..759a6c8c47 100644 --- a/mocha.css +++ b/mocha.css @@ -261,6 +261,15 @@ body { #mocha-stats .progress { float: right; padding-top: 0; + + /** + * Set safe initial values, so mochas .progress does not inherit these + * properties from Bootstrap .progress (which causes .progress height to + * equal line height set in Bootstrap). + */ + height: auto; + box-shadow: none; + background-color: initial; } #mocha-stats em { From d4f7905dbb9d4cc65cca02373b99e5549fa7ce2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Thu, 28 Jan 2016 22:28:09 +1100 Subject: [PATCH 2/6] Strenghten checks across the board for pending/skipped tests or suites Checking for boolean property `.pending` on Test or Suite objects was too brittle in a way that the caller often forgot to check whether any of the object's parents are marked as pending as well. This moves all the pending checks to new `.isPending()` predicate method that is defined on all Suite and Runnable (and thus Test and Hook as well.) Runners and reporters should always use this method to check for "pendingness" of test and suite objects. --- lib/interfaces/bdd.js | 2 +- lib/interfaces/tdd.js | 2 +- lib/reporters/html.js | 4 ++-- lib/reporters/xunit.js | 2 +- lib/runnable.js | 12 +++++++++++- lib/runner.js | 9 ++------- lib/suite.js | 20 +++++++++++++------- test/suite.js | 5 +++++ test/test.js | 21 +++++++++++++++++++++ 9 files changed, 57 insertions(+), 20 deletions(-) diff --git a/lib/interfaces/bdd.js b/lib/interfaces/bdd.js index e33c9b0d77..8519a9a7b4 100644 --- a/lib/interfaces/bdd.js +++ b/lib/interfaces/bdd.js @@ -79,7 +79,7 @@ module.exports = function(suite) { var it = context.it = context.specify = function(title, fn) { var suite = suites[0]; - if (suite.pending) { + if (suite.isPending()) { fn = null; } var test = new Test(title, fn); diff --git a/lib/interfaces/tdd.js b/lib/interfaces/tdd.js index 8cb211ba39..d37936ae41 100644 --- a/lib/interfaces/tdd.js +++ b/lib/interfaces/tdd.js @@ -81,7 +81,7 @@ module.exports = function(suite) { */ context.test = function(title, fn) { var suite = suites[0]; - if (suite.pending) { + if (suite.isPending()) { fn = null; } var test = new Test(title, fn); diff --git a/lib/reporters/html.js b/lib/reporters/html.js index 7da2508e40..95fc1e9f73 100644 --- a/lib/reporters/html.js +++ b/lib/reporters/html.js @@ -155,7 +155,7 @@ function HTML(runner) { if (test.state === 'passed') { var url = self.testURL(test); el = fragment('
  • %e%ems

  • ', test.speed, test.title, test.duration, url); - } else if (test.pending) { + } else if (test.isPending()) { el = fragment('
  • %e

  • ', test.title); } else { el = fragment('
  • %e

  • ', test.title, self.testURL(test)); @@ -193,7 +193,7 @@ function HTML(runner) { // toggle code // TODO: defer - if (!test.pending) { + if (!test.isPending()) { var h2 = el.getElementsByTagName('h2')[0]; on(h2, 'click', function() { diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 01d9d87824..875d592876 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -131,7 +131,7 @@ XUnit.prototype.test = function(test) { if (test.state === 'failed') { var err = test.err; this.write(tag('testcase', attrs, false, tag('failure', {}, false, cdata(escape(err.message) + '\n' + err.stack)))); - } else if (test.pending) { + } else if (test.isPending()) { this.write(tag('testcase', attrs, false, tag('skipped', {}, true))); } else { this.write(tag('testcase', attrs, true)); diff --git a/lib/runnable.js b/lib/runnable.js index 9ac45de3f8..0bac606fe6 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -54,6 +54,7 @@ function Runnable(title, fn) { this._trace = new Error('done() called multiple times'); this._retries = -1; this._currentRetry = 0; + this.pending = false; } /** @@ -130,6 +131,15 @@ Runnable.prototype.skip = function() { throw new Pending(); }; +/** + * Check if this runnable or its parent suite is marked as pending. + * + * @api private + */ +Runnable.prototype.isPending = function() { + return this.pending || (this.parent && this.parent.isPending()); +}; + /** * Set number of retries. * @@ -302,7 +312,7 @@ Runnable.prototype.run = function(fn) { // sync or promise-returning try { - if (this.pending) { + if (this.isPending()) { done(); } else { callFn(this.fn); diff --git a/lib/runner.js b/lib/runner.js index e4d935e8c2..ba4d7df97c 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -507,12 +507,7 @@ Runner.prototype.runTests = function(suite, fn) { return; } - function parentPending(suite) { - return suite.pending || (suite.parent && parentPending(suite.parent)); - } - - // pending - if (test.pending || parentPending(test.parent)) { + if (test.isPending()) { self.emit('pending', test); self.emit('test end', test); return next(); @@ -521,7 +516,7 @@ Runner.prototype.runTests = function(suite, fn) { // execute test and hook(s) self.emit('test', self.test = test); self.hookDown('beforeEach', function(err, errSuite) { - if (suite.pending) { + if (suite.isPending()) { self.emit('pending', test); self.emit('test end', test); return next(); diff --git a/lib/suite.js b/lib/suite.js index 8e6f393e68..d43dd45604 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -28,9 +28,6 @@ exports = module.exports = Suite; exports.create = function(parent, title) { var suite = new Suite(title, parent.ctx); suite.parent = parent; - if (parent.pending) { - suite.pending = true; - } title = suite.fullTitle(); parent.addSuite(suite); return suite; @@ -176,6 +173,15 @@ Suite.prototype.bail = function(bail) { return this; }; +/** + * Check if this suite or its parent suite is marked as pending. + * + * @api private + */ +Suite.prototype.isPending = function() { + return this.pending || (this.parent && this.parent.isPending()); +}; + /** * Run `fn(test[, done])` before running tests. * @@ -185,7 +191,7 @@ Suite.prototype.bail = function(bail) { * @return {Suite} for chaining */ Suite.prototype.beforeAll = function(title, fn) { - if (this.pending) { + if (this.isPending()) { return this; } if (typeof title === 'function') { @@ -215,7 +221,7 @@ Suite.prototype.beforeAll = function(title, fn) { * @return {Suite} for chaining */ Suite.prototype.afterAll = function(title, fn) { - if (this.pending) { + if (this.isPending()) { return this; } if (typeof title === 'function') { @@ -245,7 +251,7 @@ Suite.prototype.afterAll = function(title, fn) { * @return {Suite} for chaining */ Suite.prototype.beforeEach = function(title, fn) { - if (this.pending) { + if (this.isPending()) { return this; } if (typeof title === 'function') { @@ -275,7 +281,7 @@ Suite.prototype.beforeEach = function(title, fn) { * @return {Suite} for chaining */ Suite.prototype.afterEach = function(title, fn) { - if (this.pending) { + if (this.isPending()) { return this; } if (typeof title === 'function') { diff --git a/test/suite.js b/test/suite.js index 011b3fb2b8..2cd7327667 100644 --- a/test/suite.js +++ b/test/suite.js @@ -288,6 +288,11 @@ describe('Suite', function(){ this.first.suites.should.have.length(1); this.first.suites[0].should.equal(this.second); }); + + it('treats suite as pending if its parent is pending', function(){ + this.first.pending = true + this.second.isPending.should.be.true + }); }); // describe('.addTest()', function(){ diff --git a/test/test.js b/test/test.js index 65b27fb8f6..31a9458b5b 100644 --- a/test/test.js +++ b/test/test.js @@ -1,4 +1,5 @@ var mocha = require('../') + , should = require('should') , Context = mocha.Context , Test = mocha.Test; @@ -52,4 +53,24 @@ describe('Test', function(){ this._test.clone().file.should.equal('bar'); }); }); + + describe('.isPending()', function(){ + beforeEach(function(){ + this._test = new Test('Is it skipped', function () {}); + }); + + it('should not be pending by default', function(){ + should(this._test.isPending()).not.be.ok(); + }); + + it('should be pending when marked as such', function(){ + this._test.pending = true; + should(this._test.isPending()).be.ok(); + }); + + it('should be pending when its parent is pending', function(){ + this._test.parent = { isPending: function(){ return true } }; + should(this._test.isPending()).be.ok(); + }); + }); }); From da2fadc73dd1ea83298facd69dff0b8f26faea73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mislav=20Marohni=C4=87?= Date: Thu, 28 Jan 2016 22:33:32 +1100 Subject: [PATCH 3/6] Mark `.skip()` as public API Users are supposed to call `this.skip()` from tests and suite hooks, so this is definitely a public API. --- lib/runnable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runnable.js b/lib/runnable.js index 0bac606fe6..707f9559b4 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -125,7 +125,7 @@ Runnable.prototype.enableTimeouts = function(enabled) { /** * Halt and mark as pending. * - * @api private + * @api public */ Runnable.prototype.skip = function() { throw new Pending(); From 872684f44f677774f544373abf753b5080af8655 Mon Sep 17 00:00:00 2001 From: ryym Date: Wed, 3 Feb 2016 15:58:29 +0900 Subject: [PATCH 4/6] handle Symbol values in util.stringify() fix #2089 --- lib/utils.js | 2 ++ test/acceptance/utils.js | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/lib/utils.js b/lib/utils.js index 7a65f92ec3..de8acaffcc 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -472,6 +472,7 @@ function jsonStringify(object, spaces, depth) { break; case 'boolean': case 'regexp': + case 'symbol': case 'number': val = val === 0 && (1 / val) === -Infinity // `-0` ? '-0' @@ -597,6 +598,7 @@ exports.canonicalize = function(value, stack) { case 'number': case 'regexp': case 'boolean': + case 'symbol': canonicalizedObj = value; break; default: diff --git a/test/acceptance/utils.js b/test/acceptance/utils.js index 053e7ca1f1..5f67f27430 100644 --- a/test/acceptance/utils.js +++ b/test/acceptance/utils.js @@ -331,6 +331,15 @@ describe('lib/utils', function () { stringify(a).should.equal('{\n "foo": 1\n}'); }); + + // In old version node.js, Symbol is not available by default. + if (typeof global.Symbol === 'function') { + it('should handle Symbol', function () { + var symbol = Symbol('value'); + stringify(symbol).should.equal('Symbol(value)'); + stringify({symbol: symbol}).should.equal('{\n "symbol": Symbol(value)\n}') + }); + } }); describe('type', function () { From ae54df9e16cf2a4fec3cb0efd91f403ac7b0d47d Mon Sep 17 00:00:00 2001 From: Maximilian Antoni Date: Sun, 7 Feb 2016 11:41:14 +0100 Subject: [PATCH 5/6] fix diff for objects with custom hasOwnProperty values --- lib/utils.js | 2 +- test/reporters/base.js | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lib/utils.js b/lib/utils.js index 7a65f92ec3..5dc41aa312 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -498,7 +498,7 @@ function jsonStringify(object, spaces, depth) { } for (var i in object) { - if (!object.hasOwnProperty(i)) { + if (!Object.prototype.hasOwnProperty.call(object, i)) { continue; // not my business } --length; diff --git a/test/reporters/base.js b/test/reporters/base.js index e33e26f918..affc7ba21c 100644 --- a/test/reporters/base.js +++ b/test/reporters/base.js @@ -129,6 +129,26 @@ describe('Base reporter', function () { errOut.should.match(/\+ expected/); }); + it('should stringify Object.create(null)', function () { + var err = new Error('test'), + errOut; + + err.actual = Object.create(null); + err.actual.hasOwnProperty = 1; + err.expected = Object.create(null); + err.expected.hasOwnProperty = 2; + err.showDiff = true; + var test = makeTest(err); + + Base.list([test]); + + errOut = stdout.join('\n'); + errOut.should.match(/"hasOwnProperty"/); + errOut.should.match(/test/); + errOut.should.match(/\- actual/); + errOut.should.match(/\+ expected/); + }); + it('should remove message from stack', function () { var err = { message: 'Error', From f7a42fc77647cc57d7f8077af56e10366cc24c10 Mon Sep 17 00:00:00 2001 From: "Daniel St. Jules" Date: Tue, 16 Feb 2016 18:21:16 -0800 Subject: [PATCH 6/6] Fix hook error in browser reporter by moving body prop from test to runnable --- lib/runnable.js | 1 + lib/test.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/runnable.js b/lib/runnable.js index 9ac45de3f8..f8a2af722b 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -45,6 +45,7 @@ module.exports = Runnable; function Runnable(title, fn) { this.title = title; this.fn = fn; + this.body = (fn || '').toString(); this.async = fn && fn.length; this.sync = !this.async; this._timeout = 2000; diff --git a/lib/test.js b/lib/test.js index b39ce42cad..a95cd31a48 100644 --- a/lib/test.js +++ b/lib/test.js @@ -22,7 +22,6 @@ function Test(title, fn) { Runnable.call(this, title, fn); this.pending = !fn; this.type = 'test'; - this.body = (fn || '').toString(); } /**