mirror of
https://codeberg.org/keyoxide/doipjs.git
synced 2024-12-23 23:19:29 -07:00
372 lines
12 KiB
JavaScript
372 lines
12 KiB
JavaScript
|
"use strict";
|
||
|
|
||
|
var arrayProto = require("@sinonjs/commons").prototypes.array;
|
||
|
var deepEqual = require("./deep-equal").use(createMatcher); // eslint-disable-line no-use-before-define
|
||
|
var every = require("@sinonjs/commons").every;
|
||
|
var functionName = require("@sinonjs/commons").functionName;
|
||
|
var get = require("lodash.get");
|
||
|
var iterableToString = require("./iterable-to-string");
|
||
|
var objectProto = require("@sinonjs/commons").prototypes.object;
|
||
|
var typeOf = require("@sinonjs/commons").typeOf;
|
||
|
var valueToString = require("@sinonjs/commons").valueToString;
|
||
|
|
||
|
var assertMatcher = require("./create-matcher/assert-matcher");
|
||
|
var assertMethodExists = require("./create-matcher/assert-method-exists");
|
||
|
var assertType = require("./create-matcher/assert-type");
|
||
|
var isIterable = require("./create-matcher/is-iterable");
|
||
|
var isMatcher = require("./create-matcher/is-matcher");
|
||
|
|
||
|
var matcherPrototype = require("./create-matcher/matcher-prototype");
|
||
|
|
||
|
var arrayIndexOf = arrayProto.indexOf;
|
||
|
var some = arrayProto.some;
|
||
|
|
||
|
var hasOwnProperty = objectProto.hasOwnProperty;
|
||
|
var objectToString = objectProto.toString;
|
||
|
|
||
|
var TYPE_MAP = require("./create-matcher/type-map")(createMatcher); // eslint-disable-line no-use-before-define
|
||
|
|
||
|
/**
|
||
|
* Creates a matcher object for the passed expectation
|
||
|
*
|
||
|
* @alias module:samsam.createMatcher
|
||
|
* @param {*} expectation An expecttation
|
||
|
* @param {string} message A message for the expectation
|
||
|
* @returns {object} A matcher object
|
||
|
*/
|
||
|
function createMatcher(expectation, message) {
|
||
|
var m = Object.create(matcherPrototype);
|
||
|
var type = typeOf(expectation);
|
||
|
|
||
|
if (message !== undefined && typeof message !== "string") {
|
||
|
throw new TypeError("Message should be a string");
|
||
|
}
|
||
|
|
||
|
if (arguments.length > 2) {
|
||
|
throw new TypeError(
|
||
|
"Expected 1 or 2 arguments, received " + arguments.length
|
||
|
);
|
||
|
}
|
||
|
|
||
|
if (type in TYPE_MAP) {
|
||
|
TYPE_MAP[type](m, expectation, message);
|
||
|
} else {
|
||
|
m.test = function(actual) {
|
||
|
return deepEqual(actual, expectation);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
if (!m.message) {
|
||
|
m.message = "match(" + valueToString(expectation) + ")";
|
||
|
}
|
||
|
|
||
|
return m;
|
||
|
}
|
||
|
|
||
|
createMatcher.isMatcher = isMatcher;
|
||
|
|
||
|
createMatcher.any = createMatcher(function() {
|
||
|
return true;
|
||
|
}, "any");
|
||
|
|
||
|
createMatcher.defined = createMatcher(function(actual) {
|
||
|
return actual !== null && actual !== undefined;
|
||
|
}, "defined");
|
||
|
|
||
|
createMatcher.truthy = createMatcher(function(actual) {
|
||
|
return Boolean(actual);
|
||
|
}, "truthy");
|
||
|
|
||
|
createMatcher.falsy = createMatcher(function(actual) {
|
||
|
return !actual;
|
||
|
}, "falsy");
|
||
|
|
||
|
createMatcher.same = function(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
return expectation === actual;
|
||
|
}, "same(" + valueToString(expectation) + ")");
|
||
|
};
|
||
|
|
||
|
createMatcher.in = function(arrayOfExpectations) {
|
||
|
if (typeOf(arrayOfExpectations) !== "array") {
|
||
|
throw new TypeError("array expected");
|
||
|
}
|
||
|
|
||
|
return createMatcher(function(actual) {
|
||
|
return some(arrayOfExpectations, function(expectation) {
|
||
|
return expectation === actual;
|
||
|
});
|
||
|
}, "in(" + valueToString(arrayOfExpectations) + ")");
|
||
|
};
|
||
|
|
||
|
createMatcher.typeOf = function(type) {
|
||
|
assertType(type, "string", "type");
|
||
|
return createMatcher(function(actual) {
|
||
|
return typeOf(actual) === type;
|
||
|
}, 'typeOf("' + type + '")');
|
||
|
};
|
||
|
|
||
|
createMatcher.instanceOf = function(type) {
|
||
|
/* istanbul ignore if */
|
||
|
if (
|
||
|
typeof Symbol === "undefined" ||
|
||
|
typeof Symbol.hasInstance === "undefined"
|
||
|
) {
|
||
|
assertType(type, "function", "type");
|
||
|
} else {
|
||
|
assertMethodExists(
|
||
|
type,
|
||
|
Symbol.hasInstance,
|
||
|
"type",
|
||
|
"[Symbol.hasInstance]"
|
||
|
);
|
||
|
}
|
||
|
return createMatcher(function(actual) {
|
||
|
return actual instanceof type;
|
||
|
}, "instanceOf(" + (functionName(type) || objectToString(type)) + ")");
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Creates a property matcher
|
||
|
*
|
||
|
* @private
|
||
|
* @param {Function} propertyTest A function to test the property against a value
|
||
|
* @param {string} messagePrefix A prefix to use for messages generated by the matcher
|
||
|
* @returns {object} A matcher
|
||
|
*/
|
||
|
function createPropertyMatcher(propertyTest, messagePrefix) {
|
||
|
return function(property, value) {
|
||
|
assertType(property, "string", "property");
|
||
|
var onlyProperty = arguments.length === 1;
|
||
|
var message = messagePrefix + '("' + property + '"';
|
||
|
if (!onlyProperty) {
|
||
|
message += ", " + valueToString(value);
|
||
|
}
|
||
|
message += ")";
|
||
|
return createMatcher(function(actual) {
|
||
|
if (
|
||
|
actual === undefined ||
|
||
|
actual === null ||
|
||
|
!propertyTest(actual, property)
|
||
|
) {
|
||
|
return false;
|
||
|
}
|
||
|
return onlyProperty || deepEqual(actual[property], value);
|
||
|
}, message);
|
||
|
};
|
||
|
}
|
||
|
|
||
|
createMatcher.has = createPropertyMatcher(function(actual, property) {
|
||
|
if (typeof actual === "object") {
|
||
|
return property in actual;
|
||
|
}
|
||
|
return actual[property] !== undefined;
|
||
|
}, "has");
|
||
|
|
||
|
createMatcher.hasOwn = createPropertyMatcher(function(actual, property) {
|
||
|
return hasOwnProperty(actual, property);
|
||
|
}, "hasOwn");
|
||
|
|
||
|
createMatcher.hasNested = function(property, value) {
|
||
|
assertType(property, "string", "property");
|
||
|
var onlyProperty = arguments.length === 1;
|
||
|
var message = 'hasNested("' + property + '"';
|
||
|
if (!onlyProperty) {
|
||
|
message += ", " + valueToString(value);
|
||
|
}
|
||
|
message += ")";
|
||
|
return createMatcher(function(actual) {
|
||
|
if (
|
||
|
actual === undefined ||
|
||
|
actual === null ||
|
||
|
get(actual, property) === undefined
|
||
|
) {
|
||
|
return false;
|
||
|
}
|
||
|
return onlyProperty || deepEqual(get(actual, property), value);
|
||
|
}, message);
|
||
|
};
|
||
|
|
||
|
var jsonParseResultTypes = {
|
||
|
null: true,
|
||
|
boolean: true,
|
||
|
number: true,
|
||
|
string: true,
|
||
|
object: true,
|
||
|
array: true
|
||
|
};
|
||
|
createMatcher.json = function(value) {
|
||
|
if (!jsonParseResultTypes[typeOf(value)]) {
|
||
|
throw new TypeError("Value cannot be the result of JSON.parse");
|
||
|
}
|
||
|
var message = "json(" + JSON.stringify(value, null, " ") + ")";
|
||
|
return createMatcher(function(actual) {
|
||
|
var parsed;
|
||
|
try {
|
||
|
parsed = JSON.parse(actual);
|
||
|
} catch (e) {
|
||
|
return false;
|
||
|
}
|
||
|
return deepEqual(parsed, value);
|
||
|
}, message);
|
||
|
};
|
||
|
|
||
|
createMatcher.every = function(predicate) {
|
||
|
assertMatcher(predicate);
|
||
|
|
||
|
return createMatcher(function(actual) {
|
||
|
if (typeOf(actual) === "object") {
|
||
|
return every(Object.keys(actual), function(key) {
|
||
|
return predicate.test(actual[key]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
isIterable(actual) &&
|
||
|
every(actual, function(element) {
|
||
|
return predicate.test(element);
|
||
|
})
|
||
|
);
|
||
|
}, "every(" + predicate.message + ")");
|
||
|
};
|
||
|
|
||
|
createMatcher.some = function(predicate) {
|
||
|
assertMatcher(predicate);
|
||
|
|
||
|
return createMatcher(function(actual) {
|
||
|
if (typeOf(actual) === "object") {
|
||
|
return !every(Object.keys(actual), function(key) {
|
||
|
return !predicate.test(actual[key]);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
return (
|
||
|
isIterable(actual) &&
|
||
|
!every(actual, function(element) {
|
||
|
return !predicate.test(element);
|
||
|
})
|
||
|
);
|
||
|
}, "some(" + predicate.message + ")");
|
||
|
};
|
||
|
|
||
|
createMatcher.array = createMatcher.typeOf("array");
|
||
|
|
||
|
createMatcher.array.deepEquals = function(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
// Comparing lengths is the fastest way to spot a difference before iterating through every item
|
||
|
var sameLength = actual.length === expectation.length;
|
||
|
return (
|
||
|
typeOf(actual) === "array" &&
|
||
|
sameLength &&
|
||
|
every(actual, function(element, index) {
|
||
|
var expected = expectation[index];
|
||
|
return typeOf(expected) === "array" &&
|
||
|
typeOf(element) === "array"
|
||
|
? createMatcher.array.deepEquals(expected).test(element)
|
||
|
: deepEqual(expected, element);
|
||
|
})
|
||
|
);
|
||
|
}, "deepEquals([" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.array.startsWith = function(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
return (
|
||
|
typeOf(actual) === "array" &&
|
||
|
every(expectation, function(expectedElement, index) {
|
||
|
return actual[index] === expectedElement;
|
||
|
})
|
||
|
);
|
||
|
}, "startsWith([" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.array.endsWith = function(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
// This indicates the index in which we should start matching
|
||
|
var offset = actual.length - expectation.length;
|
||
|
|
||
|
return (
|
||
|
typeOf(actual) === "array" &&
|
||
|
every(expectation, function(expectedElement, index) {
|
||
|
return actual[offset + index] === expectedElement;
|
||
|
})
|
||
|
);
|
||
|
}, "endsWith([" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.array.contains = function(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
return (
|
||
|
typeOf(actual) === "array" &&
|
||
|
every(expectation, function(expectedElement) {
|
||
|
return arrayIndexOf(actual, expectedElement) !== -1;
|
||
|
})
|
||
|
);
|
||
|
}, "contains([" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.map = createMatcher.typeOf("map");
|
||
|
|
||
|
createMatcher.map.deepEquals = function mapDeepEquals(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
// Comparing lengths is the fastest way to spot a difference before iterating through every item
|
||
|
var sameLength = actual.size === expectation.size;
|
||
|
return (
|
||
|
typeOf(actual) === "map" &&
|
||
|
sameLength &&
|
||
|
every(actual, function(element, key) {
|
||
|
return expectation.has(key) && expectation.get(key) === element;
|
||
|
})
|
||
|
);
|
||
|
}, "deepEquals(Map[" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.map.contains = function mapContains(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
return (
|
||
|
typeOf(actual) === "map" &&
|
||
|
every(expectation, function(element, key) {
|
||
|
return actual.has(key) && actual.get(key) === element;
|
||
|
})
|
||
|
);
|
||
|
}, "contains(Map[" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.set = createMatcher.typeOf("set");
|
||
|
|
||
|
createMatcher.set.deepEquals = function setDeepEquals(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
// Comparing lengths is the fastest way to spot a difference before iterating through every item
|
||
|
var sameLength = actual.size === expectation.size;
|
||
|
return (
|
||
|
typeOf(actual) === "set" &&
|
||
|
sameLength &&
|
||
|
every(actual, function(element) {
|
||
|
return expectation.has(element);
|
||
|
})
|
||
|
);
|
||
|
}, "deepEquals(Set[" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.set.contains = function setContains(expectation) {
|
||
|
return createMatcher(function(actual) {
|
||
|
return (
|
||
|
typeOf(actual) === "set" &&
|
||
|
every(expectation, function(element) {
|
||
|
return actual.has(element);
|
||
|
})
|
||
|
);
|
||
|
}, "contains(Set[" + iterableToString(expectation) + "])");
|
||
|
};
|
||
|
|
||
|
createMatcher.bool = createMatcher.typeOf("boolean");
|
||
|
createMatcher.number = createMatcher.typeOf("number");
|
||
|
createMatcher.string = createMatcher.typeOf("string");
|
||
|
createMatcher.object = createMatcher.typeOf("object");
|
||
|
createMatcher.func = createMatcher.typeOf("function");
|
||
|
createMatcher.regexp = createMatcher.typeOf("regexp");
|
||
|
createMatcher.date = createMatcher.typeOf("date");
|
||
|
createMatcher.symbol = createMatcher.typeOf("symbol");
|
||
|
|
||
|
module.exports = createMatcher;
|