| Server IP : 68.183.124.220 / Your IP : 216.73.217.137 Web Server : Apache/2.4.18 (Ubuntu) System : Linux Sandbox-A 4.4.0-210-generic #242-Ubuntu SMP Fri Apr 16 09:57:56 UTC 2021 x86_64 User : gavin ( 1000) PHP Version : 7.0.33-0ubuntu0.16.04.16 Disable Function : pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority, MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /home/gavin/workspace/happymandarin/node_modules/should-equal/es6/ |
Upload File : |
import t from 'should-type';
function format(msg) {
var args = arguments;
for (var i = 1, l = args.length; i < l; i++) {
msg = msg.replace(/%s/, args[i]);
}
return msg;
}
var hasOwnProperty = Object.prototype.hasOwnProperty;
function EqualityFail(a, b, reason, path) {
this.a = a;
this.b = b;
this.reason = reason;
this.path = path;
}
function typeToString(tp) {
return tp.type + (tp.cls ? '(' + tp.cls + (tp.sub ? ' ' + tp.sub : '') + ')' : '');
}
var PLUS_0_AND_MINUS_0 = '+0 is not equal to -0';
var DIFFERENT_TYPES = 'A has type %s and B has type %s';
var EQUALITY = 'A is not equal to B';
var EQUALITY_PROTOTYPE = 'A and B have different prototypes';
var WRAPPED_VALUE = 'A wrapped value is not equal to B wrapped value';
var FUNCTION_SOURCES = 'function A is not equal to B by source code value (via .toString call)';
var MISSING_KEY = '%s has no key %s';
var SET_MAP_MISSING_KEY = 'Set/Map missing key %s';
var DEFAULT_OPTIONS = {
checkProtoEql: true,
checkSubType: true,
plusZeroAndMinusZeroEqual: true,
collectAllFails: false
};
function setBooleanDefault(property, obj, opts, defaults) {
obj[property] = typeof opts[property] !== 'boolean' ? defaults[property] : opts[property];
}
var METHOD_PREFIX = '_check_';
function EQ(opts, a, b, path) {
opts = opts || {};
setBooleanDefault('checkProtoEql', this, opts, DEFAULT_OPTIONS);
setBooleanDefault('plusZeroAndMinusZeroEqual', this, opts, DEFAULT_OPTIONS);
setBooleanDefault('checkSubType', this, opts, DEFAULT_OPTIONS);
setBooleanDefault('collectAllFails', this, opts, DEFAULT_OPTIONS);
this.a = a;
this.b = b;
this._meet = opts._meet || [];
this.fails = opts.fails || [];
this.path = path || [];
}
function ShortcutError(fail) {
this.name = 'ShortcutError';
this.message = 'fail fast';
this.fail = fail;
}
ShortcutError.prototype = Object.create(Error.prototype);
EQ.checkStrictEquality = function(a, b) {
this.collectFail(a !== b, EQUALITY);
};
EQ.add = function add(type, cls, sub, f) {
var args = Array.prototype.slice.call(arguments);
f = args.pop();
EQ.prototype[METHOD_PREFIX + args.join('_')] = f;
};
EQ.prototype = {
check: function() {
try {
this.check0();
} catch (e) {
if (e instanceof ShortcutError) {
return [e.fail];
}
throw e;
}
return this.fails;
},
check0: function() {
var a = this.a;
var b = this.b;
// equal a and b exit early
if (a === b) {
// check for +0 !== -0;
return this.collectFail(a === 0 && (1 / a !== 1 / b) && !this.plusZeroAndMinusZeroEqual, PLUS_0_AND_MINUS_0);
}
var typeA = t(a);
var typeB = t(b);
// if objects has different types they are not equal
if (typeA.type !== typeB.type || typeA.cls !== typeB.cls || typeA.sub !== typeB.sub) {
return this.collectFail(true, format(DIFFERENT_TYPES, typeToString(typeA), typeToString(typeB)));
}
// as types the same checks type specific things
var name1 = typeA.type, name2 = typeA.type;
if (typeA.cls) {
name1 += '_' + typeA.cls;
name2 += '_' + typeA.cls;
}
if (typeA.sub) {
name2 += '_' + typeA.sub;
}
var f = this[METHOD_PREFIX + name2] || this[METHOD_PREFIX + name1] || this[METHOD_PREFIX + typeA.type] || this.defaultCheck;
f.call(this, this.a, this.b);
},
collectFail: function(comparison, reason, showReason) {
if (comparison) {
var res = new EqualityFail(this.a, this.b, reason, this.path);
res.showReason = !!showReason;
this.fails.push(res);
if (!this.collectAllFails) {
throw new ShortcutError(res);
}
}
},
checkPlainObjectsEquality: function(a, b) {
// compare deep objects and arrays
// stacks contain references only
//
var meet = this._meet;
var m = this._meet.length;
while (m--) {
var st = meet[m];
if (st[0] === a && st[1] === b) {
return;
}
}
// add `a` and `b` to the stack of traversed objects
meet.push([a, b]);
// TODO maybe something else like getOwnPropertyNames
var key;
for (key in b) {
if (hasOwnProperty.call(b, key)) {
if (hasOwnProperty.call(a, key)) {
this.checkPropertyEquality(key);
} else {
this.collectFail(true, format(MISSING_KEY, 'A', key));
}
}
}
// ensure both objects have the same number of properties
for (key in a) {
if (hasOwnProperty.call(a, key)) {
this.collectFail(!hasOwnProperty.call(b, key), format(MISSING_KEY, 'B', key));
}
}
meet.pop();
if (this.checkProtoEql) {
//TODO should i check prototypes for === or use eq?
this.collectFail(Object.getPrototypeOf(a) !== Object.getPrototypeOf(b), EQUALITY_PROTOTYPE, true);
}
},
checkPropertyEquality: function(propertyName) {
var _eq = new EQ(this, this.a[propertyName], this.b[propertyName], this.path.concat([propertyName]));
_eq.check0();
},
defaultCheck: EQ.checkStrictEquality
};
EQ.add(t.NUMBER, function(a, b) {
this.collectFail((a !== a && b === b) || (b !== b && a === a) || (a !== b && a === a && b === b), EQUALITY);
});
[t.SYMBOL, t.BOOLEAN, t.STRING].forEach(function(tp) {
EQ.add(tp, EQ.checkStrictEquality);
});
EQ.add(t.FUNCTION, function(a, b) {
// functions are compared by their source code
this.collectFail(a.toString() !== b.toString(), FUNCTION_SOURCES);
// check user properties
this.checkPlainObjectsEquality(a, b);
});
EQ.add(t.OBJECT, t.REGEXP, function(a, b) {
// check regexp flags
var flags = ['source', 'global', 'multiline', 'lastIndex', 'ignoreCase', 'sticky', 'unicode'];
while (flags.length) {
this.checkPropertyEquality(flags.shift());
}
// check user properties
this.checkPlainObjectsEquality(a, b);
});
EQ.add(t.OBJECT, t.DATE, function(a, b) {
//check by timestamp only (using .valueOf)
this.collectFail(+a !== +b, EQUALITY);
// check user properties
this.checkPlainObjectsEquality(a, b);
});
[t.NUMBER, t.BOOLEAN, t.STRING].forEach(function(tp) {
EQ.add(t.OBJECT, tp, function(a, b) {
//primitive type wrappers
this.collectFail(a.valueOf() !== b.valueOf(), WRAPPED_VALUE);
// check user properties
this.checkPlainObjectsEquality(a, b);
});
});
EQ.add(t.OBJECT, function(a, b) {
this.checkPlainObjectsEquality(a, b);
});
[t.ARRAY, t.ARGUMENTS, t.TYPED_ARRAY].forEach(function(tp) {
EQ.add(t.OBJECT, tp, function(a, b) {
this.checkPropertyEquality('length');
this.checkPlainObjectsEquality(a, b);
});
});
EQ.add(t.OBJECT, t.ARRAY_BUFFER, function(a, b) {
this.checkPropertyEquality('byteLength');
this.checkPlainObjectsEquality(a, b);
});
EQ.add(t.OBJECT, t.ERROR, function(a, b) {
this.checkPropertyEquality('name');
this.checkPropertyEquality('message');
this.checkPlainObjectsEquality(a, b);
});
EQ.add(t.OBJECT, t.BUFFER, function(a) {
this.checkPropertyEquality('length');
var l = a.length;
while (l--) {
this.checkPropertyEquality(l);
}
//we do not check for user properties because
//node Buffer have some strange hidden properties
});
[t.MAP, t.SET, t.WEAK_MAP, t.WEAK_SET].forEach(function(tp) {
EQ.add(t.OBJECT, tp, function(a, b) {
this._meet.push([a, b]);
var iteratorA = a.entries();
for (var nextA = iteratorA.next(); !nextA.done; nextA = iteratorA.next()) {
var iteratorB = b.entries();
var keyFound = false;
for (var nextB = iteratorB.next(); !nextB.done; nextB = iteratorB.next()) {
// try to check keys first
var r = eq(nextA.value[0], nextB.value[0], { collectAllFails: false, _meet: this._meet });
if (r.length === 0) {
keyFound = true;
// check values also
eq(nextA.value[1], nextB.value[1], this);
}
}
if (!keyFound) {
// no such key at all
this.collectFail(true, format(SET_MAP_MISSING_KEY, nextA.value[0]));
}
}
this._meet.pop();
this.checkPlainObjectsEquality(a, b);
});
});
function eq(a, b, opts) {
return new EQ(opts, a, b).check();
}
eq.EQ = EQ;
export default eq;