Permalink
Cannot retrieve contributors at this time
378 lines (330 sloc)
12 KB
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
codeql-action/node_modules/@sinonjs/samsam/lib/create-matcher.js
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"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)})`; | |
} | |
// ensure that nothing mutates the exported message value, ref https://github.com/sinonjs/sinon/issues/2502 | |
Object.defineProperty(m, "message", { | |
configurable: false, | |
writable: false, | |
value: m.message, | |
}); | |
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; |