Paul Lewis | 40e2867 | 2020-11-27 09:51:37 | [diff] [blame] | 1 | "use strict"; |
| 2 | |
| 3 | var valueToString = require("@sinonjs/commons").valueToString; |
| 4 | var indexOf = require("@sinonjs/commons").prototypes.string.indexOf; |
| 5 | var forEach = require("@sinonjs/commons").prototypes.array.forEach; |
| 6 | var type = require("type-detect"); |
| 7 | |
| 8 | var engineCanCompareMaps = typeof Array.from === "function"; |
| 9 | var deepEqual = require("./deep-equal").use(match); // eslint-disable-line no-use-before-define |
| 10 | var isArrayType = require("./is-array-type"); |
| 11 | var isSubset = require("./is-subset"); |
| 12 | var createMatcher = require("./create-matcher"); |
| 13 | |
| 14 | /** |
| 15 | * Returns true when `array` contains all of `subset` as defined by the `compare` |
| 16 | * argument |
| 17 | * |
| 18 | * @param {Array} array An array to search for a subset |
| 19 | * @param {Array} subset The subset to find in the array |
| 20 | * @param {Function} compare A comparison function |
| 21 | * @returns {boolean} [description] |
| 22 | * @private |
| 23 | */ |
| 24 | function arrayContains(array, subset, compare) { |
| 25 | if (subset.length === 0) { |
| 26 | return true; |
| 27 | } |
| 28 | var i, l, j, k; |
| 29 | for (i = 0, l = array.length; i < l; ++i) { |
| 30 | if (compare(array[i], subset[0])) { |
| 31 | for (j = 0, k = subset.length; j < k; ++j) { |
| 32 | if (i + j >= l) { |
| 33 | return false; |
| 34 | } |
| 35 | if (!compare(array[i + j], subset[j])) { |
| 36 | return false; |
| 37 | } |
| 38 | } |
| 39 | return true; |
| 40 | } |
| 41 | } |
| 42 | return false; |
| 43 | } |
| 44 | |
| 45 | /* eslint-disable complexity */ |
| 46 | /** |
| 47 | * Matches an object with a matcher (or value) |
| 48 | * |
| 49 | * @alias module:samsam.match |
| 50 | * @param {object} object The object candidate to match |
| 51 | * @param {object} matcherOrValue A matcher or value to match against |
| 52 | * @returns {boolean} true when `object` matches `matcherOrValue` |
| 53 | */ |
| 54 | function match(object, matcherOrValue) { |
| 55 | if (matcherOrValue && typeof matcherOrValue.test === "function") { |
| 56 | return matcherOrValue.test(object); |
| 57 | } |
| 58 | |
| 59 | switch (type(matcherOrValue)) { |
| 60 | case "bigint": |
| 61 | case "boolean": |
| 62 | case "number": |
| 63 | case "symbol": |
| 64 | return matcherOrValue === object; |
| 65 | case "function": |
| 66 | return matcherOrValue(object) === true; |
| 67 | case "string": |
| 68 | var notNull = typeof object === "string" || Boolean(object); |
| 69 | return ( |
| 70 | notNull && |
| 71 | indexOf( |
| 72 | valueToString(object).toLowerCase(), |
| 73 | matcherOrValue.toLowerCase() |
| 74 | ) >= 0 |
| 75 | ); |
| 76 | case "null": |
| 77 | return object === null; |
| 78 | case "undefined": |
| 79 | return typeof object === "undefined"; |
| 80 | case "Date": |
| 81 | /* istanbul ignore else */ |
| 82 | if (type(object) === "Date") { |
| 83 | return object.getTime() === matcherOrValue.getTime(); |
| 84 | } |
| 85 | /* istanbul ignore next: this is basically the rest of the function, which is covered */ |
| 86 | break; |
| 87 | case "Array": |
| 88 | case "Int8Array": |
| 89 | case "Uint8Array": |
| 90 | case "Uint8ClampedArray": |
| 91 | case "Int16Array": |
| 92 | case "Uint16Array": |
| 93 | case "Int32Array": |
| 94 | case "Uint32Array": |
| 95 | case "Float32Array": |
| 96 | case "Float64Array": |
| 97 | return ( |
| 98 | isArrayType(matcherOrValue) && |
| 99 | arrayContains(object, matcherOrValue, match) |
| 100 | ); |
| 101 | case "Map": |
| 102 | /* istanbul ignore next: this is covered by a test, that is only run in IE, but we collect coverage information in node*/ |
| 103 | if (!engineCanCompareMaps) { |
| 104 | throw new Error( |
| 105 | "The JavaScript engine does not support Array.from and cannot reliably do value comparison of Map instances" |
| 106 | ); |
| 107 | } |
| 108 | |
| 109 | return ( |
| 110 | type(object) === "Map" && |
| 111 | arrayContains( |
| 112 | Array.from(object), |
| 113 | Array.from(matcherOrValue), |
| 114 | match |
| 115 | ) |
| 116 | ); |
| 117 | default: |
| 118 | break; |
| 119 | } |
| 120 | |
| 121 | switch (type(object)) { |
| 122 | case "null": |
| 123 | return false; |
| 124 | case "Set": |
| 125 | return isSubset(matcherOrValue, object, match); |
| 126 | default: |
| 127 | break; |
| 128 | } |
| 129 | |
| 130 | /* istanbul ignore else */ |
| 131 | if (matcherOrValue && typeof matcherOrValue === "object") { |
| 132 | if (matcherOrValue === object) { |
| 133 | return true; |
| 134 | } |
| 135 | if (typeof object !== "object") { |
| 136 | return false; |
| 137 | } |
| 138 | var prop; |
| 139 | // eslint-disable-next-line guard-for-in |
| 140 | for (prop in matcherOrValue) { |
| 141 | var value = object[prop]; |
| 142 | if ( |
| 143 | typeof value === "undefined" && |
| 144 | typeof object.getAttribute === "function" |
| 145 | ) { |
| 146 | value = object.getAttribute(prop); |
| 147 | } |
| 148 | if ( |
| 149 | matcherOrValue[prop] === null || |
| 150 | typeof matcherOrValue[prop] === "undefined" |
| 151 | ) { |
| 152 | if (value !== matcherOrValue[prop]) { |
| 153 | return false; |
| 154 | } |
| 155 | } else if ( |
| 156 | typeof value === "undefined" || |
| 157 | !deepEqual(value, matcherOrValue[prop]) |
| 158 | ) { |
| 159 | return false; |
| 160 | } |
| 161 | } |
| 162 | return true; |
| 163 | } |
| 164 | |
| 165 | /* istanbul ignore next */ |
| 166 | throw new Error("Matcher was an unknown or unsupported type"); |
| 167 | } |
| 168 | /* eslint-enable complexity */ |
| 169 | |
Tim van der Lippe | 61fe685 | 2021-09-13 12:21:16 | [diff] [blame^] | 170 | forEach(Object.keys(createMatcher), function (key) { |
Paul Lewis | 40e2867 | 2020-11-27 09:51:37 | [diff] [blame] | 171 | match[key] = createMatcher[key]; |
| 172 | }); |
| 173 | |
| 174 | module.exports = match; |