343 lines
8.1 KiB
JavaScript
343 lines
8.1 KiB
JavaScript
var EventEmitter = require('events').EventEmitter;
|
|
var deepEqual = require('deep-equal');
|
|
var defined = require('defined');
|
|
var path = require('path');
|
|
|
|
module.exports = Test;
|
|
|
|
Test.prototype = new EventEmitter;
|
|
|
|
function Test (name_, opts_, cb_) {
|
|
var name = '(anonymous)';
|
|
var opts = {};
|
|
var cb;
|
|
|
|
for (var i = 0; i < arguments.length; i++) {
|
|
switch (typeof arguments[i]) {
|
|
case 'string':
|
|
name = arguments[i];
|
|
break;
|
|
case 'object':
|
|
opts = arguments[i] || opts;
|
|
break;
|
|
case 'function':
|
|
cb = arguments[i];
|
|
}
|
|
}
|
|
|
|
EventEmitter.call(this);
|
|
|
|
this.name = name || '(anonymous)';
|
|
this.assertCount = 0;
|
|
this._skip = opts.skip || false;
|
|
this._plan = undefined;
|
|
this._cb = cb;
|
|
this._progeny = [];
|
|
this._ok = true;
|
|
}
|
|
|
|
Test.prototype.run = function () {
|
|
if (this._skip) {
|
|
return this.end();
|
|
}
|
|
try {
|
|
this._cb(this);
|
|
}
|
|
catch (err) {
|
|
this.error(err);
|
|
this.end();
|
|
}
|
|
};
|
|
|
|
Test.prototype.test = function (name, opts, cb) {
|
|
var t = new Test(name, opts, cb);
|
|
this._progeny.push(t);
|
|
this.emit('test', t);
|
|
};
|
|
|
|
Test.prototype.comment = function (msg) {
|
|
this.emit('result', msg.trim().replace(/^#\s*/, ''));
|
|
};
|
|
|
|
Test.prototype.plan = function (n) {
|
|
this._plan = n;
|
|
this.emit('plan', n);
|
|
};
|
|
|
|
Test.prototype.end = function () {
|
|
if (!this.ended) this.emit('end');
|
|
if (this._plan !== undefined &&
|
|
!this._planError && this.assertCount !== this._plan) {
|
|
this._planError = true;
|
|
this.fail('plan != count', {
|
|
expected : this._plan,
|
|
actual : this.assertCount
|
|
});
|
|
}
|
|
this.ended = true;
|
|
};
|
|
|
|
Test.prototype._exit = function () {
|
|
if (this._plan !== undefined &&
|
|
!this._planError && this.assertCount !== this._plan) {
|
|
this._planError = true;
|
|
this.fail('plan != count', {
|
|
expected : this._plan,
|
|
actual : this.assertCount
|
|
});
|
|
}
|
|
else if (!this.ended) {
|
|
this.fail('test exited without ending');
|
|
}
|
|
|
|
};
|
|
|
|
Test.prototype._assert = function assert (ok, opts) {
|
|
var self = this;
|
|
var extra = opts.extra || {};
|
|
|
|
var res = {
|
|
id : self.assertCount ++,
|
|
ok : Boolean(ok),
|
|
skip : defined(extra.skip, opts.skip),
|
|
name : defined(extra.message, opts.message, '(unnamed assert)'),
|
|
operator : defined(extra.operator, opts.operator),
|
|
actual : defined(extra.actual, opts.actual),
|
|
expected : defined(extra.expected, opts.expected)
|
|
};
|
|
this._ok = Boolean(this._ok && ok);
|
|
|
|
if (!ok) {
|
|
res.error = defined(extra.error, opts.error, new Error(res.name));
|
|
}
|
|
|
|
var e = new Error('exception');
|
|
var err = (e.stack || '').split('\n');
|
|
var dir = path.dirname(__dirname) + '/';
|
|
|
|
for (var i = 0; i < err.length; i++) {
|
|
var m = /^\s*\bat\s+(.+)/.exec(err[i]);
|
|
if (!m) continue;
|
|
|
|
var s = m[1].split(/\s+/);
|
|
var filem = /(\/[^:\s]+:(\d+)(?::(\d+))?)/.exec(s[1]);
|
|
if (!filem) continue;
|
|
|
|
if (filem[1].slice(0, dir.length) === dir) continue;
|
|
|
|
res.functionName = s[0];
|
|
res.file = filem[1];
|
|
res.line = Number(filem[2]);
|
|
if (filem[3]) res.column = filem[3];
|
|
|
|
res.at = m[1];
|
|
break;
|
|
}
|
|
|
|
self.emit('result', res);
|
|
|
|
if (self._plan === self.assertCount) {
|
|
process.nextTick(function () {
|
|
if (!self.ended) self.end();
|
|
});
|
|
}
|
|
|
|
if (!self._planError && self.assertCount > self._plan) {
|
|
self._planError = true;
|
|
self.fail('plan != count', {
|
|
expected : self._plan,
|
|
actual : self.assertCount
|
|
});
|
|
}
|
|
};
|
|
|
|
Test.prototype.fail = function (msg, extra) {
|
|
this._assert(false, {
|
|
message : msg,
|
|
operator : 'fail',
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.pass = function (msg, extra) {
|
|
this._assert(true, {
|
|
message : msg,
|
|
operator : 'pass',
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.skip = function (msg, extra) {
|
|
this._assert(true, {
|
|
message : msg,
|
|
operator : 'skip',
|
|
skip : true,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.ok
|
|
= Test.prototype['true']
|
|
= Test.prototype.assert
|
|
= function (value, msg, extra) {
|
|
this._assert(value, {
|
|
message : msg,
|
|
operator : 'ok',
|
|
expected : true,
|
|
actual : value,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.notOk
|
|
= Test.prototype['false']
|
|
= Test.prototype.notok
|
|
= function (value, msg, extra) {
|
|
this._assert(!value, {
|
|
message : msg,
|
|
operator : 'notOk',
|
|
expected : false,
|
|
actual : value,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.error
|
|
= Test.prototype.ifError
|
|
= Test.prototype.ifErr
|
|
= Test.prototype.iferror
|
|
= function (err, msg, extra) {
|
|
this._assert(!err, {
|
|
message : defined(msg, String(err)),
|
|
operator : 'error',
|
|
actual : err,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.equal
|
|
= Test.prototype.equals
|
|
= Test.prototype.isEqual
|
|
= Test.prototype.is
|
|
= Test.prototype.strictEqual
|
|
= Test.prototype.strictEquals
|
|
= function (a, b, msg, extra) {
|
|
this._assert(a === b, {
|
|
message : defined(msg, 'should be equal'),
|
|
operator : 'equal',
|
|
actual : a,
|
|
expected : b,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.notEqual
|
|
= Test.prototype.notEquals
|
|
= Test.prototype.notStrictEqual
|
|
= Test.prototype.notStrictEquals
|
|
= Test.prototype.isNotEqual
|
|
= Test.prototype.isNot
|
|
= Test.prototype.not
|
|
= Test.prototype.doesNotEqual
|
|
= Test.prototype.isInequal
|
|
= function (a, b, msg, extra) {
|
|
this._assert(a !== b, {
|
|
message : defined(msg, 'should not be equal'),
|
|
operator : 'notEqual',
|
|
actual : a,
|
|
notExpected : b,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.deepEqual
|
|
= Test.prototype.deepEquals
|
|
= Test.prototype.isEquivalent
|
|
= Test.prototype.looseEqual
|
|
= Test.prototype.looseEquals
|
|
= Test.prototype.same
|
|
= function (a, b, msg, extra) {
|
|
this._assert(deepEqual(a, b), {
|
|
message : defined(msg, 'should be equivalent'),
|
|
operator : 'deepEqual',
|
|
actual : a,
|
|
expected : b,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.notDeepEqual
|
|
= Test.prototype.notEquivalent
|
|
= Test.prototype.notDeeply
|
|
= Test.prototype.notSame
|
|
= Test.prototype.isNotDeepEqual
|
|
= Test.prototype.isNotDeeply
|
|
= Test.prototype.isNotEquivalent
|
|
= Test.prototype.isInequivalent
|
|
= function (a, b, msg, extra) {
|
|
this._assert(!deepEqual(a, b), {
|
|
message : defined(msg, 'should not be equivalent'),
|
|
operator : 'notDeepEqual',
|
|
actual : a,
|
|
notExpected : b,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype['throws'] = function (fn, expected, msg, extra) {
|
|
if (typeof expected === 'string') {
|
|
msg = expected;
|
|
expected = undefined;
|
|
}
|
|
var caught = undefined;
|
|
try {
|
|
fn();
|
|
}
|
|
catch (err) {
|
|
caught = { error : err };
|
|
var message = err.message;
|
|
delete err.message;
|
|
err.message = message;
|
|
}
|
|
|
|
var passed = caught;
|
|
|
|
if (expected instanceof RegExp) {
|
|
passed = expected.test(caught && caught.error);
|
|
expected = String(expected);
|
|
}
|
|
|
|
this._assert(passed, {
|
|
message : defined(msg, 'should throw'),
|
|
operator : 'throws',
|
|
actual : caught && caught.error,
|
|
expected : expected,
|
|
error: !passed && caught && caught.error,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
Test.prototype.doesNotThrow = function (fn, expected, msg, extra) {
|
|
if (typeof expected === 'string') {
|
|
msg = expected;
|
|
expected = undefined;
|
|
}
|
|
var caught = undefined;
|
|
try {
|
|
fn();
|
|
}
|
|
catch (err) {
|
|
caught = { error : err };
|
|
}
|
|
this._assert(!caught, {
|
|
message : defined(msg, 'should throw'),
|
|
operator : 'throws',
|
|
actual : caught && caught.error,
|
|
expected : expected,
|
|
error : caught && caught.error,
|
|
extra : extra
|
|
});
|
|
};
|
|
|
|
// vim: set softtabstop=4 shiftwidth=4:
|