var synonyms = require('./synonyms.js') var tsame = require('tsame') // same thing, strict or not var tmatch = require('tmatch') // ok with partial estimates var extraFromError = require('./extra-from-error.js') var stack = require('./stack.js') // Load Buffer the old way for browserify's sake var Buffer = require('buffer').Buffer // eslint-disable-line // this is actually the "working half" of the Test class. // each method figures out if it's a pass or fail, and decorates // the extra bit, and then calls either pass() or fail() or some // other assert method. // // typically, a plugin would do this on a specific instance, eg on // the root test harness instance. but we do this here to add some // useful prototype methods. exports.decorate = decorate function decorate (t) { t.addAssert('ok', 1, function (obj, message, extra) { message = message || 'expect truthy value' if (obj) { return this.pass(message, extra) } return this.fail(message, extra) }) t.addAssert('notOk', 1, function (obj, message, extra) { message = message || 'expect falsey value' return this.ok(!obj, message, extra) }) t.addAssert('error', 1, function (er, message, extra) { if (!er) { return this.pass(message || 'should not error', extra) } if (!(er instanceof Error)) { extra.found = er return this.fail(message || 'non-Error error encountered', extra) } message = message || er.message extra.found = er return this.fail(message, extra) }) t.addAssert('equal', 2, function (f, w, m, e) { m = m || 'should be equal' if (f === w) { return this.pass(m, e) } e.found = f e.wanted = w e.compare = '===' if (typeof f === 'object' && typeof w === 'object' && f && w && tsame(f, w)) { e.note = 'Objects never === one another' } return this.fail(m, e) }) t.addAssert('not', 2, function (f, w, m, e) { m = m || 'should not be equal' if (f !== w) { return this.pass(m, e) } e.found = f e.doNotWant = w e.compare = '!==' return this.fail(m, e) }) t.addAssert('same', 2, function (f, w, m, e) { m = m || 'should be equivalent' e.found = f e.wanted = w return this.ok(tsame(f, w), m, e) }) t.addAssert('notSame', 2, function (f, w, m, e) { m = m || 'should not be equivalent' e.found = f e.doNotWant = w return this.notOk(tsame(f, w), m, e) }) t.addAssert('strictSame', 2, function (f, w, m, e) { m = m || 'should be equivalent strictly' e.found = f e.wanted = w return this.ok(tsame.strict(f, w), m, e) }) t.addAssert('strictNotSame', 2, function (f, w, m, e) { m = m || 'should be equivalent strictly' e.found = f e.doNotWant = w return this.notOk(tsame.strict(f, w), m, e) }) t.addAssert('match', 2, function (f, w, m, e) { m = m || 'should match pattern provided' e.found = f e.pattern = w return this.ok(tmatch(f, w), m, e) }) t.addAssert('notMatch', 2, function (f, w, m, e) { m = m || 'should not match pattern provided' e.found = f e.pattern = w return this.ok(!tmatch(f, w), m, e) }) t.addAssert('type', 2, function (obj, klass, m, e) { var name = klass if (typeof name === 'function') { name = name.name || '(anonymous constructor)' } m = m || 'type is ' + name // simplest case, it literally is the same thing if (obj === klass) { return this.pass(m, e) } var type = typeof obj if (!obj && type === 'object') { type = 'null' } if (type === 'function' && typeof klass === 'function' && klass !== Object) { // treat as object, but not Object // t.type(() => {}, Function) type = 'object' } if (type === 'object' && klass !== 'object') { if (typeof klass === 'function') { e.found = Object.getPrototypeOf(obj).constructor.name e.wanted = name return this.ok(obj instanceof klass, m, e) } // check prototype chain for name // at this point, we already know klass is not a function // if the klass specified is an obj in the proto chain, pass // if the name specified is the name of a ctor in the chain, pass var p = obj do { var ctor = p.constructor && p.constructor.name if (p === klass || ctor === name) { return this.pass(m, e) } p = Object.getPrototypeOf(p) } while (p) } return this.equal(type, name, m, e) }) t.addAssert('throws', 4, function (fn_, wanted_, m_, e_, m, e__) { var fn, wanted, e for (var i = 0; i < arguments.length - 1; i++) { var arg = arguments[i] if (typeof arg === 'function') { if (arg === Error || arg.prototype instanceof Error) { wanted = arg } else if (!fn) { fn = arg } } else if (typeof arg === 'string' && arg) { m = arg } else if (typeof arg === 'object') { if (!wanted) { wanted = arg } else { e = arg } } } // Copy local properties of the 'extra' object, like 'skip' etc Object.keys(e__).forEach(function (i) { e[i] = e__[i] }) if (!m) { m = fn && fn.name || 'expected to throw' } if (wanted) { if (wanted instanceof Error) { var w = { message: wanted.message } if (wanted.name) { w.name = wanted.name } // intentionally copying non-local properties, since this // is an Error object, and those are funky. for (i in wanted) { w[i] = wanted[i] } wanted = w m += ': ' + (wanted.name || 'Error') + ' ' + wanted.message e = e || {} if (e !== wanted) { e.wanted = wanted } } } if (typeof fn !== 'function') { e = e || {} e.todo = true return this.pass(m, e) } try { fn() return this.fail(m, e) } catch (er) { // 'name' is a getter. if (er.name) { er.name = er.name + '' } if (wanted) { if (Object.prototype.toString.call(wanted) === '[object RegExp]') { return this.match(er.message, wanted, m, e) } return this.has(er, wanted, m, e) } else { return this.pass(m, e) } } }) t.addAssert('doesNotThrow', 1, function (fn, m, e) { if (typeof fn === 'string') { var x = fn fn = m m = x } if (!m) { m = fn && fn.name || 'expected to not throw' } if (typeof fn !== 'function') { e.todo = true return this.pass(m, e) } try { fn() return this.pass(m, e) } catch (er) { var extra = extraFromError(er, e) extra.message = er.message return this.fail(m, extra) } }) // like throws, but rejects a returned promise instead // also, can pass in a promise instead of a function t.addAssert('rejects', 4, function (fn_, wanted_, m_, e_, m, e__) { var fn, wanted, e, promise for (var i = 0; i < arguments.length - 1; i++) { var arg = arguments[i] if (typeof arg === 'function') { if (arg === Error || arg.prototype instanceof Error) { wanted = arg } else if (!fn) { fn = arg } } else if (typeof arg === 'string' && arg) { m = arg } else if (arg && typeof arg.then === 'function' && !promise) { promise = arg } else if (typeof arg === 'object') { if (!wanted) { wanted = arg } else { e = arg } } } // Copy local properties of the 'extra' object, like 'skip' etc Object.keys(e__).forEach(function (i) { e[i] = e__[i] }) if (!m) { m = fn && fn.name || 'expect rejected Promise' } if (wanted) { if (wanted instanceof Error) { var w = { message: wanted.message } if (wanted.name) { w.name = wanted.name } // intentionally copying non-local properties, since this // is an Error object, and those are funky. for (i in wanted) { w[i] = wanted[i] } wanted = w m += ': ' + (wanted.name || 'Error') + ' ' + wanted.message e = e || {} if (e !== wanted) { e.wanted = wanted } } } if (!promise && typeof fn !== 'function') { e = e || {} e.todo = true return this.pass(m, e) } if (!promise) promise = fn() if (!promise || typeof promise.then !== 'function') return this.fail(m, e) // have to do as a subtest, because promises are async e.at = stack.at(this.currentAssert) this.test(m, { buffered: true }, function (t) { return promise.then(function (value) { e.found = value t.fail(m, e) }, function (er) { // 'name' is a getter. if (er.name) { er.name = er.name + '' } if (wanted) { if (Object.prototype.toString.call(wanted) === '[object RegExp]') { return t.match(er.message, wanted, m, e) } return t.has(er, wanted, m, e) } else { return t.pass(m, e) } }) }) }) // synonyms are helpful. Object.keys(synonyms).forEach(function (c) { if (t[c]) { synonyms[c].forEach(function (s) { Object.defineProperty(t, s, { value: t[c], enumerable: false, configurable: true, writable: true }) }) } }) }