diff --git a/.commitlintrc.js b/.commitlintrc.js new file mode 100644 index 0000000000..a8a92f0efc --- /dev/null +++ b/.commitlintrc.js @@ -0,0 +1,29 @@ +'use strict'; + +module.exports = { + extends: [ + '@commitlint/config-conventional', // scoped packages are not prefixed + ], + rules: { + 'type-enum': [ + 2, + 'always', + [ + 'build', + 'chore', + 'ci', + 'docs', + 'feat', + 'fix', + 'perf', + 'proposal', + 'refactor', + 'release', + 'revert', + 'style', + 'test', + 'wip', + ], + ], + }, +}; diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000000..e6c4d17ade --- /dev/null +++ b/.eslintignore @@ -0,0 +1,5 @@ +coverage/ +coverage-merged/ +dist/ +node_modules/ +types/ diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000000..32cdcc6cc4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,99 @@ +{ + "extends": ["airbnb-base", "prettier"], + "root": true, + "env": { + "amd": true, + "browser": true, + "es6": true, + "jest": true, + "node": true + }, + "globals": { + "BigInt": "readonly", + "BigInt64Array": "readonly", + "BigUint64Array": "readonly", + "globalThis": "readonly" + }, + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "plugins": [ + "prettier" + ], + "rules": { + "prettier/prettier": "error", + // Overridden + "camelcase": ["error", { "properties": "never", "allow": [ "W[0-9]+_"] }], + "import/extensions": "off", + "no-eval": "off", + "no-self-compare": "off", + "one-var": ["error", "never"], + // @TODO: Fix the following rules progressively. + "arrow-body-style": "warn", + "prefer-arrow-callback": "warn", + "prefer-object-spread": "off", + "max-classes-per-file": "off", + "dot-notation": "off", + "object-shorthand": "off", + "no-param-reassign": "off", + "no-cond-assign": "off", + "prefer-destructuring": "off", + "func-names": "off", + "no-nested-ternary": "off", + "no-plusplus": "off", + "strict": "off", + "no-restricted-syntax": "off", + "import/no-mutable-exports": "off", + "guard-for-in": "off", + "import/prefer-default-export": "off", + "prefer-rest-params": "off", + "prefer-spread": "off", + "no-lonely-if": "off", + "no-prototype-builtins": "off", + "no-continue": "off", + "no-shadow": "off", + // Rules up for discussion. + "no-multi-assign": "off", + "new-cap": "off" + }, + "overrides": [ + { + "files": ["**/*.ts"], + "parserOptions": { + "project": "./tsconfig.json" + }, + "extends": [ + "airbnb-typescript/base", + "prettier" + ], + "plugins": ["@typescript-eslint"], + "rules": { + "@typescript-eslint/no-unused-vars": [ + "warn", + { + "vars": "all", + "args": "after-used", + "ignoreRestSiblings": true, + "argsIgnorePattern": "^_" // For tsc compatibility. + } + ], + "comma-dangle": "off", + "implicit-arrow-linebreak": "off", // Conflicts with prettier. + "import/extensions": "off", + "import/prefer-default-export": "off", + "operator-linebreak": "off", + "object-curly-newline": "off", + "prefer-rest-params": "off", // We need to use params. + "prettier/prettier": "error", + "@typescript-eslint/no-shadow": "warn", + "@typescript-eslint/no-use-before-define": ["warn", { "functions": false }], + "import/no-cycle": "warn", + "no-bitwise": "off", + "no-unsafe-finally": "warn", + "no-param-reassign": "off", + "no-shadow": "warn" + } + } + ] +} diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 8952c5779c..0000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,125 +0,0 @@ -module.exports = { - 'extends': ['plugin:import/errors'], - 'plugins': ['import'], - 'env': { - 'es6': true, - 'node': true - }, - 'parserOptions': { - 'ecmaVersion': 6, - 'sourceType': 'module', - 'ecmaFeatures': { - 'impliedStrict': true, - 'objectLiteralDuplicateProperties': false - } - }, - 'rules': { - 'array-bracket-spacing': ['error', 'never'], - - 'camelcase': ['error', { - 'properties': 'never' - }], - - 'comma-dangle': ['error', 'never'], - - 'curly': ['error', 'all'], - - 'eol-last': ['error'], - - 'indent': ['error', 2, { - 'SwitchCase': 1 - }], - - 'keyword-spacing': ['error'], - - 'max-len': ['error', { - 'code': 180, - 'ignoreComments': true, - 'ignoreRegExpLiterals': true - }], - - 'no-else-return': ['error'], - - 'no-mixed-spaces-and-tabs': ['error'], - - 'no-multiple-empty-lines': ['error'], - - 'no-spaced-func': ['error'], - - 'no-trailing-spaces': ['error'], - - 'no-undef': ['error'], - - 'no-unexpected-multiline': ['error'], - - 'no-unused-vars': ['error', { - 'args': 'none', - 'vars': 'all' - }], - - 'quotes': ['error', 'single', { - 'allowTemplateLiterals': true, - 'avoidEscape': true - }], - - 'semi': ['error', 'never'], - - 'space-before-blocks': ['error', 'always'], - - 'space-before-function-paren': ['error', 'never'], - - 'space-in-parens': ['error', 'never'], - - 'space-unary-ops': ['error', { - 'nonwords': false, - 'overrides': {} - }], - - // 'valid-jsdoc': ['error'] - - // ECMAScript 6 rules - - 'arrow-body-style': ['error', 'as-needed', { - 'requireReturnForObjectLiteral': false - }], - - 'arrow-parens': ['error', 'always'], - - 'arrow-spacing': ['error', { - 'after': true, - 'before': true - }], - - 'no-class-assign': ['error'], - - 'no-const-assign': ['error'], - - 'no-dupe-class-members': ['error'], - - 'no-duplicate-imports': ['error'], - - 'no-new-symbol': ['error'], - - 'no-useless-rename': ['error'], - - 'no-var': ['error'], - - 'object-shorthand': ['error', 'always', { - 'avoidQuotes': true, - 'ignoreConstructors': false - }], - - 'prefer-arrow-callback': ['error', { - 'allowNamedFunctions': false, - 'allowUnboundThis': true - }], - - 'prefer-const': ['error'], - - 'prefer-rest-params': ['error'], - - 'prefer-template': ['error'], - - 'template-curly-spacing': ['error', 'never'] - } -}; diff --git a/.gitattributes b/.gitattributes index 176a458f94..6f28e15bf4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ * text=auto +*.lockb binary diff=lockb diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2beeff8a5c..27ac1ac0a6 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,19 +1,16 @@ -name: Node.js CI - -on: [push, pull_request] +name: CI +on: + pull_request: + branches: [ main ] + push: + branches: [ main ] jobs: tests: runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14.x] - steps: - - uses: actions/checkout@v2 - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v1 - with: - node-version: ${{ matrix.node-version }} - - run: npm cit + - uses: actions/checkout@v3 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: 1.0.2 + - run: bun install diff --git a/.gitignore b/.gitignore index 14ec97ff6b..7e32d1977c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ .DS_Store *.log* -doc/*.html -node_modules +dist/ +node_modules/ *.code-workspace +*.lockb *.sublime-project *.sublime-workspace diff --git a/.husky/.gitignore b/.husky/.gitignore new file mode 100644 index 0000000000..31354ec138 --- /dev/null +++ b/.husky/.gitignore @@ -0,0 +1 @@ +_ diff --git a/.husky/commit-msg b/.husky/commit-msg new file mode 100755 index 0000000000..d6a1f73354 --- /dev/null +++ b/.husky/commit-msg @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +bun run commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit new file mode 100755 index 0000000000..d28fd9d2c5 --- /dev/null +++ b/.husky/pre-commit @@ -0,0 +1,4 @@ +#!/bin/sh +. "$(dirname "$0")/_/husky.sh" + +bun run lint-staged diff --git a/.internal/ListCache.js b/.internal/ListCache.js deleted file mode 100644 index f3015ddb12..0000000000 --- a/.internal/ListCache.js +++ /dev/null @@ -1,103 +0,0 @@ -import assocIndexOf from './assocIndexOf.js' - -class ListCache { - - /** - * Creates an list cache object. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - constructor(entries) { - let index = -1 - const length = entries == null ? 0 : entries.length - - this.clear() - while (++index < length) { - const entry = entries[index] - this.set(entry[0], entry[1]) - } - } - - /** - * Removes all key-value entries from the list cache. - * - * @memberOf ListCache - */ - clear() { - this.__data__ = [] - this.size = 0 - } - - /** - * Removes `key` and its value from the list cache. - * - * @memberOf ListCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - delete(key) { - const data = this.__data__ - const index = assocIndexOf(data, key) - - if (index < 0) { - return false - } - const lastIndex = data.length - 1 - if (index == lastIndex) { - data.pop() - } else { - data.splice(index, 1) - } - --this.size - return true - } - - /** - * Gets the list cache value for `key`. - * - * @memberOf ListCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - get(key) { - const data = this.__data__ - const index = assocIndexOf(data, key) - return index < 0 ? undefined : data[index][1] - } - - /** - * Checks if a list cache value for `key` exists. - * - * @memberOf ListCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - has(key) { - return assocIndexOf(this.__data__, key) > -1 - } - - /** - * Sets the list cache `key` to `value`. - * - * @memberOf ListCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the list cache instance. - */ - set(key, value) { - const data = this.__data__ - const index = assocIndexOf(data, key) - - if (index < 0) { - ++this.size - data.push([key, value]) - } else { - data[index][1] = value - } - return this - } -} - -export default ListCache diff --git a/.internal/MapCache.js b/.internal/MapCache.js deleted file mode 100644 index 706e17e132..0000000000 --- a/.internal/MapCache.js +++ /dev/null @@ -1,120 +0,0 @@ - -import Hash from './Hash.js' - -/** - * Gets the data for `map`. - * - * @private - * @param {Object} map The map to query. - * @param {string} key The reference key. - * @returns {*} Returns the map data. - */ -function getMapData({ __data__ }, key) { - const data = __data__ - return isKeyable(key) - ? data[typeof key === 'string' ? 'string' : 'hash'] - : data.map -} - -/** - * Checks if `value` is suitable for use as unique object key. - * - * @private - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is suitable, else `false`. - */ -function isKeyable(value) { - const type = typeof value - return (type === 'string' || type === 'number' || type === 'symbol' || type === 'boolean') - ? (value !== '__proto__') - : (value === null) -} - -class MapCache { - - /** - * Creates a map cache object to store key-value pairs. - * - * @private - * @constructor - * @param {Array} [entries] The key-value pairs to cache. - */ - constructor(entries) { - let index = -1 - const length = entries == null ? 0 : entries.length - - this.clear() - while (++index < length) { - const entry = entries[index] - this.set(entry[0], entry[1]) - } - } - - /** - * Removes all key-value entries from the map. - * - * @memberOf MapCache - */ - clear() { - this.size = 0 - this.__data__ = { - 'hash': new Hash, - 'map': new Map, - 'string': new Hash - } - } - - /** - * Removes `key` and its value from the map. - * - * @memberOf MapCache - * @param {string} key The key of the value to remove. - * @returns {boolean} Returns `true` if the entry was removed, else `false`. - */ - delete(key) { - const result = getMapData(this, key)['delete'](key) - this.size -= result ? 1 : 0 - return result - } - - /** - * Gets the map value for `key`. - * - * @memberOf MapCache - * @param {string} key The key of the value to get. - * @returns {*} Returns the entry value. - */ - get(key) { - return getMapData(this, key).get(key) - } - - /** - * Checks if a map value for `key` exists. - * - * @memberOf MapCache - * @param {string} key The key of the entry to check. - * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. - */ - has(key) { - return getMapData(this, key).has(key) - } - - /** - * Sets the map `key` to `value`. - * - * @memberOf MapCache - * @param {string} key The key of the value to set. - * @param {*} value The value to set. - * @returns {Object} Returns the map cache instance. - */ - set(key, value) { - const data = getMapData(this, key) - const size = data.size - - data.set(key, value) - this.size += data.size == size ? 0 : 1 - return this - } -} - -export default MapCache diff --git a/.internal/baseAssignValue.js b/.internal/baseAssignValue.js deleted file mode 100644 index 7b630c321b..0000000000 --- a/.internal/baseAssignValue.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * The base implementation of `assignValue` and `assignMergeValue` without - * value checks. - * - * @private - * @param {Object} object The object to modify. - * @param {string} key The key of the property to assign. - * @param {*} value The value to assign. - */ -function baseAssignValue(object, key, value) { - if (key == '__proto__') { - Object.defineProperty(object, key, { - 'configurable': true, - 'enumerable': true, - 'value': value, - 'writable': true - }) - } else { - object[key] = value - } -} - -export default baseAssignValue diff --git a/.internal/baseClone.js b/.internal/baseClone.js deleted file mode 100644 index a7157e114d..0000000000 --- a/.internal/baseClone.js +++ /dev/null @@ -1,241 +0,0 @@ -import Stack from './Stack.js' -import arrayEach from './arrayEach.js' -import assignValue from './assignValue.js' -import cloneBuffer from './cloneBuffer.js' -import copyArray from './copyArray.js' -import copyObject from './copyObject.js' -import cloneArrayBuffer from './cloneArrayBuffer.js' -import cloneDataView from './cloneDataView.js' -import cloneRegExp from './cloneRegExp.js' -import cloneSymbol from './cloneSymbol.js' -import cloneTypedArray from './cloneTypedArray.js' -import copySymbols from './copySymbols.js' -import copySymbolsIn from './copySymbolsIn.js' -import getAllKeys from './getAllKeys.js' -import getAllKeysIn from './getAllKeysIn.js' -import getTag from './getTag.js' -import initCloneObject from './initCloneObject.js' -import isBuffer from '../isBuffer.js' -import isObject from '../isObject.js' -import isTypedArray from '../isTypedArray.js' -import keys from '../keys.js' -import keysIn from '../keysIn.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 -const CLONE_FLAT_FLAG = 2 -const CLONE_SYMBOLS_FLAG = 4 - -/** `Object#toString` result references. */ -const argsTag = '[object Arguments]' -const arrayTag = '[object Array]' -const boolTag = '[object Boolean]' -const dateTag = '[object Date]' -const errorTag = '[object Error]' -const mapTag = '[object Map]' -const numberTag = '[object Number]' -const objectTag = '[object Object]' -const regexpTag = '[object RegExp]' -const setTag = '[object Set]' -const stringTag = '[object String]' -const symbolTag = '[object Symbol]' -const weakMapTag = '[object WeakMap]' - -const arrayBufferTag = '[object ArrayBuffer]' -const dataViewTag = '[object DataView]' -const float32Tag = '[object Float32Array]' -const float64Tag = '[object Float64Array]' -const int8Tag = '[object Int8Array]' -const int16Tag = '[object Int16Array]' -const int32Tag = '[object Int32Array]' -const uint8Tag = '[object Uint8Array]' -const uint8ClampedTag = '[object Uint8ClampedArray]' -const uint16Tag = '[object Uint16Array]' -const uint32Tag = '[object Uint32Array]' - -/** Used to identify `toStringTag` values supported by `clone`. */ -const cloneableTags = {} -cloneableTags[argsTag] = cloneableTags[arrayTag] = -cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = -cloneableTags[boolTag] = cloneableTags[dateTag] = -cloneableTags[float32Tag] = cloneableTags[float64Tag] = -cloneableTags[int8Tag] = cloneableTags[int16Tag] = -cloneableTags[int32Tag] = cloneableTags[mapTag] = -cloneableTags[numberTag] = cloneableTags[objectTag] = -cloneableTags[regexpTag] = cloneableTags[setTag] = -cloneableTags[stringTag] = cloneableTags[symbolTag] = -cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = -cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true -cloneableTags[errorTag] = cloneableTags[weakMapTag] = false - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * Initializes an object clone based on its `toStringTag`. - * - * **Note:** This function only supports cloning values with tags of - * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. - * - * @private - * @param {Object} object The object to clone. - * @param {string} tag The `toStringTag` of the object to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Object} Returns the initialized clone. - */ -function initCloneByTag(object, tag, isDeep) { - const Ctor = object.constructor - switch (tag) { - case arrayBufferTag: - return cloneArrayBuffer(object) - - case boolTag: - case dateTag: - return new Ctor(+object) - - case dataViewTag: - return cloneDataView(object, isDeep) - - case float32Tag: case float64Tag: - case int8Tag: case int16Tag: case int32Tag: - case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: - return cloneTypedArray(object, isDeep) - - case mapTag: - return new Ctor - - case numberTag: - case stringTag: - return new Ctor(object) - - case regexpTag: - return cloneRegExp(object) - - case setTag: - return new Ctor - - case symbolTag: - return cloneSymbol(object) - } -} - -/** - * Initializes an array clone. - * - * @private - * @param {Array} array The array to clone. - * @returns {Array} Returns the initialized clone. - */ -function initCloneArray(array) { - const { length } = array - const result = new array.constructor(length) - - // Add properties assigned by `RegExp#exec`. - if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) { - result.index = array.index - result.input = array.input - } - return result -} - -/** - * The base implementation of `clone` and `cloneDeep` which tracks - * traversed objects. - * - * @private - * @param {*} value The value to clone. - * @param {number} bitmask The bitmask flags. - * 1 - Deep clone - * 2 - Flatten inherited properties - * 4 - Clone symbols - * @param {Function} [customizer] The function to customize cloning. - * @param {string} [key] The key of `value`. - * @param {Object} [object] The parent object of `value`. - * @param {Object} [stack] Tracks traversed objects and their clone counterparts. - * @returns {*} Returns the cloned value. - */ -function baseClone(value, bitmask, customizer, key, object, stack) { - let result - const isDeep = bitmask & CLONE_DEEP_FLAG - const isFlat = bitmask & CLONE_FLAT_FLAG - const isFull = bitmask & CLONE_SYMBOLS_FLAG - - if (customizer) { - result = object ? customizer(value, key, object, stack) : customizer(value) - } - if (result !== undefined) { - return result - } - if (!isObject(value)) { - return value - } - const isArr = Array.isArray(value) - const tag = getTag(value) - if (isArr) { - result = initCloneArray(value) - if (!isDeep) { - return copyArray(value, result) - } - } else { - const isFunc = typeof value === 'function' - - if (isBuffer(value)) { - return cloneBuffer(value, isDeep) - } - if (tag == objectTag || tag == argsTag || (isFunc && !object)) { - result = (isFlat || isFunc) ? {} : initCloneObject(value) - if (!isDeep) { - return isFlat - ? copySymbolsIn(value, copyObject(value, keysIn(value), result)) - : copySymbols(value, Object.assign(result, value)) - } - } else { - if (isFunc || !cloneableTags[tag]) { - return object ? value : {} - } - result = initCloneByTag(value, tag, isDeep) - } - } - // Check for circular references and return its corresponding clone. - stack || (stack = new Stack) - const stacked = stack.get(value) - if (stacked) { - return stacked - } - stack.set(value, result) - - if (tag == mapTag) { - value.forEach((subValue, key) => { - result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)) - }) - return result - } - - if (tag == setTag) { - value.forEach((subValue) => { - result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)) - }) - return result - } - - if (isTypedArray(value)) { - return result - } - - const keysFunc = isFull - ? (isFlat ? getAllKeysIn : getAllKeys) - : (isFlat ? keysIn : keys) - - const props = isArr ? undefined : keysFunc(value) - arrayEach(props || value, (subValue, key) => { - if (props) { - key = subValue - subValue = value[key] - } - // Recursively populate clone (susceptible to call stack limits). - assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)) - }) - return result -} - -export default baseClone diff --git a/.internal/baseGet.js b/.internal/baseGet.js deleted file mode 100644 index d0bc3d43ca..0000000000 --- a/.internal/baseGet.js +++ /dev/null @@ -1,24 +0,0 @@ -import castPath from './castPath.js' -import toKey from './toKey.js' - -/** - * The base implementation of `get` without support for default values. - * - * @private - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @returns {*} Returns the resolved value. - */ -function baseGet(object, path) { - path = castPath(path, object) - - let index = 0 - const length = path.length - - while (object != null && index < length) { - object = object[toKey(path[index++])] - } - return (index && index == length) ? object : undefined -} - -export default baseGet diff --git a/.internal/baseIsEqualDeep.js b/.internal/baseIsEqualDeep.js deleted file mode 100644 index 27749cd016..0000000000 --- a/.internal/baseIsEqualDeep.js +++ /dev/null @@ -1,79 +0,0 @@ -import Stack from './Stack.js' -import equalArrays from './equalArrays.js' -import equalByTag from './equalByTag.js' -import equalObjects from './equalObjects.js' -import getTag from './getTag.js' -import isBuffer from '../isBuffer.js' -import isTypedArray from '../isTypedArray.js' - -/** Used to compose bitmasks for value comparisons. */ -const COMPARE_PARTIAL_FLAG = 1 - -/** `Object#toString` result references. */ -const argsTag = '[object Arguments]' -const arrayTag = '[object Array]' -const objectTag = '[object Object]' - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * A specialized version of `baseIsEqual` for arrays and objects which performs - * deep comparisons and tracks traversed objects enabling objects with circular - * references to be compared. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} [stack] Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ -function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { - let objIsArr = Array.isArray(object) - const othIsArr = Array.isArray(other) - let objTag = objIsArr ? arrayTag : getTag(object) - let othTag = othIsArr ? arrayTag : getTag(other) - - objTag = objTag == argsTag ? objectTag : objTag - othTag = othTag == argsTag ? objectTag : othTag - - let objIsObj = objTag == objectTag - const othIsObj = othTag == objectTag - const isSameTag = objTag == othTag - - if (isSameTag && isBuffer(object)) { - if (!isBuffer(other)) { - return false - } - objIsArr = true - objIsObj = false - } - if (isSameTag && !objIsObj) { - stack || (stack = new Stack) - return (objIsArr || isTypedArray(object)) - ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) - : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack) - } - if (!(bitmask & COMPARE_PARTIAL_FLAG)) { - const objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__') - const othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__') - - if (objIsWrapped || othIsWrapped) { - const objUnwrapped = objIsWrapped ? object.value() : object - const othUnwrapped = othIsWrapped ? other.value() : other - - stack || (stack = new Stack) - return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack) - } - } - if (!isSameTag) { - return false - } - stack || (stack = new Stack) - return equalObjects(object, other, bitmask, customizer, equalFunc, stack) -} - -export default baseIsEqualDeep diff --git a/.internal/baseSet.js b/.internal/baseSet.js deleted file mode 100644 index 92bad879fd..0000000000 --- a/.internal/baseSet.js +++ /dev/null @@ -1,48 +0,0 @@ -import assignValue from './assignValue.js' -import castPath from './castPath.js' -import isIndex from './isIndex.js' -import isObject from '../isObject.js' -import toKey from './toKey.js' - -/** - * The base implementation of `set`. - * - * @private - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize path creation. - * @returns {Object} Returns `object`. - */ -function baseSet(object, path, value, customizer) { - if (!isObject(object)) { - return object - } - path = castPath(path, object) - - const length = path.length - const lastIndex = length - 1 - - let index = -1 - let nested = object - - while (nested != null && ++index < length) { - const key = toKey(path[index]) - let newValue = value - - if (index != lastIndex) { - const objValue = nested[key] - newValue = customizer ? customizer(objValue, key, nested) : undefined - if (newValue === undefined) { - newValue = isObject(objValue) - ? objValue - : (isIndex(path[index + 1]) ? [] : {}) - } - } - assignValue(nested, key, newValue) - nested = nested[key] - } - return object -} - -export default baseSet diff --git a/.internal/baseSortedIndexBy.js b/.internal/baseSortedIndexBy.js deleted file mode 100644 index 4f3f84913e..0000000000 --- a/.internal/baseSortedIndexBy.js +++ /dev/null @@ -1,65 +0,0 @@ -import isSymbol from '../isSymbol.js' - -/** Used as references for the maximum length and index of an array. */ -const MAX_ARRAY_LENGTH = 4294967295 -const MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1 - -/** - * The base implementation of `sortedIndexBy` and `sortedLastIndexBy` - * which invokes `iteratee` for `value` and each element of `array` to compute - * their sort ranking. The iteratee is invoked with one argument (value). - * - * @private - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The iteratee invoked per element. - * @param {boolean} [retHighest] Specify returning the highest qualified index. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - */ -function baseSortedIndexBy(array, value, iteratee, retHighest) { - let low = 0 - let high = array == null ? 0 : array.length - if (high == 0) { - return 0 - } - - value = iteratee(value) - - const valIsNaN = value !== value - const valIsNull = value === null - const valIsSymbol = isSymbol(value) - const valIsUndefined = value === undefined - - while (low < high) { - let setLow - const mid = Math.floor((low + high) / 2) - const computed = iteratee(array[mid]) - const othIsDefined = computed !== undefined - const othIsNull = computed === null - const othIsReflexive = computed === computed - const othIsSymbol = isSymbol(computed) - - if (valIsNaN) { - setLow = retHighest || othIsReflexive - } else if (valIsUndefined) { - setLow = othIsReflexive && (retHighest || othIsDefined) - } else if (valIsNull) { - setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull) - } else if (valIsSymbol) { - setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol) - } else if (othIsNull || othIsSymbol) { - setLow = false - } else { - setLow = retHighest ? (computed <= value) : (computed < value) - } - if (setLow) { - low = mid + 1 - } else { - high = mid - } - } - return Math.min(high, MAX_ARRAY_INDEX) -} - -export default baseSortedIndexBy diff --git a/.internal/baseXor.js b/.internal/baseXor.js deleted file mode 100644 index 41493ac6fb..0000000000 --- a/.internal/baseXor.js +++ /dev/null @@ -1,36 +0,0 @@ -import baseDifference from './baseDifference.js' -import baseFlatten from './baseFlatten.js' -import baseUniq from './baseUniq.js' - -/** - * The base implementation of methods like `xor` which accepts an array of - * arrays to inspect. - * - * @private - * @param {Array} arrays The arrays to inspect. - * @param {Function} [iteratee] The iteratee invoked per element. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of values. - */ -function baseXor(arrays, iteratee, comparator) { - const length = arrays.length - if (length < 2) { - return length ? baseUniq(arrays[0]) : [] - } - let index = -1 - const result = new Array(length) - - while (++index < length) { - const array = arrays[index] - let othIndex = -1 - - while (++othIndex < length) { - if (othIndex != index) { - result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator) - } - } - } - return baseUniq(baseFlatten(result, 1), iteratee, comparator) -} - -export default baseXor diff --git a/.internal/cloneBuffer.js b/.internal/cloneBuffer.js deleted file mode 100644 index f1be8b58fc..0000000000 --- a/.internal/cloneBuffer.js +++ /dev/null @@ -1,34 +0,0 @@ -import root from './root.js' - -/** Detect free variable `exports`. */ -const freeExports = typeof exports === 'object' && exports !== null && !exports.nodeType && exports - -/** Detect free variable `module`. */ -const freeModule = freeExports && typeof module === 'object' && module !== null && !module.nodeType && module - -/** Detect the popular CommonJS extension `module.exports`. */ -const moduleExports = freeModule && freeModule.exports === freeExports - -/** Built-in value references. */ -const Buffer = moduleExports ? root.Buffer : undefined, allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined - -/** - * Creates a clone of `buffer`. - * - * @private - * @param {Buffer} buffer The buffer to clone. - * @param {boolean} [isDeep] Specify a deep clone. - * @returns {Buffer} Returns the cloned buffer. - */ -function cloneBuffer(buffer, isDeep) { - if (isDeep) { - return buffer.slice() - } - const length = buffer.length - const result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length) - - buffer.copy(result) - return result -} - -export default cloneBuffer diff --git a/.internal/compareMultiple.js b/.internal/compareMultiple.js deleted file mode 100644 index 0d9fc2d872..0000000000 --- a/.internal/compareMultiple.js +++ /dev/null @@ -1,45 +0,0 @@ -import compareAscending from './compareAscending.js' - -/** - * Used by `orderBy` to compare multiple properties of a value to another - * and stable sort them. - * - * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, - * specify an order of "desc" for descending or "asc" for ascending sort order - * of corresponding values. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {(string|function)[]} orders The order to sort by for each property. - * @returns {number} Returns the sort order indicator for `object`. - */ -function compareMultiple(object, other, orders) { - let index = -1 - const objCriteria = object.criteria - const othCriteria = other.criteria - const length = objCriteria.length - const ordersLength = orders.length - - while (++index < length) { - const order = index < ordersLength ? orders[index] : null - const cmpFn = (order && typeof order === 'function') ? order: compareAscending - const result = cmpFn(objCriteria[index], othCriteria[index]) - if (result) { - if (order && typeof order !== 'function') { - return result * (order == 'desc' ? -1 : 1) - } - return result - } - } - // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications - // that causes it, under certain circumstances, to provide the same value for - // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 - // for more details. - // - // This also ensures a stable sort in V8 and other engines. - // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. - return object.index - other.index -} - -export default compareMultiple diff --git a/.internal/createSet.js b/.internal/createSet.js deleted file mode 100644 index 0e86dc5c7d..0000000000 --- a/.internal/createSet.js +++ /dev/null @@ -1,17 +0,0 @@ -import setToArray from './setToArray.js' - -/** Used as references for various `Number` constants. */ -const INFINITY = 1 / 0 - -/** - * Creates a set object of `values`. - * - * @private - * @param {Array} values The values to add to the set. - * @returns {Object} Returns the new set. - */ -const createSet = (Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) - ? (values) => new Set(values) - : () => {} - -export default createSet diff --git a/.internal/equalArrays.js b/.internal/equalArrays.js deleted file mode 100644 index fac53cbfd9..0000000000 --- a/.internal/equalArrays.js +++ /dev/null @@ -1,84 +0,0 @@ -import SetCache from './SetCache.js' -import some from '../some.js' -import cacheHas from './cacheHas.js' - -/** Used to compose bitmasks for value comparisons. */ -const COMPARE_PARTIAL_FLAG = 1 -const COMPARE_UNORDERED_FLAG = 2 - -/** - * A specialized version of `baseIsEqualDeep` for arrays with support for - * partial deep comparisons. - * - * @private - * @param {Array} array The array to compare. - * @param {Array} other The other array to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `array` and `other` objects. - * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. - */ -function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { - const isPartial = bitmask & COMPARE_PARTIAL_FLAG - const arrLength = array.length - const othLength = other.length - - if (arrLength != othLength && !(isPartial && othLength > arrLength)) { - return false - } - // Assume cyclic values are equal. - const stacked = stack.get(array) - if (stacked && stack.get(other)) { - return stacked == other - } - let index = -1 - let result = true - const seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined - - stack.set(array, other) - stack.set(other, array) - - // Ignore non-index properties. - while (++index < arrLength) { - let compared - const arrValue = array[index] - const othValue = other[index] - - if (customizer) { - compared = isPartial - ? customizer(othValue, arrValue, index, other, array, stack) - : customizer(arrValue, othValue, index, array, other, stack) - } - if (compared !== undefined) { - if (compared) { - continue - } - result = false - break - } - // Recursively compare arrays (susceptible to call stack limits). - if (seen) { - if (!some(other, (othValue, othIndex) => { - if (!cacheHas(seen, othIndex) && - (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { - return seen.push(othIndex) - } - })) { - result = false - break - } - } else if (!( - arrValue === othValue || - equalFunc(arrValue, othValue, bitmask, customizer, stack) - )) { - result = false - break - } - } - stack['delete'](array) - stack['delete'](other) - return result -} - -export default equalArrays diff --git a/.internal/equalByTag.js b/.internal/equalByTag.js deleted file mode 100644 index fd8f80c9dd..0000000000 --- a/.internal/equalByTag.js +++ /dev/null @@ -1,109 +0,0 @@ -import eq from '../eq.js' -import equalArrays from './equalArrays.js' -import mapToArray from './mapToArray.js' -import setToArray from './setToArray.js' - -/** Used to compose bitmasks for value comparisons. */ -const COMPARE_PARTIAL_FLAG = 1 -const COMPARE_UNORDERED_FLAG = 2 - -/** `Object#toString` result references. */ -const boolTag = '[object Boolean]' -const dateTag = '[object Date]' -const errorTag = '[object Error]' -const mapTag = '[object Map]' -const numberTag = '[object Number]' -const regexpTag = '[object RegExp]' -const setTag = '[object Set]' -const stringTag = '[object String]' -const symbolTag = '[object Symbol]' - -const arrayBufferTag = '[object ArrayBuffer]' -const dataViewTag = '[object DataView]' - -/** Used to convert symbols to primitives and strings. */ -const symbolValueOf = Symbol.prototype.valueOf - -/** - * A specialized version of `baseIsEqualDeep` for comparing objects of - * the same `toStringTag`. - * - * **Note:** This function only supports comparing values with tags of - * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {string} tag The `toStringTag` of the objects to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ -function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { - switch (tag) { - case dataViewTag: - if ((object.byteLength != other.byteLength) || - (object.byteOffset != other.byteOffset)) { - return false - } - object = object.buffer - other = other.buffer - - case arrayBufferTag: - if ((object.byteLength != other.byteLength) || - !equalFunc(new Uint8Array(object), new Uint8Array(other))) { - return false - } - return true - - case boolTag: - case dateTag: - case numberTag: - // Coerce booleans to `1` or `0` and dates to milliseconds. - // Invalid dates are coerced to `NaN`. - return eq(+object, +other) - - case errorTag: - return object.name == other.name && object.message == other.message - - case regexpTag: - case stringTag: - // Coerce regexes to strings and treat strings, primitives and objects, - // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring - // for more details. - return object == `${other}` - - case mapTag: - let convert = mapToArray - - case setTag: - const isPartial = bitmask & COMPARE_PARTIAL_FLAG - convert || (convert = setToArray) - - if (object.size != other.size && !isPartial) { - return false - } - // Assume cyclic values are equal. - const stacked = stack.get(object) - if (stacked) { - return stacked == other - } - bitmask |= COMPARE_UNORDERED_FLAG - - // Recursively compare objects (susceptible to call stack limits). - stack.set(object, other) - const result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack) - stack['delete'](object) - return result - - case symbolTag: - if (symbolValueOf) { - return symbolValueOf.call(object) == symbolValueOf.call(other) - } - } - return false -} - -export default equalByTag diff --git a/.internal/equalObjects.js b/.internal/equalObjects.js deleted file mode 100644 index 8b884cd943..0000000000 --- a/.internal/equalObjects.js +++ /dev/null @@ -1,88 +0,0 @@ -import getAllKeys from './getAllKeys.js' - -/** Used to compose bitmasks for value comparisons. */ -const COMPARE_PARTIAL_FLAG = 1 - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * A specialized version of `baseIsEqualDeep` for objects with support for - * partial deep comparisons. - * - * @private - * @param {Object} object The object to compare. - * @param {Object} other The other object to compare. - * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. - * @param {Function} customizer The function to customize comparisons. - * @param {Function} equalFunc The function to determine equivalents of values. - * @param {Object} stack Tracks traversed `object` and `other` objects. - * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. - */ -function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { - const isPartial = bitmask & COMPARE_PARTIAL_FLAG - const objProps = getAllKeys(object) - const objLength = objProps.length - const othProps = getAllKeys(other) - const othLength = othProps.length - - if (objLength != othLength && !isPartial) { - return false - } - let key - let index = objLength - while (index--) { - key = objProps[index] - if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { - return false - } - } - // Assume cyclic values are equal. - const stacked = stack.get(object) - if (stacked && stack.get(other)) { - return stacked == other - } - let result = true - stack.set(object, other) - stack.set(other, object) - - let compared - let skipCtor = isPartial - while (++index < objLength) { - key = objProps[index] - const objValue = object[key] - const othValue = other[key] - - if (customizer) { - compared = isPartial - ? customizer(othValue, objValue, key, other, object, stack) - : customizer(objValue, othValue, key, object, other, stack) - } - // Recursively compare objects (susceptible to call stack limits). - if (!(compared === undefined - ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) - : compared - )) { - result = false - break - } - skipCtor || (skipCtor = key == 'constructor') - } - if (result && !skipCtor) { - const objCtor = object.constructor - const othCtor = other.constructor - - // Non `Object` object instances with different constructors are not equal. - if (objCtor != othCtor && - ('constructor' in object && 'constructor' in other) && - !(typeof objCtor === 'function' && objCtor instanceof objCtor && - typeof othCtor === 'function' && othCtor instanceof othCtor)) { - result = false - } - } - stack['delete'](object) - stack['delete'](other) - return result -} - -export default equalObjects diff --git a/.internal/isIndex.js b/.internal/isIndex.js deleted file mode 100644 index 641a0f84a8..0000000000 --- a/.internal/isIndex.js +++ /dev/null @@ -1,25 +0,0 @@ -/** Used as references for various `Number` constants. */ -const MAX_SAFE_INTEGER = 9007199254740991 - -/** Used to detect unsigned integer values. */ -const reIsUint = /^(?:0|[1-9]\d*)$/ - -/** - * Checks if `value` is a valid array-like index. - * - * @private - * @param {*} value The value to check. - * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. - * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. - */ -function isIndex(value, length) { - const type = typeof value - length = length == null ? MAX_SAFE_INTEGER : length - - return !!length && - (type === 'number' || - (type !== 'symbol' && reIsUint.test(value))) && - (value > -1 && value % 1 == 0 && value < length) -} - -export default isIndex diff --git a/.internal/root.js b/.internal/root.js deleted file mode 100644 index 58abec6ea8..0000000000 --- a/.internal/root.js +++ /dev/null @@ -1,13 +0,0 @@ -/* global globalThis, self */ -import freeGlobal from './freeGlobal.js' - -/** Detect free variable `globalThis` */ -const freeGlobalThis = typeof globalThis === 'object' && globalThis !== null && globalThis.Object == Object && globalThis - -/** Detect free variable `self`. */ -const freeSelf = typeof self === 'object' && self !== null && self.Object === Object && self - -/** Used as a reference to the global object. */ -const root = freeGlobalThis || freeGlobal || freeSelf || Function('return this')() - -export default root diff --git a/.internal/toKey.js b/.internal/toKey.js deleted file mode 100644 index cc450c80df..0000000000 --- a/.internal/toKey.js +++ /dev/null @@ -1,21 +0,0 @@ -import isSymbol from '../isSymbol.js' - -/** Used as references for various `Number` constants. */ -const INFINITY = 1 / 0 - -/** - * Converts `value` to a string key if it's not a string or symbol. - * - * @private - * @param {*} value The value to inspect. - * @returns {string|symbol} Returns the key. - */ -function toKey(value) { - if (typeof value === 'string' || isSymbol(value)) { - return value - } - const result = `${value}` - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result -} - -export default toKey diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..e6c4d17ade --- /dev/null +++ b/.prettierignore @@ -0,0 +1,5 @@ +coverage/ +coverage-merged/ +dist/ +node_modules/ +types/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000000..b06ca2eca4 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,6 @@ +{ + "printWidth": 100, + "useTabs": false, + "tabWidth": 4, + "singleQuote": true +} diff --git a/README.md b/README.md index 75f0abd515..7aebe8e5a4 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,14 @@ [Site](https://lodash.com/) | [Docs](https://lodash.com/docs) | -[FP Guide](https://github.com/lodash/lodash/wiki/FP-Guide) | [Contributing](https://github.com/lodash/lodash/blob/master/.github/CONTRIBUTING.md) | [Wiki](https://github.com/lodash/lodash/wiki "Changelog, Roadmap, etc.") | -[Code of Conduct](https://code-of-conduct.openjsf.org) | -[Twitter](https://twitter.com/bestiejs) | -[Chat](https://gitter.im/lodash/lodash) +[Code of Conduct](https://code-of-conduct.openjsf.org) The [Lodash](https://lodash.com/) library exported as a [UMD](https://github.com/umdjs/umd) module. -Generated using [lodash-cli](https://www.npmjs.com/package/lodash-cli): ```shell -$ npm run build +$ bun run build $ lodash -o ./dist/lodash.js $ lodash core -o ./dist/lodash.core.js ``` @@ -34,14 +30,12 @@ In a browser: ``` -Using npm: +Using bun: ```shell -$ npm i -g npm -$ npm i lodash +$ bun i lodash ``` -Note: add `--save` if you are using npm < 5.0.0 -In Node.js: +In [Bun](https://bun.sh): ```js // Load the full build. var _ = require('lodash'); @@ -76,5 +70,3 @@ Lodash is available in a [variety of builds](https://lodash.com/custom-builds) & * [lodash](https://www.npmjs.com/package/lodash) & [per method packages](https://www.npmjs.com/search?q=keywords:lodash-modularized) * [lodash-es](https://www.npmjs.com/package/lodash-es), [babel-plugin-lodash](https://www.npmjs.com/package/babel-plugin-lodash), & [lodash-webpack-plugin](https://www.npmjs.com/package/lodash-webpack-plugin) - * [lodash/fp](https://github.com/lodash/lodash/tree/npm/fp) - * [lodash-amd](https://www.npmjs.com/package/lodash-amd) diff --git a/add.js b/add.js deleted file mode 100644 index 1e69176591..0000000000 --- a/add.js +++ /dev/null @@ -1,18 +0,0 @@ -import createMathOperation from './.internal/createMathOperation.js' - -/** - * Adds two numbers. - * - * @since 3.4.0 - * @category Math - * @param {number} augend The first number in an addition. - * @param {number} addend The second number in an addition. - * @returns {number} Returns the total. - * @example - * - * add(6, 4) - * // => 10 - */ -const add = createMathOperation((augend, addend) => augend + addend, 0) - -export default add diff --git a/after.js b/after.js deleted file mode 100644 index dade4fd0c0..0000000000 --- a/after.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * The opposite of `before`. This method creates a function that invokes - * `func` once it's called `n` or more times. - * - * @since 0.1.0 - * @category Function - * @param {number} n The number of calls before `func` is invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * const saves = ['profile', 'settings'] - * const done = after(saves.length, () => console.log('done saving!')) - * - * forEach(saves, type => asyncSave({ 'type': type, 'complete': done })) - * // => Logs 'done saving!' after the two async saves have completed. - */ -function after(n, func) { - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - n = n || 0 - return function(...args) { - if (--n < 1) { - return func.apply(this, args) - } - } -} - -export default after diff --git a/at.js b/at.js deleted file mode 100644 index 69bcecc0d4..0000000000 --- a/at.js +++ /dev/null @@ -1,21 +0,0 @@ -import baseAt from './.internal/baseAt.js' -import baseFlatten from './.internal/baseFlatten.js' - -/** - * Creates an array of values corresponding to `paths` of `object`. - * - * @since 1.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Array} Returns the picked values. - * @example - * - * const object = { 'a': [{ 'b': { 'c': 3 } }, 4] } - * - * at(object, ['a[0].b.c', 'a[1]']) - * // => [3, 4] - */ -const at = (object, ...paths) => baseAt(object, baseFlatten(paths, 1)) - -export default at diff --git a/attempt.js b/attempt.js deleted file mode 100644 index ee6c482961..0000000000 --- a/attempt.js +++ /dev/null @@ -1,30 +0,0 @@ -import isError from './isError.js' - -/** - * Attempts to invoke `func`, returning either the result or the caught error - * object. Any additional arguments are provided to `func` when it's invoked. - * - * @since 3.0.0 - * @category Util - * @param {Function} func The function to attempt. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {*} Returns the `func` result or error object. - * @example - * - * // Avoid throwing errors for invalid selectors. - * const elements = attempt(selector => - * document.querySelectorAll(selector), '>_>') - * - * if (isError(elements)) { - * elements = [] - * } - */ -function attempt(func, ...args) { - try { - return func(...args) - } catch (e) { - return isError(e) ? e : new Error(e) - } -} - -export default attempt diff --git a/before.js b/before.js deleted file mode 100644 index 88ba32f409..0000000000 --- a/before.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Creates a function that invokes `func`, with the `this` binding and arguments - * of the created function, while it's called less than `n` times. Subsequent - * calls to the created function return the result of the last `func` invocation. - * - * @since 3.0.0 - * @category Function - * @param {number} n The number of calls at which `func` is no longer invoked. - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * jQuery(element).on('click', before(5, addContactToList)) - * // => Allows adding up to 4 contacts to the list. - */ -function before(n, func) { - let result - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - return function(...args) { - if (--n > 0) { - result = func.apply(this, args) - } - if (n <= 1) { - func = undefined - } - return result - } -} - -export default before diff --git a/bunfig.toml b/bunfig.toml new file mode 100644 index 0000000000..1fe90030c2 --- /dev/null +++ b/bunfig.toml @@ -0,0 +1,2 @@ +[install.lockfile] +print = "yarn" \ No newline at end of file diff --git a/camelCase.js b/camelCase.js deleted file mode 100644 index 9882da6f28..0000000000 --- a/camelCase.js +++ /dev/null @@ -1,31 +0,0 @@ -import upperFirst from './upperFirst.js' -import words from './words.js' -import toString from './toString.js' - -/** - * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the camel cased string. - * @see lowerCase, kebabCase, snakeCase, startCase, upperCase, upperFirst - * @example - * - * camelCase('Foo Bar') - * // => 'fooBar' - * - * camelCase('--foo-bar--') - * // => 'fooBar' - * - * camelCase('__FOO_BAR__') - * // => 'fooBar' - */ -const camelCase = (string) => ( - words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => { - word = word.toLowerCase() - return result + (index ? upperFirst(word) : word) - }, '') -) - -export default camelCase diff --git a/capitalize.js b/capitalize.js deleted file mode 100644 index 2ad2ffed9f..0000000000 --- a/capitalize.js +++ /dev/null @@ -1,20 +0,0 @@ -import upperFirst from './upperFirst.js' -import toString from './toString.js' - -/** - * Converts the first character of `string` to upper case and the remaining - * to lower case. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to capitalize. - * @returns {string} Returns the capitalized string. - * @example - * - * capitalize('FRED') - * // => 'Fred' - */ -const capitalize = (string) => upperFirst(toString(string).toLowerCase()) - - -export default capitalize diff --git a/castArray.js b/castArray.js deleted file mode 100644 index d00bd367cc..0000000000 --- a/castArray.js +++ /dev/null @@ -1,41 +0,0 @@ - -/** - * Casts `value` as an array if it's not one. - * - * @since 4.4.0 - * @category Lang - * @param {*} value The value to inspect. - * @returns {Array} Returns the cast array. - * @example - * - * castArray(1) - * // => [1] - * - * castArray({ 'a': 1 }) - * // => [{ 'a': 1 }] - * - * castArray('abc') - * // => ['abc'] - * - * castArray(null) - * // => [null] - * - * castArray(undefined) - * // => [undefined] - * - * castArray() - * // => [] - * - * const array = [1, 2, 3] - * console.log(castArray(array) === array) - * // => true - */ -function castArray(...args) { - if (!args.length) { - return [] - } - const value = args[0] - return Array.isArray(value) ? value : [value] -} - -export default castArray diff --git a/ceil.js b/ceil.js deleted file mode 100644 index ffb78b3609..0000000000 --- a/ceil.js +++ /dev/null @@ -1,24 +0,0 @@ -import createRound from './.internal/createRound.js' - -/** - * Computes `number` rounded up to `precision`. (Round up: the smallest integer greater than or equal to a given number.) - * - * @since 3.10.0 - * @category Math - * @param {number} number The number to round up. - * @param {number} [precision=0] The precision to round up to. - * @returns {number} Returns the rounded up number. - * @example - * - * ceil(4.006) - * // => 5 - * - * ceil(6.004, 2) - * // => 6.01 - * - * ceil(6040, -2) - * // => 6100 - */ -const ceil = createRound('ceil') - -export default ceil diff --git a/chunk.js b/chunk.js deleted file mode 100644 index 8d57b4b197..0000000000 --- a/chunk.js +++ /dev/null @@ -1,38 +0,0 @@ -import slice from './slice.js' -import toInteger from './toInteger.js' - -/** - * Creates an array of elements split into groups the length of `size`. - * If `array` can't be split evenly, the final chunk will be the remaining - * elements. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to process. - * @param {number} [size=1] The length of each chunk - * @returns {Array} Returns the new array of chunks. - * @example - * - * chunk(['a', 'b', 'c', 'd'], 2) - * // => [['a', 'b'], ['c', 'd']] - * - * chunk(['a', 'b', 'c', 'd'], 3) - * // => [['a', 'b', 'c'], ['d']] - */ -function chunk(array, size = 1) { - size = Math.max(toInteger(size), 0) - const length = array == null ? 0 : array.length - if (!length || size < 1) { - return [] - } - let index = 0 - let resIndex = 0 - const result = new Array(Math.ceil(length / size)) - - while (index < length) { - result[resIndex++] = slice(array, index, (index += size)) - } - return result -} - -export default chunk diff --git a/clamp.js b/clamp.js deleted file mode 100644 index 602377136a..0000000000 --- a/clamp.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Clamps `number` within the inclusive `lower` and `upper` bounds. - * - * @since 4.0.0 - * @category Number - * @param {number} number The number to clamp. - * @param {number} lower The lower bound. - * @param {number} upper The upper bound. - * @returns {number} Returns the clamped number. - * @example - * - * clamp(-10, -5, 5) - * // => -5 - * - * clamp(10, -5, 5) - * // => 5 - */ -function clamp(number, lower, upper) { - number = +number - lower = +lower - upper = +upper - lower = lower === lower ? lower : 0 - upper = upper === upper ? upper : 0 - if (number === number) { - number = number <= upper ? number : upper - number = number >= lower ? number : lower - } - return number -} - -export default clamp diff --git a/clone.js b/clone.js deleted file mode 100644 index 97d084f0d0..0000000000 --- a/clone.js +++ /dev/null @@ -1,35 +0,0 @@ -import baseClone from './.internal/baseClone.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_SYMBOLS_FLAG = 4 - -/** - * Creates a shallow clone of `value`. - * - * **Note:** This method is loosely based on the - * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) - * and supports cloning arrays, array buffers, booleans, date objects, maps, - * numbers, `Object` objects, regexes, sets, strings, symbols, and typed - * arrays. The own enumerable properties of `arguments` objects are cloned - * as plain objects. Object inheritance is preserved. An empty object is - * returned for uncloneable values such as error objects, functions, DOM nodes, - * and WeakMaps. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to clone. - * @returns {*} Returns the cloned value. - * @see cloneDeep - * @example - * - * const objects = [{ 'a': 1 }, { 'b': 2 }] - * - * const shallow = clone(objects) - * console.log(shallow[0] === objects[0]) - * // => true - */ -function clone(value) { - return baseClone(value, CLONE_SYMBOLS_FLAG) -} - -export default clone diff --git a/cloneDeep.js b/cloneDeep.js deleted file mode 100644 index 390e945fbe..0000000000 --- a/cloneDeep.js +++ /dev/null @@ -1,28 +0,0 @@ -import baseClone from './.internal/baseClone.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 -const CLONE_SYMBOLS_FLAG = 4 - -/** - * This method is like `clone` except that it recursively clones `value`. - * Object inheritance is preserved. - * - * @since 1.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @returns {*} Returns the deep cloned value. - * @see clone - * @example - * - * const objects = [{ 'a': 1 }, { 'b': 2 }] - * - * const deep = cloneDeep(objects) - * console.log(deep[0] === objects[0]) - * // => false - */ -function cloneDeep(value) { - return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG) -} - -export default cloneDeep diff --git a/cloneDeepWith.js b/cloneDeepWith.js deleted file mode 100644 index 4c29b9eaff..0000000000 --- a/cloneDeepWith.js +++ /dev/null @@ -1,40 +0,0 @@ -import baseClone from './.internal/baseClone.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 -const CLONE_SYMBOLS_FLAG = 4 - -/** - * This method is like `cloneWith` except that it recursively clones `value`. - * The customizer is invoked with up to four arguments - * (value [, index|key, object, stack]). - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to recursively clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the deep cloned value. - * @see cloneWith - * @example - * - * function customizer(value) { - * if (isElement(value)) { - * return value.cloneNode(true) - * } - * } - * - * const el = cloneDeepWith(document.body, customizer) - * - * console.log(el === document.body) - * // => false - * console.log(el.nodeName) - * // => 'BODY' - * console.log(el.childNodes.length) - * // => 20 - */ -function cloneDeepWith(value, customizer) { - customizer = typeof customizer === 'function' ? customizer : undefined - return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer) -} - -export default cloneDeepWith diff --git a/cloneWith.js b/cloneWith.js deleted file mode 100644 index 58da039ce7..0000000000 --- a/cloneWith.js +++ /dev/null @@ -1,40 +0,0 @@ -import baseClone from './.internal/baseClone.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_SYMBOLS_FLAG = 4 - -/** - * This method is like `clone` except that it accepts `customizer` which - * is invoked to produce the cloned value. If `customizer` returns `undefined`, - * cloning is handled by the method instead. The `customizer` is invoked with - * one argument (value). - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to clone. - * @param {Function} [customizer] The function to customize cloning. - * @returns {*} Returns the cloned value. - * @see cloneDeepWith - * @example - * - * function customizer(value) { - * if (isElement(value)) { - * return value.cloneNode(false) - * } - * } - * - * const el = cloneWith(document.body, customizer) - * - * console.log(el === document.body) - * // => false - * console.log(el.nodeName) - * // => 'BODY' - * console.log(el.childNodes.length) - * // => 0 - */ -function cloneWith(value, customizer) { - customizer = typeof customizer === 'function' ? customizer : undefined - return baseClone(value, CLONE_SYMBOLS_FLAG, customizer) -} - -export default cloneWith diff --git a/compact.js b/compact.js deleted file mode 100644 index 5078673e8e..0000000000 --- a/compact.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Creates an array with all falsey values removed. The values `false`, `null`, - * `0`, `""`, `undefined`, and `NaN` are falsey. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to compact. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * compact([0, 1, false, 2, '', 3]) - * // => [1, 2, 3] - */ -function compact(array) { - let resIndex = 0 - const result = [] - - if (array == null) { - return result - } - - for (const value of array) { - if (value) { - result[resIndex++] = value - } - } - return result -} - -export default compact diff --git a/cond.js b/cond.js deleted file mode 100644 index 2927110385..0000000000 --- a/cond.js +++ /dev/null @@ -1,49 +0,0 @@ -import map from './map.js' - -/** - * Creates a function that iterates over `pairs` and invokes the corresponding - * function of the first predicate to return truthy. The predicate-function - * pairs are invoked with the `this` binding and arguments of the created - * function. - * - * @since 4.0.0 - * @category Util - * @param {Array} pairs The predicate-function pairs. - * @returns {Function} Returns the new composite function. - * @example - * - * const func = cond([ - * [matches({ 'a': 1 }), () => 'matches A'], - * [conforms({ 'b': isNumber }), () => 'matches B'], - * [() => true, () => 'no match'] - * ]) - * - * func({ 'a': 1, 'b': 2 }) - * // => 'matches A' - * - * func({ 'a': 0, 'b': 1 }) - * // => 'matches B' - * - * func({ 'a': '1', 'b': '2' }) - * // => 'no match' - */ -function cond(pairs) { - const length = pairs == null ? 0 : pairs.length - - pairs = !length ? [] : map(pairs, (pair) => { - if (typeof pair[1] !== 'function') { - throw new TypeError('Expected a function') - } - return [pair[0], pair[1]] - }) - - return (...args) => { - for (const pair of pairs) { - if (pair[0].apply(this, args)) { - return pair[1].apply(this, args) - } - } - } -} - -export default cond diff --git a/conforms.js b/conforms.js deleted file mode 100644 index 843035eb00..0000000000 --- a/conforms.js +++ /dev/null @@ -1,33 +0,0 @@ -import baseClone from './.internal/baseClone.js' -import baseConforms from './.internal/baseConforms.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 - -/** - * Creates a function that invokes the predicate properties of `source` with - * the corresponding property values of a given object, returning `true` if - * all predicates return truthy, else `false`. - * - * **Note:** The created function is equivalent to `conformsTo` with - * `source` partially applied. - * - * @since 4.0.0 - * @category Util - * @param {Object} source The object of property predicates to conform to. - * @returns {Function} Returns the new spec function. - * @example - * - * const objects = [ - * { 'a': 2, 'b': 1 }, - * { 'a': 1, 'b': 2 } - * ] - * - * filter(objects, conforms({ 'b': function(n) { return n > 1 } })) - * // => [{ 'a': 1, 'b': 2 }] - */ -function conforms(source) { - return baseConforms(baseClone(source, CLONE_DEEP_FLAG)) -} - -export default conforms diff --git a/conformsTo.js b/conformsTo.js deleted file mode 100644 index ffe47039d1..0000000000 --- a/conformsTo.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseConformsTo from './.internal/baseConformsTo.js' -import keys from './keys.js' - -/** - * Checks if `object` conforms to `source` by invoking the predicate - * properties of `source` with the corresponding property values of `object`. - * - * **Note:** This method is equivalent to `conforms` when `source` is - * partially applied. - * - * @since 4.14.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property predicates to conform to. - * @returns {boolean} Returns `true` if `object` conforms, else `false`. - * @example - * - * const object = { 'a': 1, 'b': 2 } - * - * conformsTo(object, { 'b': function(n) { return n > 1 } }) - * // => true - * - * conformsTo(object, { 'b': function(n) { return n > 2 } }) - * // => false - */ -function conformsTo(object, source) { - return source == null || baseConformsTo(object, source, keys(source)) -} - -export default conformsTo diff --git a/countBy.js b/countBy.js deleted file mode 100644 index 94f45edac1..0000000000 --- a/countBy.js +++ /dev/null @@ -1,41 +0,0 @@ -import baseAssignValue from './.internal/baseAssignValue.js' -import reduce from './reduce.js' - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The corresponding value of - * each key is the number of times the key was returned by `iteratee`. The - * iteratee is invoked with one argument: (value). - * - * @since 0.5.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * const users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'betty', 'active': true }, - * { 'user': 'fred', 'active': false } - * ] - * - * countBy(users, value => value.active); - * // => { 'true': 2, 'false': 1 } - */ -function countBy(collection, iteratee) { - return reduce(collection, (result, value, key) => { - key = iteratee(value) - if (hasOwnProperty.call(result, key)) { - ++result[key] - } else { - baseAssignValue(result, key, 1) - } - return result - }, {}) -} - -export default countBy diff --git a/create.js b/create.js deleted file mode 100644 index cd01b4d530..0000000000 --- a/create.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Creates an object that inherits from the `prototype` object. If a - * `properties` object is given, its own enumerable string keyed properties - * are assigned to the created object. - * - * @since 2.3.0 - * @category Object - * @param {Object} prototype The object to inherit from. - * @param {Object} [properties] The properties to assign to the object. - * @returns {Object} Returns the new object. - * @example - * - * function Shape() { - * this.x = 0 - * this.y = 0 - * } - * - * function Circle() { - * Shape.call(this) - * } - * - * Circle.prototype = create(Shape.prototype, { - * 'constructor': Circle - * }) - * - * const circle = new Circle - * circle instanceof Circle - * // => true - * - * circle instanceof Shape - * // => true - */ -function create(prototype, properties) { - prototype = prototype === null ? null : Object(prototype) - const result = Object.create(prototype) - return properties == null ? result : Object.assign(result, properties) -} - -export default create diff --git a/debounce.js b/debounce.js deleted file mode 100644 index 2a799a6136..0000000000 --- a/debounce.js +++ /dev/null @@ -1,213 +0,0 @@ -import isObject from './isObject.js' -import root from './.internal/root.js' - -/** - * Creates a debounced function that delays invoking `func` until after `wait` - * milliseconds have elapsed since the last time the debounced function was - * invoked, or until the next browser frame is drawn. The debounced function - * comes with a `cancel` method to cancel delayed `func` invocations and a - * `flush` method to immediately invoke them. Provide `options` to indicate - * whether `func` should be invoked on the leading and/or trailing edge of the - * `wait` timeout. The `func` is invoked with the last arguments provided to the - * debounced function. Subsequent calls to the debounced function return the - * result of the last `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the debounced function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until the next tick, similar to `setTimeout` with a timeout of `0`. - * - * If `wait` is omitted in an environment with `requestAnimationFrame`, `func` - * invocation will be deferred until the next frame is drawn (typically about - * 16ms). - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `debounce` and `throttle`. - * - * @since 0.1.0 - * @category Function - * @param {Function} func The function to debounce. - * @param {number} [wait=0] - * The number of milliseconds to delay; if omitted, `requestAnimationFrame` is - * used (if available). - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=false] - * Specify invoking on the leading edge of the timeout. - * @param {number} [options.maxWait] - * The maximum time `func` is allowed to be delayed before it's invoked. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new debounced function. - * @example - * - * // Avoid costly calculations while the window size is in flux. - * jQuery(window).on('resize', debounce(calculateLayout, 150)) - * - * // Invoke `sendMail` when clicked, debouncing subsequent calls. - * jQuery(element).on('click', debounce(sendMail, 300, { - * 'leading': true, - * 'trailing': false - * })) - * - * // Ensure `batchLog` is invoked once after 1 second of debounced calls. - * const debounced = debounce(batchLog, 250, { 'maxWait': 1000 }) - * const source = new EventSource('/stream') - * jQuery(source).on('message', debounced) - * - * // Cancel the trailing debounced invocation. - * jQuery(window).on('popstate', debounced.cancel) - * - * // Check for pending invocations. - * const status = debounced.pending() ? "Pending..." : "Ready" - */ -function debounce(func, wait, options) { - let lastArgs, - lastThis, - maxWait, - result, - timerId, - lastCallTime - - let lastInvokeTime = 0 - let leading = false - let maxing = false - let trailing = true - - // Bypass `requestAnimationFrame` by explicitly setting `wait=0`. - const useRAF = (!wait && wait !== 0 && typeof root.requestAnimationFrame === 'function') - - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - wait = +wait || 0 - if (isObject(options)) { - leading = !!options.leading - maxing = 'maxWait' in options - maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait - trailing = 'trailing' in options ? !!options.trailing : trailing - } - - function invokeFunc(time) { - const args = lastArgs - const thisArg = lastThis - - lastArgs = lastThis = undefined - lastInvokeTime = time - result = func.apply(thisArg, args) - return result - } - - function startTimer(pendingFunc, wait) { - if (useRAF) { - root.cancelAnimationFrame(timerId) - return root.requestAnimationFrame(pendingFunc) - } - return setTimeout(pendingFunc, wait) - } - - function cancelTimer(id) { - if (useRAF) { - return root.cancelAnimationFrame(id) - } - clearTimeout(id) - } - - function leadingEdge(time) { - // Reset any `maxWait` timer. - lastInvokeTime = time - // Start the timer for the trailing edge. - timerId = startTimer(timerExpired, wait) - // Invoke the leading edge. - return leading ? invokeFunc(time) : result - } - - function remainingWait(time) { - const timeSinceLastCall = time - lastCallTime - const timeSinceLastInvoke = time - lastInvokeTime - const timeWaiting = wait - timeSinceLastCall - - return maxing - ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) - : timeWaiting - } - - function shouldInvoke(time) { - const timeSinceLastCall = time - lastCallTime - const timeSinceLastInvoke = time - lastInvokeTime - - // Either this is the first call, activity has stopped and we're at the - // trailing edge, the system time has gone backwards and we're treating - // it as the trailing edge, or we've hit the `maxWait` limit. - return (lastCallTime === undefined || (timeSinceLastCall >= wait) || - (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)) - } - - function timerExpired() { - const time = Date.now() - if (shouldInvoke(time)) { - return trailingEdge(time) - } - // Restart the timer. - timerId = startTimer(timerExpired, remainingWait(time)) - } - - function trailingEdge(time) { - timerId = undefined - - // Only invoke if we have `lastArgs` which means `func` has been - // debounced at least once. - if (trailing && lastArgs) { - return invokeFunc(time) - } - lastArgs = lastThis = undefined - return result - } - - function cancel() { - if (timerId !== undefined) { - cancelTimer(timerId) - } - lastInvokeTime = 0 - lastArgs = lastCallTime = lastThis = timerId = undefined - } - - function flush() { - return timerId === undefined ? result : trailingEdge(Date.now()) - } - - function pending() { - return timerId !== undefined - } - - function debounced(...args) { - const time = Date.now() - const isInvoking = shouldInvoke(time) - - lastArgs = args - lastThis = this - lastCallTime = time - - if (isInvoking) { - if (timerId === undefined) { - return leadingEdge(lastCallTime) - } - if (maxing) { - // Handle invocations in a tight loop. - timerId = startTimer(timerExpired, wait) - return invokeFunc(lastCallTime) - } - } - if (timerId === undefined) { - timerId = startTimer(timerExpired, wait) - } - return result - } - debounced.cancel = cancel - debounced.flush = flush - debounced.pending = pending - return debounced -} - -export default debounce diff --git a/deburr.js b/deburr.js deleted file mode 100644 index 8475fab629..0000000000 --- a/deburr.js +++ /dev/null @@ -1,43 +0,0 @@ -import deburrLetter from './.internal/deburrLetter.js' - -/** Used to match Latin Unicode letters (excluding mathematical operators). */ -const reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g - -/** Used to compose unicode character classes. */ -const rsComboMarksRange = '\\u0300-\\u036f' -const reComboHalfMarksRange = '\\ufe20-\\ufe2f' -const rsComboSymbolsRange = '\\u20d0-\\u20ff' -const rsComboMarksExtendedRange = '\\u1ab0-\\u1aff' -const rsComboMarksSupplementRange = '\\u1dc0-\\u1dff' -const rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange + rsComboMarksExtendedRange + rsComboMarksSupplementRange - -/** Used to compose unicode capture groups. */ -const rsCombo = `[${rsComboRange}]` - -/** - * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and - * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). - */ -const reComboMark = RegExp(rsCombo, 'g') - -/** - * Deburrs `string` by converting - * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) - * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) - * letters to basic Latin letters and removing - * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to deburr. - * @returns {string} Returns the deburred string. - * @example - * - * deburr('déjà vu') - * // => 'deja vu' - */ -function deburr(string) { - return string && string.replace(reLatin, deburrLetter).replace(reComboMark, '') -} - -export default deburr diff --git a/defaultTo.js b/defaultTo.js deleted file mode 100644 index 6f43f29ca2..0000000000 --- a/defaultTo.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Checks `value` to determine whether a default value should be returned in - * its place. The `defaultValue` is returned if `value` is `NaN`, `null`, - * or `undefined`. - * - * @since 4.14.0 - * @category Util - * @param {*} value The value to check. - * @param {*} defaultValue The default value. - * @returns {*} Returns the resolved value. - * @example - * - * defaultTo(1, 10) - * // => 1 - * - * defaultTo(undefined, 10) - * // => 10 - */ -function defaultTo(value, defaultValue) { - return (value == null || value !== value) ? defaultValue : value -} - -export default defaultTo diff --git a/defaultToAny.js b/defaultToAny.js deleted file mode 100644 index 038730c86e..0000000000 --- a/defaultToAny.js +++ /dev/null @@ -1,32 +0,0 @@ -import arrayReduce from './.internal/arrayReduce.js' -import defaultTo from './defaultTo.js' - -/** - * This method is like `defaultTo` except that it accepts multiple default values and returns the first one that is not - * `NaN`, `null`, or `undefined`. - * - * @since 5.0.0 - * @category Util - * @param {*} value The value to check. - * @param {...*} defaultValues The default values. - * @returns {*} Returns the resolved value. - * @see _.defaultTo - * @example - * - * defaultToAny(1, 10, 20) - * // => 1 - * - * defaultToAny(undefined, 10, 20) - * // => 10 - * - * defaultToAny(undefined, null, 20) - * // => 20 - * - * defaultToAny(undefined, null, NaN) - * // => NaN - */ -function defaultToAny(value, ...defaultValues) { - return arrayReduce(defaultValues, defaultTo, value) -} - -export default defaultToAny diff --git a/defaults.js b/defaults.js deleted file mode 100644 index 99e234dbdb..0000000000 --- a/defaults.js +++ /dev/null @@ -1,45 +0,0 @@ -import eq from './eq.js' - -/** Used for built-in method references. */ -const objectProto = Object.prototype - -/** Used to check objects for own properties. */ -const hasOwnProperty = objectProto.hasOwnProperty - -/** - * Assigns own and inherited enumerable string keyed properties of source - * objects to the destination object for all destination properties that - * resolve to `undefined`. Source objects are applied from left to right. - * Once a property is set, additional values of the same property are ignored. - * - * **Note:** This method mutates `object`. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see defaultsDeep - * @example - * - * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }) - * // => { 'a': 1, 'b': 2 } - */ -function defaults(object, ...sources) { - object = Object(object) - sources.forEach((source) => { - if (source != null) { - source = Object(source) - for (const key in source) { - const value = object[key] - if (value === undefined || - (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key))) { - object[key] = source[key] - } - } - } - }) - return object -} - -export default defaults diff --git a/defaultsDeep.js b/defaultsDeep.js deleted file mode 100644 index ae942f0cba..0000000000 --- a/defaultsDeep.js +++ /dev/null @@ -1,26 +0,0 @@ -import customDefaultsMerge from './.internal/customDefaultsMerge.js' -import mergeWith from './mergeWith.js' - -/** - * This method is like `defaults` except that it recursively assigns - * default properties. - * - * **Note:** This method mutates `object`. - * - * @since 3.10.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @see defaults - * @example - * - * defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }) - * // => { 'a': { 'b': 2, 'c': 3 } } - */ -function defaultsDeep(...args) { - args.push(undefined, customDefaultsMerge) - return mergeWith.apply(undefined, args) -} - -export default defaultsDeep diff --git a/defer.js b/defer.js deleted file mode 100644 index 77c3da601f..0000000000 --- a/defer.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Defers invoking the `func` until the current call stack has cleared. Any - * additional arguments are provided to `func` when it's invoked. - * - * @since 0.1.0 - * @category Function - * @param {Function} func The function to defer. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {number} Returns the timer id. - * @example - * - * defer(text => console.log(text), 'deferred') - * // => Logs 'deferred' after one millisecond. - */ -function defer(func, ...args) { - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - return setTimeout(func, 1, ...args) -} - -export default defer diff --git a/delay.js b/delay.js deleted file mode 100644 index 52258b280a..0000000000 --- a/delay.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Invokes `func` after `wait` milliseconds. Any additional arguments are - * provided to `func` when it's invoked. - * - * @since 0.1.0 - * @category Function - * @param {Function} func The function to delay. - * @param {number} wait The number of milliseconds to delay invocation. - * @param {...*} [args] The arguments to invoke `func` with. - * @returns {number} Returns the timer id. - * @example - * - * delay(text => console.log(text), 1000, 'later') - * // => Logs 'later' after one second. - */ -function delay(func, wait, ...args) { - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - return setTimeout(func, +wait || 0, ...args) -} - -export default delay diff --git a/difference.js b/difference.js deleted file mode 100644 index 6a25b1e8aa..0000000000 --- a/difference.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseDifference from './.internal/baseDifference.js' -import baseFlatten from './.internal/baseFlatten.js' -import isArrayLikeObject from './isArrayLikeObject.js' - -/** - * Creates an array of `array` values not included in the other given arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. The order and references of result values are - * determined by the first array. - * - * **Note:** Unlike `pullAll`, this method returns a new array. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @see union, unionBy, unionWith, without, xor, xorBy, xorWith, - * @example - * - * difference([2, 1], [2, 3]) - * // => [1] - */ -function difference(array, ...values) { - return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) - : [] -} - -export default difference diff --git a/differenceBy.js b/differenceBy.js deleted file mode 100644 index f4c063e0eb..0000000000 --- a/differenceBy.js +++ /dev/null @@ -1,36 +0,0 @@ -import baseDifference from './.internal/baseDifference.js' -import baseFlatten from './.internal/baseFlatten.js' -import isArrayLikeObject from './isArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `difference` except that it accepts `iteratee` which - * is invoked for each element of `array` and `values` to generate the criterion - * by which they're compared. The order and references of result values are - * determined by the first array. The iteratee is invoked with one argument: - * (value). - * - * **Note:** Unlike `pullAllBy`, this method returns a new array. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor) - * // => [1.2] - */ -function differenceBy(array, ...values) { - let iteratee = last(values) - if (isArrayLikeObject(iteratee)) { - iteratee = undefined - } - return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), iteratee) - : [] -} - -export default differenceBy diff --git a/differenceWith.js b/differenceWith.js deleted file mode 100644 index 542cf2adfe..0000000000 --- a/differenceWith.js +++ /dev/null @@ -1,37 +0,0 @@ -import baseDifference from './.internal/baseDifference.js' -import baseFlatten from './.internal/baseFlatten.js' -import isArrayLikeObject from './isArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `difference` except that it accepts `comparator` - * which is invoked to compare elements of `array` to `values`. The order and - * references of result values are determined by the first array. The comparator - * is invoked with two arguments: (arrVal, othVal). - * - * **Note:** Unlike `pullAllWith`, this method returns a new array. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...Array} [values] The values to exclude. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @example - * - * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] - * - * differenceWith(objects, [{ 'x': 1, 'y': 2 }], isEqual) - * // => [{ 'x': 2, 'y': 1 }] - */ -function differenceWith(array, ...values) { - let comparator = last(values) - if (isArrayLikeObject(comparator)) { - comparator = undefined - } - return isArrayLikeObject(array) - ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), undefined, comparator) - : [] -} - -export default differenceWith diff --git a/divide.js b/divide.js deleted file mode 100644 index 70f6f4d0b4..0000000000 --- a/divide.js +++ /dev/null @@ -1,18 +0,0 @@ -import createMathOperation from './.internal/createMathOperation.js' - -/** - * Divide two numbers. - * - * @since 4.7.0 - * @category Math - * @param {number} dividend The first number in a division. - * @param {number} divisor The second number in a division. - * @returns {number} Returns the quotient. - * @example - * - * divide(6, 4) - * // => 1.5 - */ -const divide = createMathOperation((dividend, divisor) => dividend / divisor, 1) - -export default divide diff --git a/drop.js b/drop.js deleted file mode 100644 index 438f1e2012..0000000000 --- a/drop.js +++ /dev/null @@ -1,33 +0,0 @@ -import slice from './slice.js' -import toInteger from './toInteger.js' - -/** - * Creates a slice of `array` with `n` elements dropped from the beginning. - * - * @since 0.5.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @returns {Array} Returns the slice of `array`. - * @example - * - * drop([1, 2, 3]) - * // => [2, 3] - * - * drop([1, 2, 3], 2) - * // => [3] - * - * drop([1, 2, 3], 5) - * // => [] - * - * drop([1, 2, 3], 0) - * // => [1, 2, 3] - */ -function drop(array, n=1) { - const length = array == null ? 0 : array.length - return length - ? slice(array, n < 0 ? 0 : toInteger(n), length) - : [] -} - -export default drop diff --git a/dropRight.js b/dropRight.js deleted file mode 100644 index 2f023aeea3..0000000000 --- a/dropRight.js +++ /dev/null @@ -1,32 +0,0 @@ -import slice from './slice.js' -import toInteger from './toInteger.js' - -/** - * Creates a slice of `array` with `n` elements dropped from the end. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to drop. - * @returns {Array} Returns the slice of `array`. - * @example - * - * dropRight([1, 2, 3]) - * // => [1, 2] - * - * dropRight([1, 2, 3], 2) - * // => [1] - * - * dropRight([1, 2, 3], 5) - * // => [] - * - * dropRight([1, 2, 3], 0) - * // => [1, 2, 3] - */ -function dropRight(array, n=1) { - const length = array == null ? 0 : array.length - n = length - toInteger(n) - return length ? slice(array, 0, n < 0 ? 0 : n) : [] -} - -export default dropRight diff --git a/dropRightWhile.js b/dropRightWhile.js deleted file mode 100644 index e209b70d39..0000000000 --- a/dropRightWhile.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseWhile from './.internal/baseWhile.js' - -/** - * Creates a slice of `array` excluding elements dropped from the end. - * Elements are dropped until `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * const users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': true }, - * { 'user': 'pebbles', 'active': true } - * ] - * - * dropRightWhile(users, ({ active }) => active) - * // => objects for ['barney'] - */ -function dropRightWhile(array, predicate) { - return (array != null && array.length) - ? baseWhile(array, predicate, true, true) - : [] -} - -export default dropRightWhile diff --git a/dropWhile.js b/dropWhile.js deleted file mode 100644 index 88b7b6c8b3..0000000000 --- a/dropWhile.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseWhile from './.internal/baseWhile.js' - -/** - * Creates a slice of `array` excluding elements dropped from the beginning. - * Elements are dropped until `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * const users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': true }, - * { 'user': 'pebbles', 'active': false } - * ] - * - * dropWhile(users, ({ active }) => active) - * // => objects for ['pebbles'] - */ -function dropWhile(array, predicate) { - return (array != null && array.length) - ? baseWhile(array, predicate, true) - : [] -} - -export default dropWhile diff --git a/each.js b/each.js deleted file mode 100644 index d2fd2ffbd3..0000000000 --- a/each.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './forEach.js' diff --git a/eachRight.js b/eachRight.js deleted file mode 100644 index b08d8f5e0e..0000000000 --- a/eachRight.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './forEachRight.js' diff --git a/endsWith.js b/endsWith.js deleted file mode 100644 index 9cba731c50..0000000000 --- a/endsWith.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Checks if `string` ends with the given target string. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=string.length] The position to search up to. - * @returns {boolean} Returns `true` if `string` ends with `target`, - * else `false`. - * @see includes, startsWith - * @example - * - * endsWith('abc', 'c') - * // => true - * - * endsWith('abc', 'b') - * // => false - * - * endsWith('abc', 'b', 2) - * // => true - */ -function endsWith(string, target, position) { - const { length } = string - position = position === undefined ? length : +position - if (position < 0 || position != position) { - position = 0 - } - else if (position > length) { - position = length - } - const end = position - position -= target.length - return position >= 0 && string.slice(position, end) == target -} - -export default endsWith diff --git a/eq.js b/eq.js deleted file mode 100644 index 06333c4cd4..0000000000 --- a/eq.js +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Performs a - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * comparison between two values to determine if they are equivalent. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * const object = { 'a': 1 } - * const other = { 'a': 1 } - * - * eq(object, object) - * // => true - * - * eq(object, other) - * // => false - * - * eq('a', 'a') - * // => true - * - * eq('a', Object('a')) - * // => false - * - * eq(NaN, NaN) - * // => true - */ -function eq(value, other) { - return value === other || (value !== value && other !== other) -} - -export default eq diff --git a/eqDeep.js b/eqDeep.js deleted file mode 100644 index b1870f5d6e..0000000000 --- a/eqDeep.js +++ /dev/null @@ -1,33 +0,0 @@ -import baseIsEqual from './.internal/baseIsEqual.js' - -/** - * Performs a deep comparison between two values to determine if they are - * equivalent. - * - * **Note:** This method supports comparing arrays, array buffers, booleans, - * date objects, error objects, maps, numbers, `Object` objects, regexes, - * sets, strings, symbols, and typed arrays. `Object` objects are compared - * by their own, not inherited, enumerable properties. Functions and DOM - * nodes are compared by strict equality, i.e. `===`. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * const object = { 'a': 1 } - * const other = { 'a': 1 } - * - * isEqual(object, other) - * // => true - * - * object === other - * // => false - */ -function isEqual(value, other) { - return baseIsEqual(value, other) -} - -export default isEqual diff --git a/escape.js b/escape.js deleted file mode 100644 index eda499a1c0..0000000000 --- a/escape.js +++ /dev/null @@ -1,47 +0,0 @@ -/** Used to map characters to HTML entities. */ -const htmlEscapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' -} - -/** Used to match HTML entities and HTML characters. */ -const reUnescapedHtml = /[&<>"']/g -const reHasUnescapedHtml = RegExp(reUnescapedHtml.source) - -/** - * Converts the characters "&", "<", ">", '"', and "'" in `string` to their - * corresponding HTML entities. - * - * **Note:** No other characters are escaped. To escape additional - * characters use a third-party library like [_he_](https://mths.be/he). - * - * Though the ">" character is escaped for symmetry, characters like - * ">" and "/" don't need escaping in HTML and have no special meaning - * unless they're part of a tag or unquoted attribute value. See - * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) - * (under "semi-related fun fact") for more details. - * - * When working with HTML you should always - * [quote attribute values](http://wonko.com/post/html-escaping) to reduce - * XSS vectors. - * - * @since 0.1.0 - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @see escapeRegExp, unescape - * @example - * - * escape('fred, barney, & pebbles') - * // => 'fred, barney, & pebbles' - */ -function escape(string) { - return (string && reHasUnescapedHtml.test(string)) - ? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) - : (string || '') -} - -export default escape diff --git a/escapeRegExp.js b/escapeRegExp.js deleted file mode 100644 index 6c77d42dec..0000000000 --- a/escapeRegExp.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ -const reRegExpChar = /[\\^$.*+?()[\]{}|]/g -const reHasRegExpChar = RegExp(reRegExpChar.source) - -/** - * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", - * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to escape. - * @returns {string} Returns the escaped string. - * @see escape, escapeRegExp, unescape - * @example - * - * escapeRegExp('[lodash](https://lodash.com/)') - * // => '\[lodash\]\(https://lodash\.com/\)' - */ -function escapeRegExp(string) { - return (string && reHasRegExpChar.test(string)) - ? string.replace(reRegExpChar, '\\$&') - : (string || '') -} - -export default escapeRegExp diff --git a/every.js b/every.js deleted file mode 100644 index 045cde669c..0000000000 --- a/every.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Checks if `predicate` returns truthy for **all** elements of `array`. - * Iteration is stopped once `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, index, array). - * - * **Note:** This method returns `true` for - * [empty arrays](https://en.wikipedia.org/wiki/Empty_set) because - * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of - * elements of empty arrays. - * - * @since 5.0.0 - * @category Array - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all elements pass the predicate check, - * else `false`. - * @example - * - * every([true, 1, null, 'yes'], Boolean) - * // => false - */ -function every(array, predicate) { - let index = -1 - const length = array == null ? 0 : array.length - - while (++index < length) { - if (!predicate(array[index], index, array)) { - return false - } - } - return true -} - -export default every diff --git a/everyValue.js b/everyValue.js deleted file mode 100644 index bec5e7aacd..0000000000 --- a/everyValue.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Checks if `predicate` returns truthy for **all** properties of `object`. - * Iteration is stopped once `predicate` returns falsey. The predicate is - * invoked with three arguments: (value, key, object). - * - * **Note:** This method returns `true` for - * [empty objects](https://en.wikipedia.org/wiki/Empty_set) because - * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of - * elements of empty objects. - * - * @since 5.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if all properties pass the predicate check, - * else `false`. - * @example - * - * everyValue({ 'a': 0, 'b': 'yes', 'c': false }, Boolean) - * // => false - */ -function everyValue(object, predicate) { - object = Object(object) - const props = Object.keys(object) - - for (const key of props) { - if (!predicate(object[key], key, object)) { - return false - } - } - return true -} - -export default everyValue diff --git a/filter.js b/filter.js deleted file mode 100644 index 98606e5315..0000000000 --- a/filter.js +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Iterates over elements of `array`, returning an array of all elements - * `predicate` returns truthy for. The predicate is invoked with three - * arguments: (value, index, array). - * - * **Note:** Unlike `remove`, this method returns a new array. - * - * @since 5.0.0 - * @category Array - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see pull, pullAll, pullAllBy, pullAllWith, pullAt, remove, reject - * @example - * - * const users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false } - * ] - * - * filter(users, ({ active }) => active) - * // => objects for ['barney'] - */ -function filter(array, predicate) { - let index = -1 - let resIndex = 0 - const length = array == null ? 0 : array.length - const result = [] - - while (++index < length) { - const value = array[index] - if (predicate(value, index, array)) { - result[resIndex++] = value - } - } - return result -} - -export default filter diff --git a/filterObject.js b/filterObject.js deleted file mode 100644 index 08631f5712..0000000000 --- a/filterObject.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Iterates over properties of `object`, returning an array of all elements - * `predicate` returns truthy for. The predicate is invoked with three - * arguments: (value, key, object). - * - * If you want an object in return, consider `pickBy`. - * - * @since 5.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see pickBy, pull, pullAll, pullAllBy, pullAllWith, pullAt, remove, reject - * @example - * - * const object = { 'a': 5, 'b': 8, 'c': 10 } - * - * filterObject(object, (n) => !(n % 5)) - * // => [5, 10] - */ -function filterObject(object, predicate) { - object = Object(object) - const result = [] - - Object.keys(object).forEach((key) => { - const value = object[key] - if (predicate(value, key, object)) { - result.push(value) - } - }) - return result -} - -export default filterObject diff --git a/findKey.js b/findKey.js deleted file mode 100644 index 1cced32f1e..0000000000 --- a/findKey.js +++ /dev/null @@ -1,38 +0,0 @@ -/** - * This method is like `find` except that it returns the key of the first - * element `predicate` returns truthy for instead of the element itself. - * - * @since 1.1.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} predicate The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @see find, findIndex, findLast, findLastIndex, findLastKey - * @example - * - * const users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * } - * - * findKey(users, ({ age }) => age < 40) - * // => 'barney' (iteration order is not guaranteed) - */ -function findKey(object, predicate) { - let result - if (object == null) { - return result - } - Object.keys(object).some((key) => { - const value = object[key] - if (predicate(value, key, object)) { - result = key - return true - } - }) - return result -} - -export default findKey diff --git a/findLast.js b/findLast.js deleted file mode 100644 index 391b5e9c44..0000000000 --- a/findLast.js +++ /dev/null @@ -1,32 +0,0 @@ -import findLastIndex from './findLastIndex.js' -import isArrayLike from './isArrayLike.js' - -/** - * This method is like `find` except that it iterates over elements of - * `collection` from right to left. - * - * @since 2.0.0 - * @category Collection - * @param {Array|Object} collection The collection to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {number} [fromIndex=collection.length-1] The index to search from. - * @returns {*} Returns the matched element, else `undefined`. - * @see find, findIndex, findKey, findLastIndex, findLastKey - * @example - * - * findLast([1, 2, 3, 4], n => n % 2 == 1) - * // => 3 - */ -function findLast(collection, predicate, fromIndex) { - let iteratee - const iterable = Object(collection) - if (!isArrayLike(collection)) { - collection = Object.keys(collection) - iteratee = predicate - predicate = (key) => iteratee(iterable[key], key, iterable) - } - const index = findLastIndex(collection, predicate, fromIndex) - return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined -} - -export default findLast diff --git a/findLastIndex.js b/findLastIndex.js deleted file mode 100644 index f38fc082dc..0000000000 --- a/findLastIndex.js +++ /dev/null @@ -1,41 +0,0 @@ -import baseFindIndex from './.internal/baseFindIndex.js' -import toInteger from './toInteger.js' - -/** - * This method is like `findIndex` except that it iterates over elements - * of `collection` from right to left. - * - * @since 2.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} predicate The function invoked per iteration. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the found element, else `-1`. - * @see find, findIndex, findKey, findLast, findLastKey - * @example - * - * const users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false }, - * { 'user': 'pebbles', 'active': false } - * ] - * - * findLastIndex(users, ({ user }) => user == 'pebbles') - * // => 2 - */ -function findLastIndex(array, predicate, fromIndex) { - const length = array == null ? 0 : array.length - if (!length) { - return -1 - } - let index = length - 1 - if (fromIndex !== undefined) { - index = toInteger(fromIndex) - index = fromIndex < 0 - ? Math.max(length + index, 0) - : Math.min(index, length - 1) - } - return baseFindIndex(array, predicate, index, true) -} - -export default findLastIndex diff --git a/findLastKey.js b/findLastKey.js deleted file mode 100644 index 879f3ad5f1..0000000000 --- a/findLastKey.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseFindKey from './.internal/baseFindKey.js' -import baseForOwnRight from './.internal/baseForOwnRight.js' - -/** - * This method is like `findKey` except that it iterates over elements of - * a collection in the opposite order. - * - * @since 2.0.0 - * @category Object - * @param {Object} object The object to inspect. - * @param {Function} predicate The function invoked per iteration. - * @returns {string|undefined} Returns the key of the matched element, - * else `undefined`. - * @see find, findIndex, findKey, findLast, findLastIndex - * @example - * - * const users = { - * 'barney': { 'age': 36, 'active': true }, - * 'fred': { 'age': 40, 'active': false }, - * 'pebbles': { 'age': 1, 'active': true } - * } - * - * findLastKey(users, ({ age }) => age < 40) - * // => returns 'pebbles' assuming `findKey` returns 'barney' - */ -function findLastKey(object, predicate) { - return baseFindKey(object, predicate, baseForOwnRight) -} - -export default findLastKey diff --git a/first.js b/first.js deleted file mode 100644 index db707ff9a5..0000000000 --- a/first.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from './head.js' diff --git a/flatMap.js b/flatMap.js deleted file mode 100644 index 54d88448b1..0000000000 --- a/flatMap.js +++ /dev/null @@ -1,28 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' -import map from './map.js' - -/** - * Creates a flattened array of values by running each element in `collection` - * thru `iteratee` and flattening the mapped results. The iteratee is invoked - * with three arguments: (value, index|key, collection). - * - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new flattened array. - * @see flatMapDeep, flatMapDepth, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues - * @example - * - * function duplicate(n) { - * return [n, n] - * } - * - * flatMap([1, 2], duplicate) - * // => [1, 1, 2, 2] - */ -function flatMap(collection, iteratee) { - return baseFlatten(map(collection, iteratee), 1) -} - -export default flatMap diff --git a/flatMapDeep.js b/flatMapDeep.js deleted file mode 100644 index 9c9c5d855b..0000000000 --- a/flatMapDeep.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' -import map from './map.js' - -/** Used as references for various `Number` constants. */ -const INFINITY = 1 / 0 - -/** - * This method is like `flatMap` except that it recursively flattens the - * mapped results. - * - * @since 4.7.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new flattened array. - * @see flatMap, flatMapDepth, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues - * @example - * - * function duplicate(n) { - * return [[[n, n]]] - * } - * - * flatMapDeep([1, 2], duplicate) - * // => [1, 1, 2, 2] - */ -function flatMapDeep(collection, iteratee) { - return baseFlatten(map(collection, iteratee), INFINITY) -} - -export default flatMapDeep diff --git a/flatMapDepth.js b/flatMapDepth.js deleted file mode 100644 index 8a1ee20c93..0000000000 --- a/flatMapDepth.js +++ /dev/null @@ -1,29 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' -import map from './map.js' - -/** - * This method is like `flatMap` except that it recursively flattens the - * mapped results up to `depth` times. - * - * @since 4.7.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {number} [depth=1] The maximum recursion depth. - * @returns {Array} Returns the new flattened array. - * @see flatMap, flatMapDeep, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues - * @example - * - * function duplicate(n) { - * return [[[n, n]]] - * } - * - * flatMapDepth([1, 2], duplicate, 2) - * // => [[1, 1], [2, 2]] - */ -function flatMapDepth(collection, iteratee, depth) { - depth = depth === undefined ? 1 : +depth - return baseFlatten(map(collection, iteratee), depth) -} - -export default flatMapDepth diff --git a/flatten.js b/flatten.js deleted file mode 100644 index 75a834c844..0000000000 --- a/flatten.js +++ /dev/null @@ -1,21 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' - -/** - * Flattens `array` a single level deep. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to flatten. - * @returns {Array} Returns the new flattened array. - * @see flatMap, flatMapDeep, flatMapDepth, flattenDeep, flattenDepth - * @example - * - * flatten([1, [2, [3, [4]], 5]]) - * // => [1, 2, [3, [4]], 5] - */ -function flatten(array) { - const length = array == null ? 0 : array.length - return length ? baseFlatten(array, 1) : [] -} - -export default flatten diff --git a/flattenDeep.js b/flattenDeep.js deleted file mode 100644 index 8a96f58cf5..0000000000 --- a/flattenDeep.js +++ /dev/null @@ -1,24 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' - -/** Used as references for various `Number` constants. */ -const INFINITY = 1 / 0 - -/** - * Recursively flattens `array`. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to flatten. - * @returns {Array} Returns the new flattened array. - * @see flatMap, flatMapDeep, flatMapDepth, flatten, flattenDepth - * @example - * - * flattenDeep([1, [2, [3, [4]], 5]]) - * // => [1, 2, 3, 4, 5] - */ -function flattenDeep(array) { - const length = array == null ? 0 : array.length - return length ? baseFlatten(array, INFINITY) : [] -} - -export default flattenDeep diff --git a/flattenDepth.js b/flattenDepth.js deleted file mode 100644 index 0a8c4564b6..0000000000 --- a/flattenDepth.js +++ /dev/null @@ -1,31 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' - -/** - * Recursively flatten `array` up to `depth` times. - * - * @since 4.4.0 - * @category Array - * @param {Array} array The array to flatten. - * @param {number} [depth=1] The maximum recursion depth. - * @returns {Array} Returns the new flattened array. - * @see flatMap, flatMapDeep, flatMapDepth, flattenDeep - * @example - * - * const array = [1, [2, [3, [4]], 5]] - * - * flattenDepth(array, 1) - * // => [1, 2, [3, [4]], 5] - * - * flattenDepth(array, 2) - * // => [1, 2, 3, [4], 5] - */ -function flattenDepth(array, depth) { - const length = array == null ? 0 : array.length - if (!length) { - return [] - } - depth = depth === undefined ? 1 : +depth - return baseFlatten(array, depth) -} - -export default flattenDepth diff --git a/flip.js b/flip.js deleted file mode 100644 index 99297aa648..0000000000 --- a/flip.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Creates a function that invokes `func` with arguments reversed. - * - * @since 4.0.0 - * @category Function - * @param {Function} func The function to flip arguments for. - * @returns {Function} Returns the new flipped function. - * @see reverse - * @example - * - * const flipped = flip((...args) => args) - * - * flipped('a', 'b', 'c', 'd') - * // => ['d', 'c', 'b', 'a'] - */ -function flip(func) { - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - return function(...args) { - return func.apply(this, args.reverse()) - } -} - -export default flip diff --git a/floor.js b/floor.js deleted file mode 100644 index b9124a6ce5..0000000000 --- a/floor.js +++ /dev/null @@ -1,24 +0,0 @@ -import createRound from './.internal/createRound.js' - -/** - * Computes `number` rounded down to `precision`. - * - * @since 3.10.0 - * @category Math - * @param {number} number The number to round down. - * @param {number} [precision=0] The precision to round down to. - * @returns {number} Returns the rounded down number. - * @example - * - * floor(4.006) - * // => 4 - * - * floor(0.046, 2) - * // => 0.04 - * - * floor(4060, -2) - * // => 4000 - */ -const floor = createRound('floor') - -export default floor diff --git a/flow.js b/flow.js deleted file mode 100644 index c797a72477..0000000000 --- a/flow.js +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Composes a function that returns the result of invoking the given functions - * with the `this` binding of the created function, where each successive - * invocation is supplied the return value of the previous. - * - * @since 3.0.0 - * @category Util - * @param {Function[]} [funcs] The functions to invoke. - * @returns {Function} Returns the new composite function. - * @see flowRight - * @example - * - * import add from 'lodash/add' - * - * function square(n) { - * return n * n - * } - * - * const addSquare = flow(add, square) - * addSquare(1, 2) - * // => 9 - */ -function flow(...funcs) { - const length = funcs.length - let index = length - while (index--) { - if (typeof funcs[index] !== 'function') { - throw new TypeError('Expected a function') - } - } - return function(...args) { - let index = 0 - let result = length ? funcs[index].apply(this, args) : args[0] - while (++index < length) { - result = funcs[index].call(this, result) - } - return result - } -} - -export default flow diff --git a/flowRight.js b/flowRight.js deleted file mode 100644 index c1fafff785..0000000000 --- a/flowRight.js +++ /dev/null @@ -1,28 +0,0 @@ -import flow from './flow.js' - -/** - * This method is like `flow` except that it composes a function that - * invokes the given functions from right to left. - * - * @since 3.0.0 - * @category Util - * @param {Function[]} [funcs] The functions to invoke. - * @returns {Function} Returns the new composite function. - * @see flow - * @example - * - * import add from 'lodash/add' - * - * function square(n) { - * return n * n - * } - * - * const addSquare = flowRight(square, add) - * addSquare(1, 2) - * // => 9 - */ -function flowRight(...funcs) { - return flow(...funcs.reverse()) -} - -export default flowRight diff --git a/forEach.js b/forEach.js deleted file mode 100644 index facccb07a7..0000000000 --- a/forEach.js +++ /dev/null @@ -1,33 +0,0 @@ -import arrayEach from './.internal/arrayEach.js' -import baseEach from './.internal/baseEach.js' - -/** - * Iterates over elements of `collection` and invokes `iteratee` for each element. - * The iteratee is invoked with three arguments: (value, index|key, collection). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * **Note:** As with other "Collections" methods, objects with a "length" - * property are iterated like arrays. To avoid this behavior use `forIn` - * or `forOwn` for object iteration. - * - * @since 0.1.0 - * @alias each - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see forEachRight, forIn, forInRight, forOwn, forOwnRight - * @example - * - * forEach([1, 2], value => console.log(value)) - * // => Logs `1` then `2`. - * - * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ -function forEach(collection, iteratee) { - const func = Array.isArray(collection) ? arrayEach : baseEach - return func(collection, iteratee) -} - -export default forEach diff --git a/forEachRight.js b/forEachRight.js deleted file mode 100644 index b8682b94b1..0000000000 --- a/forEachRight.js +++ /dev/null @@ -1,25 +0,0 @@ -import arrayEachRight from './.internal/arrayEachRight.js' -import baseEachRight from './.internal/baseEachRight.js' - -/** - * This method is like `forEach` except that it iterates over elements of - * `collection` from right to left. - * - * @since 2.0.0 - * @alias eachRight - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array|Object} Returns `collection`. - * @see forEach, forIn, forInRight, forOwn, forOwnRight - * @example - * - * forEachRight([1, 2], value => console.log(value)) - * // => Logs `2` then `1`. - */ -function forEachRight(collection, iteratee) { - const func = Array.isArray(collection) ? arrayEachRight : baseEachRight - return func(collection, iteratee) -} - -export default forEachRight diff --git a/forOwn.js b/forOwn.js deleted file mode 100644 index 40ea48b251..0000000000 --- a/forOwn.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Iterates over own enumerable string keyed properties of an object and - * invokes `iteratee` for each property. The iteratee is invoked with three - * arguments: (value, key, object). Iteratee functions may exit iteration - * early by explicitly returning `false`. - * - * @since 0.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @see forEach, forEachRight, forIn, forInRight, forOwnRight - * @example - * - * function Foo() { - * this.a = 1 - * this.b = 2 - * } - * - * Foo.prototype.c = 3 - * - * forOwn(new Foo, function(value, key) { - * console.log(key) - * }) - * // => Logs 'a' then 'b' (iteration order is not guaranteed). - */ -function forOwn(object, iteratee) { - object = Object(object) - Object.keys(object).forEach((key) => iteratee(object[key], key, object)) -} - -export default forOwn diff --git a/forOwnRight.js b/forOwnRight.js deleted file mode 100644 index 5fbfbd5e16..0000000000 --- a/forOwnRight.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * This method is like `forOwn` except that it iterates over properties of - * `object` in the opposite order. - * - * @since 2.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns `object`. - * @see forEach, forEachRight, forIn, forInRight, forOwn - * @example - * - * function Foo() { - * this.a = 1 - * this.b = 2 - * } - * - * Foo.prototype.c = 3 - * - * forOwnRight(new Foo, function(value, key) { - * console.log(key) - * }) - * // => Logs 'b' then 'a' assuming `forOwn` logs 'a' then 'b'. - */ -function forOwnRight(object, iteratee) { - if (object == null) { - return - } - const props = Object.keys(object) - let length = props.length - while (length--) { - iteratee(object[props[length]], iteratee, object) - } -} - -export default forOwnRight diff --git a/fromEntries.js b/fromEntries.js deleted file mode 100644 index cd21ba02a9..0000000000 --- a/fromEntries.js +++ /dev/null @@ -1,25 +0,0 @@ -/** - * The inverse of `entries`is method returns an object composed - * from key-value `pairs`. - * - * @since 4.0.0 - * @category Array - * @param {Array} pairs The key-value pairs. - * @returns {Object} Returns the new object. - * @example - * - * fromEntries([['a', 1], ['b', 2]]) - * // => { 'a': 1, 'b': 2 } - */ -function fromEntries(pairs) { - const result = {} - if (pairs == null) { - return result - } - for (const pair of pairs) { - result[pair[0]] = pair[1] - } - return result -} - -export default fromEntries diff --git a/functions.js b/functions.js deleted file mode 100644 index f59d8a737f..0000000000 --- a/functions.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Creates an array of function property names from own enumerable properties - * of `object`. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The object to inspect. - * @returns {Array} Returns the function names. - * @see functionsIn - * @example - * - * function Foo() { - * this.a = () => 'a' - * this.b = () => 'b' - * } - * - * Foo.prototype.c = () => 'c' - * - * functions(new Foo) - * // => ['a', 'b'] - */ -function functions(object) { - if (object == null) { - return [] - } - return Object.keys(object).filter((key) => typeof object[key] === 'function') -} - -export default functions diff --git a/get.js b/get.js deleted file mode 100644 index ca4354c410..0000000000 --- a/get.js +++ /dev/null @@ -1,32 +0,0 @@ -import baseGet from './.internal/baseGet.js' - -/** - * Gets the value at `path` of `object`. If the resolved value is - * `undefined`, the `defaultValue` is returned in its place. - * - * @since 3.7.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to get. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @see has, hasIn, set, unset - * @example - * - * const object = { 'a': [{ 'b': { 'c': 3 } }] } - * - * get(object, 'a[0].b.c') - * // => 3 - * - * get(object, ['a', '0', 'b', 'c']) - * // => 3 - * - * get(object, 'a.b.c', 'default') - * // => 'default' - */ -function get(object, path, defaultValue) { - const result = object == null ? undefined : baseGet(object, path) - return result === undefined ? defaultValue : result -} - -export default get diff --git a/groupBy.js b/groupBy.js deleted file mode 100644 index 03b5ea557f..0000000000 --- a/groupBy.js +++ /dev/null @@ -1,36 +0,0 @@ -import baseAssignValue from './.internal/baseAssignValue.js' -import reduce from './reduce.js' - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The order of grouped values - * is determined by the order they occur in `collection`. The corresponding - * value of each key is an array of elements responsible for generating the - * key. The iteratee is invoked with one argument: (value). - * - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @example - * - * groupBy([6.1, 4.2, 6.3], Math.floor) - * // => { '4': [4.2], '6': [6.1, 6.3] } - */ -function groupBy(collection, iteratee) { - return reduce(collection, (result, value, key) => { - key = iteratee(value) - if (hasOwnProperty.call(result, key)) { - result[key].push(value) - } else { - baseAssignValue(result, key, [value]) - } - return result - }, {}) -} - -export default groupBy diff --git a/gt.js b/gt.js deleted file mode 100644 index 5359e1665a..0000000000 --- a/gt.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Checks if `value` is greater than `other`. - * - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than `other`, - * else `false`. - * @see gte, lt, lte - * @example - * - * gt(3, 1) - * // => true - * - * gt(3, 3) - * // => false - * - * gt(1, 3) - * // => false - */ -function gt(value, other) { - if (!(typeof value === 'string' && typeof other === 'string')) { - value = +value - other = +other - } - return value > other -} - -export default gt diff --git a/gte.js b/gte.js deleted file mode 100644 index b6fa0e572c..0000000000 --- a/gte.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Checks if `value` is greater than or equal to `other`. - * - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is greater than or equal to - * `other`, else `false`. - * @see gt, lt, lte - * @example - * - * gte(3, 1) - * // => true - * - * gte(3, 3) - * // => true - * - * gte(1, 3) - * // => false - */ -function gte(value, other) { - if (!(typeof value === 'string' && typeof other === 'string')) { - value = +value - other = +other - } - return value >= other -} - -export default gte diff --git a/has.js b/has.js deleted file mode 100644 index 9cec79d5ac..0000000000 --- a/has.js +++ /dev/null @@ -1,28 +0,0 @@ -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * Checks if `key` is a direct property of `object`. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The object to query. - * @param {string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - * @see hasIn, hasPath, hasPathIn - * @example - * - * const object = { 'a': { 'b': 2 } } - * const other = create({ 'a': create({ 'b': 2 }) }) - * - * has(object, 'a') - * // => true - * - * has(other, 'a') - * // => false - */ -function has(object, key) { - return object != null && hasOwnProperty.call(object, key) -} - -export default has diff --git a/hasIn.js b/hasIn.js deleted file mode 100644 index 71d35d5113..0000000000 --- a/hasIn.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Checks if `path` is a direct or inherited property of `object`. - * - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {string} key The key to check. - * @returns {boolean} Returns `true` if `key` exists, else `false`. - * @see has, hasPath, hasPathIn - * @example - * - * const object = create({ 'a': create({ 'b': 2 }) }) - * - * hasIn(object, 'a') - * // => true - * - * hasIn(object, 'b') - * // => false - */ -function hasIn(object, key) { - return object != null && key in Object(object) -} - -export default hasIn diff --git a/hasPath.js b/hasPath.js deleted file mode 100644 index 2a3a7fb6fc..0000000000 --- a/hasPath.js +++ /dev/null @@ -1,53 +0,0 @@ -import castPath from './.internal/castPath.js' -import isArguments from './isArguments.js' -import isIndex from './.internal/isIndex.js' -import isLength from './isLength.js' -import toKey from './.internal/toKey.js' - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * Checks if `path` is a direct property of `object`. - * - * @since 5.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @see has, hasIn, hasPathIn - * @example - * - * const object = { 'a': { 'b': 2 } } - * const other = create({ 'a': create({ 'b': 2 }) }) - * - * hasPath(object, 'a.b') - * // => true - * - * hasPath(object, ['a', 'b']) - * // => true - */ -function hasPath(object, path) { - path = castPath(path, object) - - let index = -1 - let { length } = path - let result = false - let key - - while (++index < length) { - key = toKey(path[index]) - if (!(result = object != null && hasOwnProperty.call(object, key))) { - break - } - object = object[key] - } - if (result || ++index != length) { - return result - } - length = object == null ? 0 : object.length - return !!length && isLength(length) && isIndex(key, length) && - (Array.isArray(object) || isArguments(object)) -} - -export default hasPath diff --git a/hasPathIn.js b/hasPathIn.js deleted file mode 100644 index 5b9b5a2666..0000000000 --- a/hasPathIn.js +++ /dev/null @@ -1,50 +0,0 @@ -import castPath from './.internal/castPath.js' -import isArguments from './isArguments.js' -import isIndex from './.internal/isIndex.js' -import isLength from './isLength.js' -import toKey from './.internal/toKey.js' - -/** - * Checks if `path` is a direct property of `object`. - * - * @since 5.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path to check. - * @returns {boolean} Returns `true` if `path` exists, else `false`. - * @see has, hasIn hasPath - * @example - * - * const object = { 'a': { 'b': 2 } } - * const other = create({ 'a': create({ 'b': 2 }) }) - * - * hasPathIn(object, 'a.b') - * // => true - * - * hasPathIn(object, ['a', 'b']) - * // => true - */ -function hasPathIn(object, path) { - path = castPath(path, object) - - let index = -1 - let { length } = path - let result = false - let key - - while (++index < length) { - key = toKey(path[index]) - if (!(result = object != null && key in Object(object))) { - break - } - object = object[key] - } - if (result || ++index != length) { - return result - } - length = object == null ? 0 : object.length - return !!length && isLength(length) && isIndex(key, length) && - (Array.isArray(object) || isArguments(object)) -} - -export default hasPathIn diff --git a/head.js b/head.js deleted file mode 100644 index a9df4111f3..0000000000 --- a/head.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Gets the first element of `array`. - * - * @since 0.1.0 - * @alias first - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the first element of `array`. - * @see last - * @example - * - * head([1, 2, 3]) - * // => 1 - * - * head([]) - * // => undefined - */ -function head(array) { - return (array != null && array.length) - ? array[0] - : undefined -} - -export default head diff --git a/inRange.js b/inRange.js deleted file mode 100644 index ce2962de0e..0000000000 --- a/inRange.js +++ /dev/null @@ -1,47 +0,0 @@ -import baseInRange from './.internal/baseInRange.js' - -/** - * Checks if `number` is between `start` and up to, but not including, `end`. If - * `end` is not specified, it's set to `start` with `start` then set to `0`. - * If `start` is greater than `end` the params are swapped to support - * negative ranges. - * - * @since 3.3.0 - * @category Number - * @param {number} number The number to check. - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @returns {boolean} Returns `true` if `number` is in the range, else `false`. - * @see range, rangeRight - * @example - * - * inRange(3, 2, 4) - * // => true - * - * inRange(4, 8) - * // => true - * - * inRange(4, 2) - * // => false - * - * inRange(2, 2) - * // => false - * - * inRange(1.2, 2) - * // => true - * - * inRange(5.2, 4) - * // => false - * - * inRange(-3, -2, -6) - * // => true - */ -function inRange(number, start, end) { - if (end === undefined) { - end = start - start = 0 - } - return baseInRange(+number, +start, +end) -} - -export default inRange diff --git a/indexOf.js b/indexOf.js deleted file mode 100644 index 358fff4ccd..0000000000 --- a/indexOf.js +++ /dev/null @@ -1,37 +0,0 @@ -import baseIndexOf from './.internal/baseIndexOf.js' -import toInteger from './toInteger.js' - -/** - * Gets the index at which the first occurrence of `value` is found in `array` - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. If `fromIndex` is negative, it's used as the - * offset from the end of `array`. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=0] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * indexOf([1, 2, 1, 2], 2) - * // => 1 - * - * // Search from the `fromIndex`. - * indexOf([1, 2, 1, 2], 2, 2) - * // => 3 - */ -function indexOf(array, value, fromIndex) { - const length = array == null ? 0 : array.length - if (!length) { - return -1 - } - let index = fromIndex == null ? 0 : toInteger(fromIndex) - if (index < 0) { - index = Math.max(length + index, 0) - } - return baseIndexOf(array, value, index) -} - -export default indexOf diff --git a/initial.js b/initial.js deleted file mode 100644 index 1a4b1d8371..0000000000 --- a/initial.js +++ /dev/null @@ -1,20 +0,0 @@ -import slice from './slice.js' - -/** - * Gets all but the last element of `array`. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * initial([1, 2, 3]) - * // => [1, 2] - */ -function initial(array) { - const length = array == null ? 0 : array.length - return length ? slice(array, 0, -1) : [] -} - -export default initial diff --git a/intersection.js b/intersection.js deleted file mode 100644 index 0b16d3b4ae..0000000000 --- a/intersection.js +++ /dev/null @@ -1,27 +0,0 @@ -import map from './map.js' -import baseIntersection from './.internal/baseIntersection.js' -import castArrayLikeObject from './.internal/castArrayLikeObject.js' - -/** - * Creates an array of unique values that are included in all given arrays - * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. The order and references of result values are - * determined by the first array. - * - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * intersection([2, 1], [2, 3]) - * // => [2] - */ -function intersection(...arrays) { - const mapped = map(arrays, castArrayLikeObject) - return (mapped.length && mapped[0] === arrays[0]) - ? baseIntersection(mapped) - : [] -} - -export default intersection diff --git a/intersectionBy.js b/intersectionBy.js deleted file mode 100644 index 3f1f852460..0000000000 --- a/intersectionBy.js +++ /dev/null @@ -1,37 +0,0 @@ -import map from './map.js' -import baseIntersection from './.internal/baseIntersection.js' -import castArrayLikeObject from './.internal/castArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `intersection` except that it accepts `iteratee` - * which is invoked for each element of each `arrays` to generate the criterion - * by which they're compared. The order and references of result values are - * determined by the first array. The iteratee is invoked with one argument: - * (value). - * - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor) - * // => [2.1] - */ -function intersectionBy(...arrays) { - let iteratee = last(arrays) - const mapped = map(arrays, castArrayLikeObject) - - if (iteratee === last(mapped)) { - iteratee = undefined - } else { - mapped.pop() - } - return (mapped.length && mapped[0] === arrays[0]) - ? baseIntersection(mapped, iteratee) - : [] -} - -export default intersectionBy diff --git a/intersectionWith.js b/intersectionWith.js deleted file mode 100644 index 7621de8c3f..0000000000 --- a/intersectionWith.js +++ /dev/null @@ -1,38 +0,0 @@ -import map from './map.js' -import baseIntersection from './.internal/baseIntersection.js' -import castArrayLikeObject from './.internal/castArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `intersection` except that it accepts `comparator` - * which is invoked to compare elements of `arrays`. The order and references - * of result values are determined by the first array. The comparator is - * invoked with two arguments: (arrVal, othVal). - * - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of intersecting values. - * @example - * - * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] - * const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }] - * - * intersectionWith(objects, others, isEqual) - * // => [{ 'x': 1, 'y': 2 }] - */ -function intersectionWith(...arrays) { - let comparator = last(arrays) - const mapped = map(arrays, castArrayLikeObject) - - comparator = typeof comparator === 'function' ? comparator : undefined - if (comparator) { - mapped.pop() - } - return (mapped.length && mapped[0] === arrays[0]) - ? baseIntersection(mapped, undefined, comparator) - : [] -} - -export default intersectionWith diff --git a/invert.js b/invert.js deleted file mode 100644 index f6e67398c8..0000000000 --- a/invert.js +++ /dev/null @@ -1,31 +0,0 @@ -const toString = Object.prototype.toString - -/** - * Creates an object composed of the inverted keys and values of `object`. - * If `object` contains duplicate values, subsequent values overwrite - * property assignments of previous values. - * - * @since 0.7.0 - * @category Object - * @param {Object} object The object to invert. - * @returns {Object} Returns the new inverted object. - * @example - * - * const object = { 'a': 1, 'b': 2, 'c': 1 } - * - * invert(object) - * // => { '1': 'c', '2': 'b' } - */ -function invert(object) { - const result = {} - Object.keys(object).forEach((key) => { - let value = object[key] - if (value != null && typeof value.toString !== 'function') { - value = toString.call(value) - } - result[value] = key - }) - return result -} - -export default invert diff --git a/invertBy.js b/invertBy.js deleted file mode 100644 index 856f9f77fc..0000000000 --- a/invertBy.js +++ /dev/null @@ -1,36 +0,0 @@ -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * This method is like `invert` except that the inverted object is generated - * from the results of running each element of `object` thru `iteratee`. The - * corresponding inverted value of each inverted key is an array of keys - * responsible for generating the inverted value. The iteratee is invoked - * with one argument: (value). - * - * @since 4.1.0 - * @category Object - * @param {Object} object The object to invert. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Object} Returns the new inverted object. - * @example - * - * const object = { 'a': 1, 'b': 2, 'c': 1 } - * - * invertBy(object, value => `group${value}`) - * // => { 'group1': ['a', 'c'], 'group2': ['b'] } - */ -function invertBy(object, iteratee) { - const result = {} - Object.keys(object).forEach((key) => { - const value = iteratee(object[key]) - if (hasOwnProperty.call(result, value)) { - result[value].push(key) - } else { - result[value] = [key] - } - }) - return result -} - -export default invertBy diff --git a/invoke.js b/invoke.js deleted file mode 100644 index 6767b99d2f..0000000000 --- a/invoke.js +++ /dev/null @@ -1,29 +0,0 @@ -import castPath from './.internal/castPath.js' -import last from './last.js' -import parent from './.internal/parent.js' -import toKey from './.internal/toKey.js' - -/** - * Invokes the method at `path` of `object`. - * - * @since 4.0.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the method to invoke. - * @param {Array} [args] The arguments to invoke the method with. - * @returns {*} Returns the result of the invoked method. - * @example - * - * const object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] } - * - * invoke(object, 'a[0].b.c.slice', [1, 3]) - * // => [2, 3] - */ -function invoke(object, path, args) { - path = castPath(path, object) - object = parent(object, path) - const func = object == null ? object : object[toKey(last(path))] - return func == null ? undefined : func.apply(object, args) -} - -export default invoke diff --git a/invokeMap.js b/invokeMap.js deleted file mode 100644 index f52890b494..0000000000 --- a/invokeMap.js +++ /dev/null @@ -1,37 +0,0 @@ -import baseEach from './.internal/baseEach.js' -import invoke from './invoke.js' -import isArrayLike from './isArrayLike.js' - -/** - * Invokes the method at `path` of each element in `collection`, returning - * an array of the results of each invoked method. Any additional arguments - * are provided to each invoked method. If `path` is a function, it's invoked - * for, and `this` bound to, each element in `collection`. - * - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array|Function|string} path The path of the method to invoke or - * the function invoked per iteration. - * @param {Array} [args] The arguments to invoke each method with. - * @returns {Array} Returns the array of results. - * @example - * - * invokeMap([[5, 1, 7], [3, 2, 1]], 'sort') - * // => [[1, 5, 7], [1, 2, 3]] - * - * invokeMap([123, 456], String.prototype.split, ['']) - * // => [['1', '2', '3'], ['4', '5', '6']] - */ -function invokeMap(collection, path, args) { - let index = -1 - const isFunc = typeof path === 'function' - const result = isArrayLike(collection) ? new Array(collection.length) : [] - - baseEach(collection, (value) => { - result[++index] = isFunc ? path.apply(value, args) : invoke(value, path, args) - }) - return result -} - -export default invokeMap diff --git a/isArguments.js b/isArguments.js deleted file mode 100644 index acddb7fcb6..0000000000 --- a/isArguments.js +++ /dev/null @@ -1,23 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' - -/** - * Checks if `value` is likely an `arguments` object. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`. - * @example - * - * isArguments(function() { return arguments }()) - * // => true - * - * isArguments([1, 2, 3]) - * // => false - */ -function isArguments(value) { - return isObjectLike(value) && getTag(value) == '[object Arguments]' -} - -export default isArguments diff --git a/isArrayBuffer.js b/isArrayBuffer.js deleted file mode 100644 index add5067c7e..0000000000 --- a/isArrayBuffer.js +++ /dev/null @@ -1,27 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' -import nodeTypes from './.internal/nodeTypes.js' - -/* Node.js helper references. */ -const nodeIsArrayBuffer = nodeTypes && nodeTypes.isArrayBuffer - -/** - * Checks if `value` is classified as an `ArrayBuffer` object. - * - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. - * @example - * - * isArrayBuffer(new ArrayBuffer(2)) - * // => true - * - * isArrayBuffer(new Array(2)) - * // => false - */ -const isArrayBuffer = nodeIsArrayBuffer - ? (value) => nodeIsArrayBuffer(value) - : (value) => isObjectLike(value) && getTag(value) == '[object ArrayBuffer]' - -export default isArrayBuffer diff --git a/isArrayLike.js b/isArrayLike.js deleted file mode 100644 index ab72a63538..0000000000 --- a/isArrayLike.js +++ /dev/null @@ -1,30 +0,0 @@ -import isLength from './isLength.js' - -/** - * Checks if `value` is array-like. A value is considered array-like if it's - * not a function and has a `value.length` that's an integer greater than or - * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is array-like, else `false`. - * @example - * - * isArrayLike([1, 2, 3]) - * // => true - * - * isArrayLike(document.body.children) - * // => true - * - * isArrayLike('abc') - * // => true - * - * isArrayLike(Function) - * // => false - */ -function isArrayLike(value) { - return value != null && typeof value !== 'function' && isLength(value.length) -} - -export default isArrayLike diff --git a/isArrayLikeObject.js b/isArrayLikeObject.js deleted file mode 100644 index 50ac56bd2b..0000000000 --- a/isArrayLikeObject.js +++ /dev/null @@ -1,31 +0,0 @@ -import isArrayLike from './isArrayLike.js' -import isObjectLike from './isObjectLike.js' - -/** - * This method is like `isArrayLike` except that it also checks if `value` - * is an object. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an array-like object, - * else `false`. - * @example - * - * isArrayLikeObject([1, 2, 3]) - * // => true - * - * isArrayLikeObject(document.body.children) - * // => true - * - * isArrayLikeObject('abc') - * // => false - * - * isArrayLikeObject(Function) - * // => false - */ -function isArrayLikeObject(value) { - return isObjectLike(value) && isArrayLike(value) -} - -export default isArrayLikeObject diff --git a/isBoolean.js b/isBoolean.js deleted file mode 100644 index 04a5c530bd..0000000000 --- a/isBoolean.js +++ /dev/null @@ -1,24 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' - -/** - * Checks if `value` is classified as a boolean primitive or object. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. - * @example - * - * isBoolean(false) - * // => true - * - * isBoolean(null) - * // => false - */ -function isBoolean(value) { - return value === true || value === false || - (isObjectLike(value) && getTag(value) == '[object Boolean]') -} - -export default isBoolean diff --git a/isBuffer.js b/isBuffer.js deleted file mode 100644 index 1831fe3a22..0000000000 --- a/isBuffer.js +++ /dev/null @@ -1,35 +0,0 @@ -import root from './.internal/root.js' - -/** Detect free variable `exports`. */ -const freeExports = typeof exports === 'object' && exports !== null && !exports.nodeType && exports - -/** Detect free variable `module`. */ -const freeModule = freeExports && typeof module === 'object' && module !== null && !module.nodeType && module - -/** Detect the popular CommonJS extension `module.exports`. */ -const moduleExports = freeModule && freeModule.exports === freeExports - -/** Built-in value references. */ -const Buffer = moduleExports ? root.Buffer : undefined - -/* Built-in method references for those with the same name as other `lodash` methods. */ -const nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined - -/** - * Checks if `value` is a buffer. - * - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. - * @example - * - * isBuffer(new Buffer(2)) - * // => true - * - * isBuffer(new Uint8Array(2)) - * // => false - */ -const isBuffer = nativeIsBuffer || (() => false) - -export default isBuffer diff --git a/isDate.js b/isDate.js deleted file mode 100644 index 1486be6330..0000000000 --- a/isDate.js +++ /dev/null @@ -1,27 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' -import nodeTypes from './.internal/nodeTypes.js' - -/* Node.js helper references. */ -const nodeIsDate = nodeTypes && nodeTypes.isDate - -/** - * Checks if `value` is classified as a `Date` object. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a date object, else `false`. - * @example - * - * isDate(new Date) - * // => true - * - * isDate('Mon April 23 2012') - * // => false - */ -const isDate = nodeIsDate - ? (value) => nodeIsDate(value) - : (value) => isObjectLike(value) && getTag(value) == '[object Date]' - -export default isDate diff --git a/isElement.js b/isElement.js deleted file mode 100644 index 636d792eda..0000000000 --- a/isElement.js +++ /dev/null @@ -1,23 +0,0 @@ -import isObjectLike from './isObjectLike.js' -import isPlainObject from './isPlainObject.js' - -/** - * Checks if `value` is likely a DOM element. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. - * @example - * - * isElement(document.body) - * // => true - * - * isElement('') - * // => false - */ -function isElement(value) { - return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value) -} - -export default isElement diff --git a/isEmpty.js b/isEmpty.js deleted file mode 100644 index 346b051405..0000000000 --- a/isEmpty.js +++ /dev/null @@ -1,69 +0,0 @@ -import getTag from './.internal/getTag.js' -import isArguments from './isArguments.js' -import isArrayLike from './isArrayLike.js' -import isBuffer from './isBuffer.js' -import isPrototype from './.internal/isPrototype.js' -import isTypedArray from './isTypedArray.js' - -/** Used to check objects for own properties. */ -const hasOwnProperty = Object.prototype.hasOwnProperty - -/** - * Checks if `value` is an empty object, collection, map, or set. - * - * Objects are considered empty if they have no own enumerable string keyed - * properties. - * - * Array-like values such as `arguments` objects, arrays, buffers, strings, or - * jQuery-like collections are considered empty if they have a `length` of `0`. - * Similarly, maps and sets are considered empty if they have a `size` of `0`. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is empty, else `false`. - * @example - * - * isEmpty(null) - * // => true - * - * isEmpty(true) - * // => true - * - * isEmpty(1) - * // => true - * - * isEmpty([1, 2, 3]) - * // => false - * - * isEmpty('abc') - * // => false - * - * isEmpty({ 'a': 1 }) - * // => false - */ -function isEmpty(value) { - if (value == null) { - return true - } - if (isArrayLike(value) && - (Array.isArray(value) || typeof value === 'string' || typeof value.splice === 'function' || - isBuffer(value) || isTypedArray(value) || isArguments(value))) { - return !value.length - } - const tag = getTag(value) - if (tag == '[object Map]' || tag == '[object Set]') { - return !value.size - } - if (isPrototype(value)) { - return !Object.keys(value).length - } - for (const key in value) { - if (hasOwnProperty.call(value, key)) { - return false - } - } - return true -} - -export default isEmpty diff --git a/isEqualWith.js b/isEqualWith.js deleted file mode 100644 index 98ab4f7b01..0000000000 --- a/isEqualWith.js +++ /dev/null @@ -1,39 +0,0 @@ -import baseIsEqual from './.internal/baseIsEqual.js' - -/** - * This method is like `isEqual` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with up to - * six arguments: (objValue, othValue [, index|key, object, other, stack]). - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if the values are equivalent, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value) - * } - * - * function customizer(objValue, othValue) { - * if (isGreeting(objValue) && isGreeting(othValue)) { - * return true - * } - * } - * - * const array = ['hello', 'goodbye'] - * const other = ['hi', 'goodbye'] - * - * isEqualWith(array, other, customizer) - * // => true - */ -function isEqualWith(value, other, customizer) { - customizer = typeof customizer === 'function' ? customizer : undefined - const result = customizer ? customizer(value, other) : undefined - return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result -} - -export default isEqualWith diff --git a/isError.js b/isError.js deleted file mode 100644 index 1e85ff5a80..0000000000 --- a/isError.js +++ /dev/null @@ -1,30 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' -import isPlainObject from './isPlainObject.js' - -/** - * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, - * `SyntaxError`, `TypeError`, or `URIError` object. - * - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an error object, else `false`. - * @example - * - * isError(new Error) - * // => true - * - * isError(Error) - * // => false - */ -function isError(value) { - if (!isObjectLike(value)) { - return false - } - const tag = getTag(value) - return tag == '[object Error]' || tag == '[object DOMException]' || - (typeof value.message === 'string' && typeof value.name === 'string' && !isPlainObject(value)) -} - -export default isError diff --git a/isFunction.js b/isFunction.js deleted file mode 100644 index be3f2f89f3..0000000000 --- a/isFunction.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Checks if `value` is classified as a `Function` object. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a function, else `false`. - * @example - * - * isFunction(class Any{}) - * // => true - * - * isFunction(() => {}) - * // => true - * - * isFunction(async () => {}) - * // => true - * - * isFunction(function * Any() {}) - * // => true - * - * isFunction(Math.round) - * // => true - * - * isFunction(/abc/) - * // => false - */ -function isFunction(value) { - return typeof value === 'function' -} - -export default isFunction diff --git a/isLength.js b/isLength.js deleted file mode 100644 index 193f254e87..0000000000 --- a/isLength.js +++ /dev/null @@ -1,33 +0,0 @@ -/** Used as references for various `Number` constants. */ -const MAX_SAFE_INTEGER = 9007199254740991 - -/** - * Checks if `value` is a valid array-like length. - * - * **Note:** This method is loosely based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. - * @example - * - * isLength(3) - * // => true - * - * isLength(Number.MIN_VALUE) - * // => false - * - * isLength(Infinity) - * // => false - * - * isLength('3') - * // => false - */ -function isLength(value) { - return typeof value === 'number' && - value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER -} - -export default isLength diff --git a/isMap.js b/isMap.js deleted file mode 100644 index 1b59892e22..0000000000 --- a/isMap.js +++ /dev/null @@ -1,27 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' -import nodeTypes from './.internal/nodeTypes.js' - -/* Node.js helper references. */ -const nodeIsMap = nodeTypes && nodeTypes.isMap - -/** - * Checks if `value` is classified as a `Map` object. - * - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a map, else `false`. - * @example - * - * isMap(new Map) - * // => true - * - * isMap(new WeakMap) - * // => false - */ -const isMap = nodeIsMap - ? (value) => nodeIsMap(value) - : (value) => isObjectLike(value) && getTag(value) == '[object Map]' - -export default isMap diff --git a/isMatch.js b/isMatch.js deleted file mode 100644 index a9c46ad218..0000000000 --- a/isMatch.js +++ /dev/null @@ -1,34 +0,0 @@ -import baseIsMatch from './.internal/baseIsMatch.js' -import getMatchData from './.internal/getMatchData.js' - -/** - * Performs a partial deep comparison between `object` and `source` to - * determine if `object` contains equivalent property values. - * - * **Note:** This method is equivalent to `matches` when `source` is - * partially applied. - * - * Partial comparisons will match empty array and empty object `source` - * values against any array or object value, respectively. See `isEqual` - * for a list of supported value comparisons. - * - * @since 3.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * const object = { 'a': 1, 'b': 2 } - * - * isMatch(object, { 'b': 2 }) - * // => true - * - * isMatch(object, { 'b': 1 }) - * // => false - */ -function isMatch(object, source) { - return object === source || baseIsMatch(object, source, getMatchData(source)) -} - -export default isMatch diff --git a/isMatchWith.js b/isMatchWith.js deleted file mode 100644 index 79ef07a57a..0000000000 --- a/isMatchWith.js +++ /dev/null @@ -1,39 +0,0 @@ -import baseIsMatch from './.internal/baseIsMatch.js' -import getMatchData from './.internal/getMatchData.js' - -/** - * This method is like `isMatch` except that it accepts `customizer` which - * is invoked to compare values. If `customizer` returns `undefined`, comparisons - * are handled by the method instead. The `customizer` is invoked with five - * arguments: (objValue, srcValue, index|key, object, source). - * - * @since 4.0.0 - * @category Lang - * @param {Object} object The object to inspect. - * @param {Object} source The object of property values to match. - * @param {Function} [customizer] The function to customize comparisons. - * @returns {boolean} Returns `true` if `object` is a match, else `false`. - * @example - * - * function isGreeting(value) { - * return /^h(?:i|ello)$/.test(value) - * } - * - * function customizer(objValue, srcValue) { - * if (isGreeting(objValue) && isGreeting(srcValue)) { - * return true - * } - * } - * - * const object = { 'greeting': 'hello' } - * const source = { 'greeting': 'hi' } - * - * isMatchWith(object, source, customizer) - * // => true - */ -function isMatchWith(object, source, customizer) { - customizer = typeof customizer === 'function' ? customizer : undefined - return baseIsMatch(object, source, getMatchData(source), customizer) -} - -export default isMatchWith diff --git a/isNative.js b/isNative.js deleted file mode 100644 index 2bcd268166..0000000000 --- a/isNative.js +++ /dev/null @@ -1,36 +0,0 @@ -import isObject from './isObject.js' - -/** - * Used to match `RegExp` - * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). - */ -const reRegExpChar = /[\\^$.*+?()[\]{}|]/g - -/** Used to detect if a method is native. */ -const reIsNative = RegExp(`^${ - Function.prototype.toString.call(Object.prototype.hasOwnProperty) - .replace(reRegExpChar, '\\$&') - .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') -}$`) - -/** - * Checks if `value` is a pristine native function. - * - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a native function, - * else `false`. - * @example - * - * isNative(Array.prototype.push) - * // => true - * - * isNative(isDate) - * // => false - */ -function isNative(value) { - return isObject(value) && reIsNative.test(value) -} - -export default isNative diff --git a/isNil.js b/isNil.js deleted file mode 100644 index 4a70c9a563..0000000000 --- a/isNil.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Checks if `value` is `null` or `undefined`. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is nullish, else `false`. - * @example - * - * isNil(null) - * // => true - * - * isNil(void 0) - * // => true - * - * isNil(NaN) - * // => false - */ -function isNil(value) { - return value == null -} - -export default isNil diff --git a/isNull.js b/isNull.js deleted file mode 100644 index 1627b3ce71..0000000000 --- a/isNull.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Checks if `value` is `null`. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `null`, else `false`. - * @example - * - * isNull(null) - * // => true - * - * isNull(void 0) - * // => false - */ -function isNull(value) { - return value === null -} - -export default isNull diff --git a/isNumber.js b/isNumber.js deleted file mode 100644 index bb808a2c48..0000000000 --- a/isNumber.js +++ /dev/null @@ -1,34 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' - -/** - * Checks if `value` is classified as a `Number` primitive or object. - * - * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are - * classified as numbers, use the `Number.isFinite` method. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a number, else `false`. - * @see isInteger, toInteger, toNumber - * @example - * - * isNumber(3) - * // => true - * - * isNumber(Number.MIN_VALUE) - * // => true - * - * isNumber(Infinity) - * // => true - * - * isNumber('3') - * // => false - */ -function isNumber(value) { - return typeof value === 'number' || - (isObjectLike(value) && getTag(value) == '[object Number]') -} - -export default isNumber diff --git a/isObject.js b/isObject.js deleted file mode 100644 index e9e81bff51..0000000000 --- a/isObject.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Checks if `value` is the - * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) - * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is an object, else `false`. - * @example - * - * isObject({}) - * // => true - * - * isObject([1, 2, 3]) - * // => true - * - * isObject(Function) - * // => true - * - * isObject(null) - * // => false - */ -function isObject(value) { - const type = typeof value - return value != null && (type === 'object' || type === 'function') -} - -export default isObject diff --git a/isObjectLike.js b/isObjectLike.js deleted file mode 100644 index 7175076a85..0000000000 --- a/isObjectLike.js +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Checks if `value` is object-like. A value is object-like if it's not `null` - * and has a `typeof` result of "object". - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is object-like, else `false`. - * @example - * - * isObjectLike({}) - * // => true - * - * isObjectLike([1, 2, 3]) - * // => true - * - * isObjectLike(Function) - * // => false - * - * isObjectLike(null) - * // => false - */ -function isObjectLike(value) { - return typeof value === 'object' && value !== null -} - -export default isObjectLike diff --git a/isPlainObject.js b/isPlainObject.js deleted file mode 100644 index 896d4576fc..0000000000 --- a/isPlainObject.js +++ /dev/null @@ -1,44 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' - -/** - * Checks if `value` is a plain object, that is, an object created by the - * `Object` constructor or one with a `[[Prototype]]` of `null`. - * - * @since 0.8.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. - * @example - * - * function Foo() { - * this.a = 1 - * } - * - * isPlainObject(new Foo) - * // => false - * - * isPlainObject([1, 2, 3]) - * // => false - * - * isPlainObject({ 'x': 0, 'y': 0 }) - * // => true - * - * isPlainObject(Object.create(null)) - * // => true - */ -function isPlainObject(value) { - if (!isObjectLike(value) || getTag(value) != '[object Object]') { - return false - } - if (Object.getPrototypeOf(value) === null) { - return true - } - let proto = value - while (Object.getPrototypeOf(proto) !== null) { - proto = Object.getPrototypeOf(proto) - } - return Object.getPrototypeOf(value) === proto -} - -export default isPlainObject diff --git a/isRegExp.js b/isRegExp.js deleted file mode 100644 index 42d567d68a..0000000000 --- a/isRegExp.js +++ /dev/null @@ -1,27 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' -import nodeTypes from './.internal/nodeTypes.js' - -/* Node.js helper references. */ -const nodeIsRegExp = nodeTypes && nodeTypes.isRegExp - -/** - * Checks if `value` is classified as a `RegExp` object. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. - * @example - * - * isRegExp(/abc/) - * // => true - * - * isRegExp('/abc/') - * // => false - */ -const isRegExp = nodeIsRegExp - ? (value) => nodeIsRegExp(value) - : (value) => isObjectLike(value) && getTag(value) == '[object RegExp]' - -export default isRegExp diff --git a/isSet.js b/isSet.js deleted file mode 100644 index 51195abe63..0000000000 --- a/isSet.js +++ /dev/null @@ -1,27 +0,0 @@ -import getTag from './.internal/getTag.js' -import nodeTypes from './.internal/nodeTypes.js' -import isObjectLike from './isObjectLike.js' - -/* Node.js helper references. */ -const nodeIsSet = nodeTypes && nodeTypes.isSet - -/** - * Checks if `value` is classified as a `Set` object. - * - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a set, else `false`. - * @example - * - * isSet(new Set) - * // => true - * - * isSet(new WeakSet) - * // => false - */ -const isSet = nodeIsSet - ? (value) => nodeIsSet(value) - : (value) => isObjectLike(value) && getTag(value) == '[object Set]' - -export default isSet diff --git a/isString.js b/isString.js deleted file mode 100644 index b5470a9cc8..0000000000 --- a/isString.js +++ /dev/null @@ -1,23 +0,0 @@ -import getTag from './.internal/getTag.js' - -/** - * Checks if `value` is classified as a `String` primitive or object. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a string, else `false`. - * @example - * - * isString('abc') - * // => true - * - * isString(1) - * // => false - */ -function isString(value) { - const type = typeof value - return type === 'string' || (type === 'object' && value != null && !Array.isArray(value) && getTag(value) == '[object String]') -} - -export default isString diff --git a/isSymbol.js b/isSymbol.js deleted file mode 100644 index 0cf4c70e59..0000000000 --- a/isSymbol.js +++ /dev/null @@ -1,23 +0,0 @@ -import getTag from './.internal/getTag.js' - -/** - * Checks if `value` is classified as a `Symbol` primitive or object. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. - * @example - * - * isSymbol(Symbol.iterator) - * // => true - * - * isSymbol('abc') - * // => false - */ -function isSymbol(value) { - const type = typeof value - return type == 'symbol' || (type === 'object' && value != null && getTag(value) == '[object Symbol]') -} - -export default isSymbol diff --git a/isTypedArray.js b/isTypedArray.js deleted file mode 100644 index 184bbe4b36..0000000000 --- a/isTypedArray.js +++ /dev/null @@ -1,30 +0,0 @@ -import getTag from './.internal/getTag.js' -import nodeTypes from './.internal/nodeTypes.js' -import isObjectLike from './isObjectLike.js' - -/** Used to match `toStringTag` values of typed arrays. */ -const reTypedTag = /^\[object (?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array\]$/ - -/* Node.js helper references. */ -const nodeIsTypedArray = nodeTypes && nodeTypes.isTypedArray - -/** - * Checks if `value` is classified as a typed array. - * - * @since 3.0.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. - * @example - * - * isTypedArray(new Uint8Array) - * // => true - * - * isTypedArray([]) - * // => false - */ -const isTypedArray = nodeIsTypedArray - ? (value) => nodeIsTypedArray(value) - : (value) => isObjectLike(value) && reTypedTag.test(getTag(value)) - -export default isTypedArray diff --git a/isUndefined.js b/isUndefined.js deleted file mode 100644 index e92c558c80..0000000000 --- a/isUndefined.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Checks if `value` is `undefined`. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - * @example - * - * isUndefined(void 0) - * // => true - * - * isUndefined(null) - * // => false - */ -function isUndefined(value) { - return value === undefined -} - -export default isUndefined diff --git a/isWeakMap.js b/isWeakMap.js deleted file mode 100644 index 0271dca456..0000000000 --- a/isWeakMap.js +++ /dev/null @@ -1,23 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' - -/** - * Checks if `value` is classified as a `WeakMap` object. - * - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. - * @example - * - * isWeakMap(new WeakMap) - * // => true - * - * isWeakMap(new Map) - * // => false - */ -function isWeakMap(value) { - return isObjectLike(value) && getTag(value) == '[object WeakMap]' -} - -export default isWeakMap diff --git a/isWeakSet.js b/isWeakSet.js deleted file mode 100644 index a14b579145..0000000000 --- a/isWeakSet.js +++ /dev/null @@ -1,23 +0,0 @@ -import getTag from './.internal/getTag.js' -import isObjectLike from './isObjectLike.js' - -/** - * Checks if `value` is classified as a `WeakSet` object. - * - * @since 4.3.0 - * @category Lang - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. - * @example - * - * isWeakSet(new WeakSet) - * // => true - * - * isWeakSet(new Set) - * // => false - */ -function isWeakSet(value) { - return isObjectLike(value) && getTag(value) == '[object WeakSet]' -} - -export default isWeakSet diff --git a/kebabCase.js b/kebabCase.js deleted file mode 100644 index bc436b9490..0000000000 --- a/kebabCase.js +++ /dev/null @@ -1,30 +0,0 @@ -import words from './words.js' -import toString from './toString.js' - -/** - * Converts `string` to - * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the kebab cased string. - * @see camelCase, lowerCase, snakeCase, startCase, upperCase, upperFirst - * @example - * - * kebabCase('Foo Bar') - * // => 'foo-bar' - * - * kebabCase('fooBar') - * // => 'foo-bar' - * - * kebabCase('__FOO_BAR__') - * // => 'foo-bar' - */ -const kebabCase = (string) => ( - words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => ( - result + (index ? '-' : '') + word.toLowerCase() - ), '') -) - -export default kebabCase diff --git a/keyBy.js b/keyBy.js deleted file mode 100644 index 2297565bb7..0000000000 --- a/keyBy.js +++ /dev/null @@ -1,32 +0,0 @@ -import baseAssignValue from './.internal/baseAssignValue.js' -import reduce from './reduce.js' - -/** - * Creates an object composed of keys generated from the results of running - * each element of `collection` thru `iteratee`. The corresponding value of - * each key is the last element responsible for generating the key. The - * iteratee is invoked with one argument: (value). - * - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The iteratee to transform keys. - * @returns {Object} Returns the composed aggregate object. - * @see groupBy, partition - * @example - * - * const array = [ - * { 'dir': 'left', 'code': 97 }, - * { 'dir': 'right', 'code': 100 } - * ] - * - * keyBy(array, ({ code }) => String.fromCharCode(code)) - * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } - */ -function keyBy(collection, iteratee) { - return reduce(collection, (result, value, key) => ( - baseAssignValue(result, iteratee(value), value), result - ), {}) -} - -export default keyBy diff --git a/keys.js b/keys.js deleted file mode 100644 index 43b241c11e..0000000000 --- a/keys.js +++ /dev/null @@ -1,37 +0,0 @@ -import arrayLikeKeys from './.internal/arrayLikeKeys.js' -import isArrayLike from './isArrayLike.js' - -/** - * Creates an array of the own enumerable property names of `object`. - * - * **Note:** Non-object values are coerced to objects. See the - * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) - * for more details. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @see values, valuesIn - * @example - * - * function Foo() { - * this.a = 1 - * this.b = 2 - * } - * - * Foo.prototype.c = 3 - * - * keys(new Foo) - * // => ['a', 'b'] (iteration order is not guaranteed) - * - * keys('hi') - * // => ['0', '1'] - */ -function keys(object) { - return isArrayLike(object) - ? arrayLikeKeys(object) - : Object.keys(Object(object)) -} - -export default keys diff --git a/keysIn.js b/keysIn.js deleted file mode 100644 index b426b3de0b..0000000000 --- a/keysIn.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Creates an array of the own and inherited enumerable property names of `object`. - * - * - * @static - * @memberOf _ - * @since 3.0.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property names. - * @example - * - * function Foo() { - * this.a = 1; - * this.b = 2; - * } - * - * Foo.prototype.c = 3; - * - * _.keysIn(new Foo); - * // => ['a', 'b', 'c'] (iteration order is not guaranteed) - */ -function keysIn(object) { - const result = [] - for (const key in object) { - result.push(key) - } - return result -} - -export default keysIn - diff --git a/last.js b/last.js deleted file mode 100644 index 3bb7dfd8b8..0000000000 --- a/last.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Gets the last element of `array`. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @returns {*} Returns the last element of `array`. - * @example - * - * last([1, 2, 3]) - * // => 3 - */ -function last(array) { - const length = array == null ? 0 : array.length - return length ? array[length - 1] : undefined -} - -export default last diff --git a/lastIndexOf.js b/lastIndexOf.js deleted file mode 100644 index 23f3a76e72..0000000000 --- a/lastIndexOf.js +++ /dev/null @@ -1,40 +0,0 @@ -import baseFindIndex from './.internal/baseFindIndex.js' -import baseIsNaN from './.internal/baseIsNaN.js' -import strictLastIndexOf from './.internal/strictLastIndexOf.js' -import toInteger from './toInteger.js' - -/** - * This method is like `indexOf` except that it iterates over elements of - * `array` from right to left. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @param {number} [fromIndex=array.length-1] The index to search from. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * lastIndexOf([1, 2, 1, 2], 2) - * // => 3 - * - * // Search from the `fromIndex`. - * lastIndexOf([1, 2, 1, 2], 2, 2) - * // => 1 - */ -function lastIndexOf(array, value, fromIndex) { - const length = array == null ? 0 : array.length - if (!length) { - return -1 - } - let index = length - if (fromIndex !== undefined) { - index = toInteger(fromIndex) - index = index < 0 ? Math.max(length + index, 0) : Math.min(index, length - 1) - } - return value === value - ? strictLastIndexOf(array, value, index) - : baseFindIndex(array, baseIsNaN, index, true) -} - -export default lastIndexOf diff --git a/lowerCase.js b/lowerCase.js deleted file mode 100644 index 3da8966cfc..0000000000 --- a/lowerCase.js +++ /dev/null @@ -1,31 +0,0 @@ -import words from './words.js' -import toString from './toString.js' - -const reQuotes = /['\u2019]/g - -/** - * Converts `string`, as space separated words, to lower case. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the lower cased string. - * @see camelCase, kebabCase, snakeCase, startCase, upperCase, upperFirst - * @example - * - * lowerCase('--Foo-Bar--') - * // => 'foo bar' - * - * lowerCase('fooBar') - * // => 'foo bar' - * - * lowerCase('__FOO_BAR__') - * // => 'foo bar' - */ -const lowerCase = (string) => ( - words(toString(string).replace(reQuotes, '')).reduce((result, word, index) => ( - result + (index ? ' ' : '') + word.toLowerCase() - ), '') -) - -export default lowerCase diff --git a/lowerFirst.js b/lowerFirst.js deleted file mode 100644 index f569521919..0000000000 --- a/lowerFirst.js +++ /dev/null @@ -1,20 +0,0 @@ -import createCaseFirst from './.internal/createCaseFirst.js' - -/** - * Converts the first character of `string` to lower case. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the converted string. - * @example - * - * lowerFirst('Fred') - * // => 'fred' - * - * lowerFirst('FRED') - * // => 'fRED' - */ -const lowerFirst = createCaseFirst('toLowerCase') - -export default lowerFirst diff --git a/lt.js b/lt.js deleted file mode 100644 index f2acbcdaad..0000000000 --- a/lt.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Checks if `value` is less than `other`. - * - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than `other`, - * else `false`. - * @see gt, gte, lte - * @example - * - * lt(1, 3) - * // => true - * - * lt(3, 3) - * // => false - * - * lt(3, 1) - * // => false - */ -function lt(value, other) { - if (!(typeof value === 'string' && typeof other === 'string')) { - value = +value - other = +other - } - return value < other -} - -export default lt diff --git a/lte.js b/lte.js deleted file mode 100644 index ef7ef451f4..0000000000 --- a/lte.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Checks if `value` is less than or equal to `other`. - * - * @since 3.9.0 - * @category Lang - * @param {*} value The value to compare. - * @param {*} other The other value to compare. - * @returns {boolean} Returns `true` if `value` is less than or equal to - * `other`, else `false`. - * @see gt, gte, lt - * @example - * - * lte(1, 3) - * // => true - * - * lte(3, 3) - * // => true - * - * lte(3, 1) - * // => false - */ -function lte(value, other) { - if (!(typeof value === 'string' && typeof other === 'string')) { - value = +value - other = +other - } - return value <= other -} - -export default lte diff --git a/map.js b/map.js deleted file mode 100644 index 3711d2b1dc..0000000000 --- a/map.js +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Creates an array of values by running each element of `array` thru `iteratee`. - * The iteratee is invoked with three arguments: (value, index, array). - * - * @since 5.0.0 - * @category Array - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - * @example - * - * function square(n) { - * return n * n - * } - * - * map([4, 8], square) - * // => [16, 64] - */ -function map(array, iteratee) { - let index = -1 - const length = array == null ? 0 : array.length - const result = new Array(length) - - while (++index < length) { - result[index] = iteratee(array[index], index, array) - } - return result -} - -export default map diff --git a/mapKey.js b/mapKey.js deleted file mode 100644 index a69e5b2d31..0000000000 --- a/mapKey.js +++ /dev/null @@ -1,31 +0,0 @@ -/** - * The opposite of `mapValue` this method creates an object with the - * same values as `object` and keys generated by running each own enumerable - * string keyed property of `object` thru `iteratee`. The iteratee is invoked - * with three arguments: (value, key, object). - * - * @since 3.8.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see mapValue - * @example - * - * mapKey({ 'a': 1, 'b': 2 }, function(value, key) { - * return key + value - * }) - * // => { 'a1': 1, 'b2': 2 } - */ -function mapKey(object, iteratee) { - object = Object(object) - const result = {} - - Object.keys(object).forEach((key) => { - const value = object[key] - result[iteratee(value, key, object)] = value - }) - return result -} - -export default mapKey diff --git a/mapObject.js b/mapObject.js deleted file mode 100644 index a817ca7297..0000000000 --- a/mapObject.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Creates an array of values by running each property of `object` thru - * `iteratee`. The iteratee is invoked with three arguments: (value, key, object). - * - * @since 5.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the new mapped array. - * @example - * - * function square(n) { - * return n * n - * } - * - * map({ 'a': 4, 'b': 8 }, square) - * // => [16, 64] (iteration order is not guaranteed) - */ -function mapObject(object, iteratee) { - const props = Object.keys(object) - const result = new Array(props.length) - - props.forEach((key, index) => { - result[index] = iteratee(object[key], key, object) - }) - return result -} - -export default mapObject diff --git a/mapValue.js b/mapValue.js deleted file mode 100644 index d8d9aa7795..0000000000 --- a/mapValue.js +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Creates an object with the same keys as `object` and values generated - * by running each own enumerable string keyed property of `object` thru - * `iteratee`. The iteratee is invoked with three arguments: - * (value, key, object). - * - * @since 2.4.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Object} Returns the new mapped object. - * @see mapKeys - * @example - * - * const users = { - * 'fred': { 'user': 'fred', 'age': 40 }, - * 'pebbles': { 'user': 'pebbles', 'age': 1 } - * } - * - * mapValue(users, ({ age }) => age) - * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) - */ -function mapValue(object, iteratee) { - object = Object(object) - const result = {} - - Object.keys(object).forEach((key) => { - result[key] = iteratee(object[key], key, object) - }) - return result -} - -export default mapValue diff --git a/matches.js b/matches.js deleted file mode 100644 index a0ae76a844..0000000000 --- a/matches.js +++ /dev/null @@ -1,37 +0,0 @@ -import baseClone from './.internal/baseClone.js' -import baseMatches from './.internal/baseMatches.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 - -/** - * Creates a function that performs a partial deep comparison between a given - * object and `source`, returning `true` if the given object has equivalent - * property values, else `false`. - * - * **Note:** The created function is equivalent to `isMatch` with `source` - * partially applied. - * - * Partial comparisons will match empty array and empty object `source` - * values against any array or object value, respectively. See `isEqual` - * for a list of supported value comparisons. - * - * @since 3.0.0 - * @category Util - * @param {Object} source The object of property values to match. - * @returns {Function} Returns the new spec function. - * @example - * - * const objects = [ - * { 'a': 1, 'b': 2, 'c': 3 }, - * { 'a': 4, 'b': 5, 'c': 6 } - * ] - * - * filter(objects, matches({ 'a': 4, 'c': 6 })) - * // => [{ 'a': 4, 'b': 5, 'c': 6 }] - */ -function matches(source) { - return baseMatches(baseClone(source, CLONE_DEEP_FLAG)) -} - -export default matches diff --git a/matchesProperty.js b/matchesProperty.js deleted file mode 100644 index cf6122e8f7..0000000000 --- a/matchesProperty.js +++ /dev/null @@ -1,35 +0,0 @@ -import baseClone from './.internal/baseClone.js' -import baseMatchesProperty from './.internal/baseMatchesProperty.js' - -/** Used to compose bitmasks for cloning. */ -const CLONE_DEEP_FLAG = 1 - -/** - * Creates a function that performs a partial deep comparison between the - * value at `path` of a given object to `srcValue`, returning `true` if the - * object value is equivalent, else `false`. - * - * **Note:** Partial comparisons will match empty array and empty object - * `srcValue` values against any array or object value, respectively. See - * `isEqual` for a list of supported value comparisons. - * - * @since 3.2.0 - * @category Util - * @param {Array|string} path The path of the property to get. - * @param {*} srcValue The value to match. - * @returns {Function} Returns the new spec function. - * @example - * - * const objects = [ - * { 'a': 1, 'b': 2, 'c': 3 }, - * { 'a': 4, 'b': 5, 'c': 6 } - * ] - * - * find(objects, matchesProperty('a', 4)) - * // => { 'a': 4, 'b': 5, 'c': 6 } - */ -function matchesProperty(path, srcValue) { - return baseMatchesProperty(path, baseClone(srcValue, CLONE_DEEP_FLAG)) -} - -export default matchesProperty diff --git a/maxBy.js b/maxBy.js deleted file mode 100644 index d8d9d59035..0000000000 --- a/maxBy.js +++ /dev/null @@ -1,40 +0,0 @@ -import isSymbol from './isSymbol.js' - -/** - * This method is like `max` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * the value is ranked. The iteratee is invoked with one argument: (value). - * - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {*} Returns the maximum value. - * @example - * - * const objects = [{ 'n': 1 }, { 'n': 2 }] - * - * maxBy(objects, ({ n }) => n) - * // => { 'n': 2 } - */ -function maxBy(array, iteratee) { - let result - if (array == null) { - return result - } - let computed - for (const value of array) { - const current = iteratee(value) - - if (current != null && (computed === undefined - ? (current === current && !isSymbol(current)) - : (current > computed) - )) { - computed = current - result = value - } - } - return result -} - -export default maxBy diff --git a/mean.js b/mean.js deleted file mode 100644 index 8f6d317f7b..0000000000 --- a/mean.js +++ /dev/null @@ -1,19 +0,0 @@ -import baseMean from './meanBy.js' - -/** - * Computes the mean of the values in `array`. - * - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @returns {number} Returns the mean. - * @example - * - * mean([4, 2, 8, 6]) - * // => 5 - */ -function mean(array) { - return baseMean(array, (value) => value) -} - -export default mean diff --git a/meanBy.js b/meanBy.js deleted file mode 100644 index d365c89726..0000000000 --- a/meanBy.js +++ /dev/null @@ -1,28 +0,0 @@ -import baseSum from './.internal/baseSum.js' - -/** Used as references for various `Number` constants. */ -const NAN = 0 / 0 - -/** - * This method is like `mean` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the value to be averaged. - * The iteratee is invoked with one argument: (value). - * - * @since 4.7.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {number} Returns the mean. - * @example - * - * const objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }] - * - * meanBy(objects, ({ n }) => n) - * // => 5 - */ -function meanBy(array, iteratee) { - const length = array == null ? 0 : array.length - return length ? (baseSum(array, iteratee) / length) : NAN -} - -export default meanBy diff --git a/memoize.js b/memoize.js deleted file mode 100644 index 8e41558ecd..0000000000 --- a/memoize.js +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Creates a function that memoizes the result of `func`. If `resolver` is - * provided, it determines the cache key for storing the result based on the - * arguments provided to the memoized function. By default, the first argument - * provided to the memoized function is used as the map cache key. The `func` - * is invoked with the `this` binding of the memoized function. - * - * **Note:** The cache is exposed as the `cache` property on the memoized - * function. Its creation may be customized by replacing the `memoize.Cache` - * constructor with one whose instances implement the - * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) - * method interface of `clear`, `delete`, `get`, `has`, and `set`. - * - * @since 0.1.0 - * @category Function - * @param {Function} func The function to have its output memoized. - * @param {Function} [resolver] The function to resolve the cache key. - * @returns {Function} Returns the new memoized function. - * @example - * - * const object = { 'a': 1, 'b': 2 } - * const other = { 'c': 3, 'd': 4 } - * - * const values = memoize(values) - * values(object) - * // => [1, 2] - * - * values(other) - * // => [3, 4] - * - * object.a = 2 - * values(object) - * // => [1, 2] - * - * // Modify the result cache. - * values.cache.set(object, ['a', 'b']) - * values(object) - * // => ['a', 'b'] - * - * // Replace `memoize.Cache`. - * memoize.Cache = WeakMap - */ -function memoize(func, resolver) { - if (typeof func !== 'function' || (resolver != null && typeof resolver !== 'function')) { - throw new TypeError('Expected a function') - } - const memoized = function(...args) { - const key = resolver ? resolver.apply(this, args) : args[0] - const cache = memoized.cache - - if (cache.has(key)) { - return cache.get(key) - } - const result = func.apply(this, args) - memoized.cache = cache.set(key, result) || cache - return result - } - memoized.cache = new (memoize.Cache || Map) - return memoized -} - -memoize.Cache = Map - -export default memoize diff --git a/merge.js b/merge.js deleted file mode 100644 index 730b296347..0000000000 --- a/merge.js +++ /dev/null @@ -1,37 +0,0 @@ -import baseMerge from './.internal/baseMerge.js' -import createAssigner from './.internal/createAssigner.js' - -/** - * This method is like `assign` except that it recursively merges own and - * inherited enumerable string keyed properties of source objects into the - * destination object. Source properties that resolve to `undefined` are - * skipped if a destination value exists. Array and plain object properties - * are merged recursively. Other objects and value types are overridden by - * assignment. Source objects are applied from left to right. Subsequent - * sources overwrite property assignments of previous sources. - * - * **Note:** This method mutates `object`. - * - * @since 0.5.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} [sources] The source objects. - * @returns {Object} Returns `object`. - * @example - * - * const object = { - * 'a': [{ 'b': 2 }, { 'd': 4 }] - * } - * - * const other = { - * 'a': [{ 'c': 3 }, { 'e': 5 }] - * } - * - * merge(object, other) - * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } - */ -const merge = createAssigner((object, source, srcIndex) => { - baseMerge(object, source, srcIndex) -}) - -export default merge diff --git a/mergeWith.js b/mergeWith.js deleted file mode 100644 index dc930be41f..0000000000 --- a/mergeWith.js +++ /dev/null @@ -1,37 +0,0 @@ -import baseMerge from './.internal/baseMerge.js' -import createAssigner from './.internal/createAssigner.js' - -/** - * This method is like `merge` except that it accepts `customizer` which - * is invoked to produce the merged values of the destination and source - * properties. If `customizer` returns `undefined`, merging is handled by the - * method instead. The `customizer` is invoked with six arguments: - * (objValue, srcValue, key, object, source, stack). - * - * **Note:** This method mutates `object`. - * - * @since 4.0.0 - * @category Object - * @param {Object} object The destination object. - * @param {...Object} sources The source objects. - * @param {Function} customizer The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * function customizer(objValue, srcValue) { - * if (Array.isArray(objValue)) { - * return objValue.concat(srcValue) - * } - * } - * - * const object = { 'a': [1], 'b': [2] } - * const other = { 'a': [3], 'b': [4] } - * - * mergeWith(object, other, customizer) - * // => { 'a': [1, 3], 'b': [2, 4] } - */ -const mergeWith = createAssigner((object, source, srcIndex, customizer) => { - baseMerge(object, source, srcIndex, customizer) -}) - -export default mergeWith diff --git a/method.js b/method.js deleted file mode 100644 index 3405e695c9..0000000000 --- a/method.js +++ /dev/null @@ -1,29 +0,0 @@ -import invoke from './invoke.js' - -/** - * Creates a function that invokes the method at `path` of a given object. - * Any additional arguments are provided to the invoked method. - * - * @since 3.7.0 - * @category Util - * @param {Array|string} path The path of the method to invoke. - * @param {Array} [args] The arguments to invoke the method with. - * @returns {Function} Returns the new invoker function. - * @example - * - * const objects = [ - * { 'a': { 'b': () => 2 } }, - * { 'a': { 'b': () => 1 } } - * ] - * - * map(objects, method('a.b')) - * // => [2, 1] - * - * map(objects, method(['a', 'b'])) - * // => [2, 1] - */ -function method(path, args) { - return (object) => invoke(object, path, args) -} - -export default method diff --git a/methodOf.js b/methodOf.js deleted file mode 100644 index ff39385b1e..0000000000 --- a/methodOf.js +++ /dev/null @@ -1,28 +0,0 @@ -import invoke from './invoke.js' - -/** - * The opposite of `method` this method creates a function that invokes - * the method at a given path of `object`. Any additional arguments are - * provided to the invoked method. - * - * @since 3.7.0 - * @category Util - * @param {Object} object The object to query. - * @param {Array} [args] The arguments to invoke the method with. - * @returns {Function} Returns the new invoker function. - * @example - * - * const array = times(3, i => () => i) - * const object = { 'a': array, 'b': array, 'c': array } - * - * map(['a[2]', 'c[0]'], methodOf(object)) - * // => [2, 0] - * - * map([['a', '2'], ['c', '0']], methodOf(object)) - * // => [2, 0]f - */ -function methodOf(object, args) { - return (path) => invoke(object, path, args) -} - -export default methodOf diff --git a/minBy.js b/minBy.js deleted file mode 100644 index cb0fe8233f..0000000000 --- a/minBy.js +++ /dev/null @@ -1,40 +0,0 @@ -import isSymbol from './isSymbol.js' - -/** - * This method is like `min` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * the value is ranked. The iteratee is invoked with one argument: (value). - * - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {*} Returns the minimum value. - * @example - * - * const objects = [{ 'n': 1 }, { 'n': 2 }] - * - * minBy(objects, ({ n }) => n) - * // => { 'n': 1 } - */ -function minBy(array, iteratee) { - let result - if (array == null) { - return result - } - let computed - for (const value of array) { - const current = iteratee(value) - - if (current != null && (computed === undefined - ? (current === current && !isSymbol(current)) - : (current < computed) - )) { - computed = current - result = value - } - } - return result -} - -export default minBy diff --git a/multiply.js b/multiply.js deleted file mode 100644 index 8ccba1406f..0000000000 --- a/multiply.js +++ /dev/null @@ -1,18 +0,0 @@ -import createMathOperation from './.internal/createMathOperation.js' - -/** - * Multiply two numbers. - * - * @since 4.7.0 - * @category Math - * @param {number} multiplier The first number in a multiplication. - * @param {number} multiplicand The second number in a multiplication. - * @returns {number} Returns the product. - * @example - * - * multiply(6, 4) - * // => 24 - */ -const multiply = createMathOperation((multiplier, multiplicand) => multiplier * multiplicand, 1) - -export default multiply diff --git a/negate.js b/negate.js deleted file mode 100644 index 2e2f863f17..0000000000 --- a/negate.js +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Creates a function that negates the result of the predicate `func`. The - * `func` predicate is invoked with the `this` binding and arguments of the - * created function. - * - * @since 3.0.0 - * @category Function - * @param {Function} predicate The predicate to negate. - * @returns {Function} Returns the new negated function. - * @example - * - * function isEven(n) { - * return n % 2 == 0 - * } - * - * filter([1, 2, 3, 4, 5, 6], negate(isEven)) - * // => [1, 3, 5] - */ -function negate(predicate) { - if (typeof predicate !== 'function') { - throw new TypeError('Expected a function') - } - return function(...args) { - return !predicate.apply(this, args) - } -} - -export default negate diff --git a/nth.js b/nth.js deleted file mode 100644 index b2425a7dd1..0000000000 --- a/nth.js +++ /dev/null @@ -1,31 +0,0 @@ -import isIndex from './.internal/isIndex.js' - -/** - * Gets the element at index `n` of `array`. If `n` is negative, the nth - * element from the end is returned. - * - * @since 4.11.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=0] The index of the element to return. - * @returns {*} Returns the nth element of `array`. - * @example - * - * const array = ['a', 'b', 'c', 'd'] - * - * nth(array, 1) - * // => 'b' - * - * nth(array, -2) - * // => 'c' - */ -function nth(array, n) { - const length = array == null ? 0 : array.length - if (!length) { - return - } - n += n < 0 ? length : 0 - return isIndex(n, length) ? array[n] : undefined -} - -export default nth diff --git a/nthArg.js b/nthArg.js deleted file mode 100644 index f5da0cf517..0000000000 --- a/nthArg.js +++ /dev/null @@ -1,25 +0,0 @@ -import nth from './nth.js' - -/** - * Creates a function that gets the argument at index `n`. If `n` is negative, - * the nth argument from the end is returned. - * - * @since 4.0.0 - * @category Util - * @param {number} [n=0] The index of the argument to return. - * @returns {Function} Returns the new pass-thru function. - * @example - * - * const func = nthArg(1) - * func('a', 'b', 'c', 'd') - * // => 'b' - * - * const func = nthArg(-2) - * func('a', 'b', 'c', 'd') - * // => 'c' - */ -function nthArg(n) { - return (...args) => nth(args, n) -} - -export default nthArg diff --git a/once.js b/once.js deleted file mode 100644 index 653000bea0..0000000000 --- a/once.js +++ /dev/null @@ -1,23 +0,0 @@ -import before from './before.js' - -/** - * Creates a function that is restricted to invoking `func` once. Repeat calls - * to the function return the value of the first invocation. The `func` is - * invoked with the `this` binding and arguments of the created function. - * - * @since 0.1.0 - * @category Function - * @param {Function} func The function to restrict. - * @returns {Function} Returns the new restricted function. - * @example - * - * const initialize = once(createApplication) - * initialize() - * initialize() - * // => `createApplication` is invoked once - */ -function once(func) { - return before(2, func) -} - -export default once diff --git a/orderBy.js b/orderBy.js deleted file mode 100644 index 4bb2112efe..0000000000 --- a/orderBy.js +++ /dev/null @@ -1,51 +0,0 @@ -import baseOrderBy from './.internal/baseOrderBy.js' - -/** - * This method is like `sortBy` except that it allows specifying the sort - * orders of the iteratees to sort by. If `orders` is unspecified, all values - * are sorted in ascending order. Otherwise, specify an order of "desc" for - * descending or "asc" for ascending sort order of corresponding values. - * You may also specify a compare function for an order. - * - * @since 4.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[identity]] - * The iteratees to sort by. - * @param {(string|function)[]} [orders] The sort orders of `iteratees`. - * @returns {Array} Returns the new sorted array. - * @see reverse - * @example - * - * const users = [ - * { 'user': 'fred', 'age': 48 }, - * { 'user': 'barney', 'age': 34 }, - * { 'user': 'fred', 'age': 40 }, - * { 'user': 'barney', 'age': 36 } - * ] - * - * // Sort by `user` in ascending order and by `age` in descending order. - * orderBy(users, ['user', 'age'], ['asc', 'desc']) - * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] - * - * // Sort by `user` then by `age` using custom compare functions for each - * orderBy(users, ['user', 'age'], [ - * (a, b) => a.localeCompare(b, 'de', { sensitivity: 'base' }), - * (a, b) => a - b, - * ]) - * - */ -function orderBy(collection, iteratees, orders) { - if (collection == null) { - return [] - } - if (!Array.isArray(iteratees)) { - iteratees = iteratees == null ? [] : [iteratees] - } - if (!Array.isArray(orders)) { - orders = orders == null ? [] : [orders] - } - return baseOrderBy(collection, iteratees, orders) -} - -export default orderBy diff --git a/over.js b/over.js deleted file mode 100644 index d4e41daa09..0000000000 --- a/over.js +++ /dev/null @@ -1,25 +0,0 @@ -import map from './map.js' - -/** - * Creates a function that invokes `iteratees` with the arguments it receives - * and returns their results. - * - * @since 4.0.0 - * @category Util - * @param {Function[]} [iteratees=[identity]] - * The iteratees to invoke. - * @returns {Function} Returns the new function. - * @example - * - * const func = over([Math.max, Math.min]) - * - * func(1, 2, 3, 4) - * // => [4, 1] - */ -function over(iteratees) { - return function(...args) { - return map(iteratees, (iteratee) => iteratee.apply(this, args)) - } -} - -export default over diff --git a/overArgs.js b/overArgs.js deleted file mode 100644 index 7a1479ec68..0000000000 --- a/overArgs.js +++ /dev/null @@ -1,41 +0,0 @@ - -/** - * Creates a function that invokes `func` with its arguments transformed. - * - * @since 4.0.0 - * @category Function - * @param {Function} func The function to wrap. - * @param {Function[]} [transforms=[identity]] - * The argument transforms. - * @returns {Function} Returns the new function. - * @example - * - * function doubled(n) { - * return n * 2 - * } - * - * function square(n) { - * return n * n - * } - * - * const func = overArgs((x, y) => [x, y], [square, doubled]) - * - * func(9, 3) - * // => [81, 6] - * - * func(10, 5) - * // => [100, 10] - */ -function overArgs(func, transforms) { - const funcsLength = transforms.length - return function(...args) { - let index = -1 - const length = Math.min(args.length, funcsLength) - while (++index < length) { - args[index] = transforms[index].call(this, args[index]) - } - return func.apply(this, args) - } -} - -export default overArgs diff --git a/overEvery.js b/overEvery.js deleted file mode 100644 index 4f22a9956c..0000000000 --- a/overEvery.js +++ /dev/null @@ -1,31 +0,0 @@ -import every from './every.js' - -/** - * Creates a function that checks if **all** of the `predicates` return - * truthy when invoked with the arguments it receives. - * - * @since 4.0.0 - * @category Util - * @param {Function[]} [predicates=[identity]] - * The predicates to check. - * @returns {Function} Returns the new function. - * @example - * - * const func = overEvery([Boolean, isFinite]) - * - * func('1') - * // => true - * - * func(null) - * // => false - * - * func(NaN) - * // => false - */ -function overEvery(iteratees) { - return function(...args) { - return every(iteratees, (iteratee) => iteratee.apply(this, args)) - } -} - -export default overEvery diff --git a/overSome.js b/overSome.js deleted file mode 100644 index 6b2c0dcbb3..0000000000 --- a/overSome.js +++ /dev/null @@ -1,31 +0,0 @@ -import some from './some.js' - -/** - * Creates a function that checks if **any** of the `predicates` return - * truthy when invoked with the arguments it receives. - * - * @since 4.0.0 - * @category Util - * @param {Function[]} [predicates=[identity]] - * The predicates to check. - * @returns {Function} Returns the new function. - * @example - * - * const func = overSome([Boolean, isFinite]) - * - * func('1') - * // => true - * - * func(null) - * // => true - * - * func(NaN) - * // => false - */ -function overSome(iteratees) { - return function(...args) { - return some(iteratees, (iteratee) => iteratee.apply(this, args)) - } -} - -export default overSome diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index d46ba027a5..0000000000 --- a/package-lock.json +++ /dev/null @@ -1,1697 +0,0 @@ -{ - "name": "lodash", - "version": "5.0.0", - "lockfileVersion": 1, - "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz", - "integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==", - "dev": true - }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@eslint/eslintrc": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.2.2.tgz", - "integrity": "sha512-EfB5OHNYp1F4px/LI/FEnGylop7nOqkQ1LRzCM0KccA2U8tvV8w01KBv37LbO7nW4H+YhKyo2LcJhRwjjV17QQ==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - } - } - }, - "@types/json5": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", - "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", - "dev": true - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true - }, - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "dependencies": { - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - } - }, - "array-includes": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.2.tgz", - "integrity": "sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "get-intrinsic": "^1.0.1", - "is-string": "^1.0.5" - } - }, - "array.prototype.flat": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz", - "integrity": "sha512-4470Xi3GAPAjZqFcljX2xzckv1qeKPizoNkiS0+O4IoPR2ZNpcjE0pkhdihlDouK+x6QOast26B4Q/O9DJnwSg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1" - } - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "browser-stdout": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", - "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true - }, - "call-bind": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.0.tgz", - "integrity": "sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "commander": { - "version": "2.15.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", - "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "diff": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", - "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", - "dev": true - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.18.0-next.1", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.1.tgz", - "integrity": "sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.2.2", - "is-negative-zero": "^2.0.0", - "is-regex": "^1.1.1", - "object-inspect": "^1.8.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.1", - "string.prototype.trimend": "^1.0.1", - "string.prototype.trimstart": "^1.0.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "eslint": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.16.0.tgz", - "integrity": "sha512-iVWPS785RuDA4dWuhhgXTNrGxHHK3a8HLSMBgbbU59ruJDubUraXN8N5rn7kb8tG6sjg74eE0RA3YWT51eusEw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@eslint/eslintrc": "^0.2.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.2.0", - "esutils": "^2.0.2", - "file-entry-cache": "^6.0.0", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", - "globals": "^12.1.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash": "^4.17.19", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.4", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "chalk": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", - "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "glob-parent": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz", - "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz", - "integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz", - "integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-import": { - "version": "2.22.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz", - "integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==", - "dev": true, - "requires": { - "array-includes": "^3.1.1", - "array.prototype.flat": "^1.2.3", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.4", - "eslint-module-utils": "^2.6.0", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.1", - "read-pkg-up": "^2.0.0", - "resolve": "^1.17.0", - "tsconfig-paths": "^3.9.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "eslint-visitor-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz", - "integrity": "sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ==", - "dev": true - }, - "esm": { - "version": "3.2.25", - "resolved": "https://registry.npmjs.org/esm/-/esm-3.2.25.tgz", - "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "esquery": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz", - "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "file-entry-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.0.tgz", - "integrity": "sha512-fqoO76jZ3ZnYrXLDRxBR1YvOvc0k844kcOg40bgsPrE25LAb/PDqTY+ho64Xh2c8ZXgIKldchCFHczG2UVRcWA==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.0.tgz", - "integrity": "sha512-tW+UkmtNg/jv9CSofAKvgVcO7c2URjhTdW1ZTkcAritblu8tajiYy7YisnIflEwtKssCtOxpnBRoCB7iap0/TA==", - "dev": true - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "get-intrinsic": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.0.2.tgz", - "integrity": "sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, - "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==", - "dev": true - }, - "growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "he": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", - "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", - "dev": true - }, - "hosted-git-info": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz", - "integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==", - "dev": true - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-callable": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz", - "integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==", - "dev": true - }, - "is-core-module": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz", - "integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-negative-zero": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz", - "integrity": "sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w==", - "dev": true - }, - "is-regex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", - "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "dependencies": { - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", - "dev": true - }, - "mkdirp": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", - "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", - "dev": true, - "requires": { - "minimist": "0.0.8" - }, - "dependencies": { - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - } - } - }, - "mocha": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", - "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", - "dev": true, - "requires": { - "browser-stdout": "1.3.1", - "commander": "2.15.1", - "debug": "3.1.0", - "diff": "3.5.0", - "escape-string-regexp": "1.0.5", - "glob": "7.1.2", - "growl": "1.10.5", - "he": "1.1.1", - "minimatch": "3.0.4", - "mkdirp": "0.5.1", - "supports-color": "5.4.0" - }, - "dependencies": { - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - }, - "dependencies": { - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - } - } - }, - "object-inspect": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.9.0.tgz", - "integrity": "sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object.assign": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", - "integrity": "sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "has-symbols": "^1.0.1", - "object-keys": "^1.1.1" - } - }, - "object.values": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.2.tgz", - "integrity": "sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3", - "es-abstract": "^1.18.0-next.1", - "has": "^1.0.3" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - } - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "resolve": { - "version": "1.19.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz", - "integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==", - "dev": true, - "requires": { - "is-core-module": "^2.1.0", - "path-parse": "^1.0.6" - }, - "dependencies": { - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", - "dev": true - } - } - }, - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - }, - "dependencies": { - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - } - } - }, - "semver": { - "version": "7.3.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.4.tgz", - "integrity": "sha512-tCfb2WLjqFAtXn4KEdxIhalnRtoKFN7nAwj0B3ZXCbQloV2tq5eDbcTmT68JJD3nRJq24/XgxtQKFIpQdtvmVw==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - }, - "dependencies": { - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - } - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - } - } - }, - "spdx-correct": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz", - "integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==", - "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz", - "integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==", - "dev": true - }, - "spdx-expression-parse": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", - "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", - "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-license-ids": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz", - "integrity": "sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ==", - "dev": true - }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "string.prototype.trimend": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz", - "integrity": "sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "string.prototype.trimstart": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz", - "integrity": "sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg==", - "dev": true, - "requires": { - "call-bind": "^1.0.0", - "define-properties": "^1.1.3" - } - }, - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - }, - "strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", - "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.4.tgz", - "integrity": "sha512-sBT4xRLdALd+NFBvwOz8bw4b15htyythha+q+DVZqy2RS08PPC8O2sZFgJYEY7bJvbCFKccs+WIZ/cd+xxTWCw==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "lodash": "^4.17.20", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" - }, - "dependencies": { - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "string-width": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz", - "integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==", - "dev": true, - "requires": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "requires": { - "ansi-regex": "^5.0.0" - } - } - } - }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "tsconfig-paths": { - "version": "3.9.0", - "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz", - "integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==", - "dev": true, - "requires": { - "@types/json5": "^0.0.29", - "json5": "^1.0.1", - "minimist": "^1.2.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "json5": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", - "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", - "dev": true, - "requires": { - "minimist": "^1.2.0" - } - } - } - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", - "dev": true, - "requires": { - "punycode": "^2.1.0" - }, - "dependencies": { - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - } - } - }, - "v8-compile-cache": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz", - "integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==", - "dev": true - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true - }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - } - } -} diff --git a/package.json b/package.json index 622676b99e..8853f11efd 100644 --- a/package.json +++ b/package.json @@ -3,21 +3,48 @@ "version": "5.0.0", "license": "MIT", "private": true, - "main": "lodash.js", - "engines": { - "node": ">=4.0.0" - }, + "main": "dist/lodash.js", "sideEffects": false, "scripts": { - "style": "eslint *.js .internal/**/*.js", - "test": "mocha -r esm test/*.test.js", - "validate": "npm run style && npm run test" + "prepare": "husky install", + "lint": "eslint ./src/**/*.ts" }, "devDependencies": { - "mocha": "^5.2.0", - "eslint": "^7.16.0", - "eslint-plugin-import": "^2.22.1", - "lodash": "4.17.20", - "esm": "^3.2.25" - } + "@commitlint/cli": "17.7.1", + "@commitlint/config-conventional": "17.7.0", + "@types/eslint": "8.44.2", + "@types/jest": "29.5.5", + "@typescript-eslint/eslint-plugin": "6.7.0", + "@typescript-eslint/parser": "6.7.0", + "eslint": "8.49.0", + "eslint-config-airbnb-base": "15.0.0", + "eslint-config-airbnb-typescript": "17.1.0", + "eslint-config-prettier": "9.0.0", + "eslint-plugin-import": "2.28.1", + "eslint-plugin-prettier": "5.0.0", + "husky": "8.0.3", + "lint-staged": "14.0.1", + "lodash": "4.17.21", + "prettier": "3.0.3" + }, + "lint-staged": { + "src/**/*.{ts}": [ + "eslint --fix" + ] + }, + "husky": { + "hooks": { + "pre-commit": "lint-staged", + "commit-msg": "bun run commitlint --edit $1" + } + }, + "engines": { + "node": "^18.17.1", + "yarn": "^1.22.19" + }, + "volta": { + "node": "18.17.1", + "yarn": "1.22.19" + }, + "engineStrict": true } diff --git a/pad.js b/pad.js deleted file mode 100644 index 3cfd9131b3..0000000000 --- a/pad.js +++ /dev/null @@ -1,38 +0,0 @@ -import createPadding from './.internal/createPadding.js' -import stringSize from './.internal/stringSize.js' - -/** - * Pads `string` on the left and right sides if it's shorter than `length`. - * Padding characters are truncated if they can't be evenly divided by `length`. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * pad('abc', 8) - * // => ' abc ' - * - * pad('abc', 8, '_-') - * // => '_-abc_-_' - * - * pad('abc', 2) - * // => 'abc' - */ -function pad(string, length, chars) { - const strLength = length ? stringSize(string) : 0 - if (!length || strLength >= length) { - return (string || '') - } - const mid = (length - strLength) / 2 - return ( - createPadding(Math.floor(mid), chars) + - string + - createPadding(Math.ceil(mid), chars) - ) -} - -export default pad diff --git a/padEnd.js b/padEnd.js deleted file mode 100644 index 0fe4ab1888..0000000000 --- a/padEnd.js +++ /dev/null @@ -1,32 +0,0 @@ -import createPadding from './.internal/createPadding.js' -import stringSize from './.internal/stringSize.js' - -/** - * Pads `string` on the right side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * padEnd('abc', 6) - * // => 'abc ' - * - * padEnd('abc', 6, '_-') - * // => 'abc_-_' - * - * padEnd('abc', 2) - * // => 'abc' - */ -function padEnd(string, length, chars) { - const strLength = length ? stringSize(string) : 0 - return (length && strLength < length) - ? (string + createPadding(length - strLength, chars)) - : (string || '') -} - -export default padEnd diff --git a/padStart.js b/padStart.js deleted file mode 100644 index 0257083846..0000000000 --- a/padStart.js +++ /dev/null @@ -1,32 +0,0 @@ -import createPadding from './.internal/createPadding.js' -import stringSize from './.internal/stringSize.js' - -/** - * Pads `string` on the left side if it's shorter than `length`. Padding - * characters are truncated if they exceed `length`. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to pad. - * @param {number} [length=0] The padding length. - * @param {string} [chars=' '] The string used as padding. - * @returns {string} Returns the padded string. - * @example - * - * padStart('abc', 6) - * // => ' abc' - * - * padStart('abc', 6, '_-') - * // => '_-_abc' - * - * padStart('abc', 2) - * // => 'abc' - */ -function padStart(string, length, chars) { - const strLength = length ? stringSize(string) : 0 - return (length && strLength < length) - ? (createPadding(length - strLength, chars) + string) - : (string || '') -} - -export default padStart diff --git a/parseInt.js b/parseInt.js deleted file mode 100644 index b8975ccb5c..0000000000 --- a/parseInt.js +++ /dev/null @@ -1,36 +0,0 @@ -import root from './.internal/root.js' - -/** Used to match leading and trailing whitespace. */ -const reTrimStart = /^\s+/ - -/* Built-in method references for those with the same name as other `lodash` methods. */ -const nativeParseInt = root.parseInt - -/** - * Converts `string` to an integer of the specified radix. If `radix` is - * `undefined` or `0`, a `radix` of `10` is used unless `string` is a - * hexadecimal, in which case a `radix` of `16` is used. - * - * **Note:** This method aligns with the - * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. - * - * @since 1.1.0 - * @category String - * @param {string} string The string to convert. - * @param {number} [radix=10] The radix to interpret `string` by. - * @returns {number} Returns the converted integer. - * @example - * - * parseInt('08') - * // => 8 - */ -function parseInt(string, radix) { - if (radix == null) { - radix = 0 - } else if (radix) { - radix = +radix - } - return nativeParseInt(`${string}`.replace(reTrimStart, ''), radix || 0) -} - -export default parseInt diff --git a/partition.js b/partition.js deleted file mode 100644 index f7f9376157..0000000000 --- a/partition.js +++ /dev/null @@ -1,32 +0,0 @@ -import reduce from './reduce.js' - -/** - * Creates an array of elements split into two groups, the first of which - * contains elements `predicate` returns truthy for, the second of which - * contains elements `predicate` returns falsey for. The predicate is - * invoked with one argument: (value). - * - * @since 3.0.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the array of grouped elements. - * @see groupBy, keyBy - * @example - * - * const users = [ - * { 'user': 'barney', 'age': 36, 'active': false }, - * { 'user': 'fred', 'age': 40, 'active': true }, - * { 'user': 'pebbles', 'age': 1, 'active': false } - * ] - * - * partition(users, ({ active }) => active) - * // => objects for [['fred'], ['barney', 'pebbles']] - */ -function partition(collection, predicate) { - return reduce(collection, (result, value, key) => ( - result[predicate(value) ? 0 : 1].push(value), result - ), [[], []]) -} - -export default partition diff --git a/pick.js b/pick.js deleted file mode 100644 index 8878cb013f..0000000000 --- a/pick.js +++ /dev/null @@ -1,22 +0,0 @@ -import basePick from './.internal/basePick.js' - -/** - * Creates an object composed of the picked `object` properties. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The source object. - * @param {...(string|string[])} [paths] The property paths to pick. - * @returns {Object} Returns the new object. - * @example - * - * const object = { 'a': 1, 'b': '2', 'c': 3 } - * - * pick(object, ['a', 'c']) - * // => { 'a': 1, 'c': 3 } - */ -function pick(object, ...paths) { - return object == null ? {} : basePick(object, paths) -} - -export default pick diff --git a/pickBy.js b/pickBy.js deleted file mode 100644 index 2b8b4dbdf5..0000000000 --- a/pickBy.js +++ /dev/null @@ -1,29 +0,0 @@ -import map from './map.js' -import basePickBy from './.internal/basePickBy.js' -import getAllKeysIn from './.internal/getAllKeysIn.js' - -/** - * Creates an object composed of the `object` properties `predicate` returns - * truthy for. The predicate is invoked with two arguments: (value, key). - * - * @since 4.0.0 - * @category Object - * @param {Object} object The source object. - * @param {Function} predicate The function invoked per property. - * @returns {Object} Returns the new object. - * @example - * - * const object = { 'a': 1, 'b': '2', 'c': 3 } - * - * pickBy(object, isNumber) - * // => { 'a': 1, 'c': 3 } - */ -function pickBy(object, predicate) { - if (object == null) { - return {} - } - const props = map(getAllKeysIn(object), (prop) => [prop]) - return basePickBy(object, props, (value, path) => predicate(value, path[0])) -} - -export default pickBy diff --git a/property.js b/property.js deleted file mode 100644 index 20520c32a9..0000000000 --- a/property.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseProperty from './.internal/baseProperty.js' -import basePropertyDeep from './.internal/basePropertyDeep.js' -import isKey from './.internal/isKey.js' -import toKey from './.internal/toKey.js' - -/** - * Creates a function that returns the value at `path` of a given object. - * - * @since 2.4.0 - * @category Util - * @param {Array|string} path The path of the property to get. - * @returns {Function} Returns the new accessor function. - * @example - * - * const objects = [ - * { 'a': { 'b': 2 } }, - * { 'a': { 'b': 1 } } - * ] - * - * map(objects, property('a.b')) - * // => [2, 1] - * - * map(sortBy(objects, property(['a', 'b'])), 'a.b') - * // => [1, 2] - */ -function property(path) { - return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path) -} - -export default property diff --git a/propertyOf.js b/propertyOf.js deleted file mode 100644 index ed6daac0b0..0000000000 --- a/propertyOf.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseGet from './.internal/baseGet.js' - -/** - * The opposite of `property`s method creates a function that returns - * the value at a given path of `object`. - * - * @since 3.0.0 - * @category Util - * @param {Object} object The object to query. - * @returns {Function} Returns the new accessor function. - * @example - * - * const array = [0, 1, 2] - * const object = { 'a': array, 'b': array, 'c': array } - * - * map(['a[2]', 'c[0]'], propertyOf(object)) - * // => [2, 0] - * - * map([['a', '2'], ['c', '0']], propertyOf(object)) - * // => [2, 0] - */ -function propertyOf(object) { - return (path) => object == null ? undefined : baseGet(object, path) -} - -export default propertyOf diff --git a/pull.js b/pull.js deleted file mode 100644 index 8c4c3d7d5d..0000000000 --- a/pull.js +++ /dev/null @@ -1,29 +0,0 @@ -import pullAll from './pullAll.js' - -/** - * Removes all given values from `array` using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `without`, this method mutates `array`. Use `remove` - * to remove elements from an array by predicate. - * - * @since 2.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {...*} [values] The values to remove. - * @returns {Array} Returns `array`. - * @see pullAll, pullAllBy, pullAllWith, pullAt, remove, reject - * @example - * - * const array = ['a', 'b', 'c', 'a', 'b', 'c'] - * - * pull(array, 'a', 'c') - * console.log(array) - * // => ['b', 'b'] - */ -function pull(array, ...values) { - return pullAll(array, values) -} - -export default pull diff --git a/pullAll.js b/pullAll.js deleted file mode 100644 index 1f320204b9..0000000000 --- a/pullAll.js +++ /dev/null @@ -1,28 +0,0 @@ -import basePullAll from './.internal/basePullAll.js' - -/** - * This method is like `pull` except that it accepts an array of values to remove. - * - * **Note:** Unlike `difference`, this method mutates `array`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @returns {Array} Returns `array`. - * @see pull, pullAllBy, pullAllWith, pullAt, remove, reject - * @example - * - * const array = ['a', 'b', 'c', 'a', 'b', 'c'] - * - * pullAll(array, ['a', 'c']) - * console.log(array) - * // => ['b', 'b'] - */ -function pullAll(array, values) { - return (array != null && array.length && values != null && values.length) - ? basePullAll(array, values) - : array -} - -export default pullAll diff --git a/pullAllBy.js b/pullAllBy.js deleted file mode 100644 index 863628d733..0000000000 --- a/pullAllBy.js +++ /dev/null @@ -1,31 +0,0 @@ -import basePullAll from './.internal/basePullAll.js' - -/** - * This method is like `pullAll` except that it accepts `iteratee` which is - * invoked for each element of `array` and `values` to generate the criterion - * by which they're compared. The iteratee is invoked with one argument: (value). - * - * **Note:** Unlike `differenceBy`, this method mutates `array`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns `array`. - * @see pull, pullAll, pullAllWith, pullAt, remove, reject - * @example - * - * const array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }] - * - * pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x') - * console.log(array) - * // => [{ 'x': 2 }] - */ -function pullAllBy(array, values, iteratee) { - return (array != null && array.length && values != null && values.length) - ? basePullAll(array, values, iteratee) - : array -} - -export default pullAllBy diff --git a/pullAllWith.js b/pullAllWith.js deleted file mode 100644 index bd77c5849b..0000000000 --- a/pullAllWith.js +++ /dev/null @@ -1,31 +0,0 @@ -import basePullAll from './.internal/basePullAll.js' - -/** - * This method is like `pullAll` except that it accepts `comparator` which - * is invoked to compare elements of `array` to `values`. The comparator is - * invoked with two arguments: (arrVal, othVal). - * - * **Note:** Unlike `differenceWith`, this method mutates `array`. - * - * @since 4.6.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Array} values The values to remove. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns `array`. - * @see pull, pullAll, pullAllBy, pullAt, remove, reject - * @example - * - * const array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }] - * - * pullAllWith(array, [{ 'x': 3, 'y': 4 }], isEqual) - * console.log(array) - * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] - */ -function pullAllWith(array, values, comparator) { - return (array != null && array.length && values != null && values.length) - ? basePullAll(array, values, undefined, comparator) - : array -} - -export default pullAllWith diff --git a/pullAt.js b/pullAt.js deleted file mode 100644 index 86a22707ea..0000000000 --- a/pullAt.js +++ /dev/null @@ -1,38 +0,0 @@ -import map from './map.js' -import baseAt from './.internal/baseAt.js' -import basePullAt from './.internal/basePullAt.js' -import compareAscending from './.internal/compareAscending.js' -import isIndex from './.internal/isIndex.js' - -/** - * Removes elements from `array` corresponding to `indexes` and returns an - * array of removed elements. - * - * **Note:** Unlike `at`, this method mutates `array`. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {...(number|number[])} [indexes] The indexes of elements to remove. - * @returns {Array} Returns the new array of removed elements. - * @see pull, pullAll, pullAllBy, pullAllWith, remove, reject - * @example - * - * const array = ['a', 'b', 'c', 'd'] - * const pulled = pullAt(array, [1, 3]) - * - * console.log(array) - * // => ['a', 'c'] - * - * console.log(pulled) - * // => ['b', 'd'] - */ -function pullAt(array, ...indexes) { - const length = array == null ? 0 : array.length - const result = baseAt(array, indexes) - - basePullAt(array, map(indexes, (index) => isIndex(index, length) ? +index : index).sort(compareAscending)) - return result -} - -export default pullAt diff --git a/random.js b/random.js deleted file mode 100644 index 7f2acc6557..0000000000 --- a/random.js +++ /dev/null @@ -1,73 +0,0 @@ -import toFinite from './toFinite.js' - -/** Built-in method references without a dependency on `root`. */ -const freeParseFloat = parseFloat - -/** - * Produces a random number between the inclusive `lower` and `upper` bounds. - * If only one argument is provided a number between `0` and the given number - * is returned. If `floating` is `true`, or either `lower` or `upper` are - * floats, a floating-point number is returned instead of an integer. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @since 0.7.0 - * @category Number - * @param {number} [lower=0] The lower bound. - * @param {number} [upper=1] The upper bound. - * @param {boolean} [floating] Specify returning a floating-point number. - * @returns {number} Returns the random number. - * @see uniqueId - * @example - * - * random(0, 5) - * // => an integer between 0 and 5 - * - * random(5) - * // => also an integer between 0 and 5 - * - * random(5, true) - * // => a floating-point number between 0 and 5 - * - * random(1.2, 5.2) - * // => a floating-point number between 1.2 and 5.2 - */ -function random(lower, upper, floating) { - if (floating === undefined) { - if (typeof upper === 'boolean') { - floating = upper - upper = undefined - } - else if (typeof lower === 'boolean') { - floating = lower - lower = undefined - } - } - if (lower === undefined && upper === undefined) { - lower = 0 - upper = 1 - } - else { - lower = toFinite(lower) - if (upper === undefined) { - upper = lower - lower = 0 - } else { - upper = toFinite(upper) - } - } - if (lower > upper) { - const temp = lower - lower = upper - upper = temp - } - if (floating || lower % 1 || upper % 1) { - const rand = Math.random() - const randLength = `${rand}`.length - 1 - return Math.min(lower + (rand * (upper - lower + freeParseFloat(`1e-${randLength}`))), upper) - } - return lower + Math.floor(Math.random() * (upper - lower + 1)) -} - -export default random diff --git a/range.js b/range.js deleted file mode 100644 index 8925c3d63f..0000000000 --- a/range.js +++ /dev/null @@ -1,44 +0,0 @@ -import createRange from './.internal/createRange.js' - -/** - * Creates an array of numbers (positive and/or negative) progressing from - * `start` up to, but not including, `end`. A step of `-1` is used if a negative - * `start` is specified without an `end` or `step`. If `end` is not specified, - * it's set to `start`, and `start` is then set to `0`. - * - * **Note:** JavaScript follows the IEEE-754 standard for resolving - * floating-point values which can produce unexpected results. - * - * @since 0.1.0 - * @category Util - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns the range of numbers. - * @see inRange, rangeRight - * @example - * - * range(4) - * // => [0, 1, 2, 3] - * - * range(-4) - * // => [0, -1, -2, -3] - * - * range(1, 5) - * // => [1, 2, 3, 4] - * - * range(0, 20, 5) - * // => [0, 5, 10, 15] - * - * range(0, -4, -1) - * // => [0, -1, -2, -3] - * - * range(1, 4, 0) - * // => [1, 1, 1] - * - * range(0) - * // => [] - */ -const range = createRange() - -export default range diff --git a/rangeRight.js b/rangeRight.js deleted file mode 100644 index 139f14c434..0000000000 --- a/rangeRight.js +++ /dev/null @@ -1,39 +0,0 @@ -import createRange from './.internal/createRange.js' - -/** - * This method is like `range` except that it populates values in - * descending order. - * - * @since 4.0.0 - * @category Util - * @param {number} [start=0] The start of the range. - * @param {number} end The end of the range. - * @param {number} [step=1] The value to increment or decrement by. - * @returns {Array} Returns the range of numbers. - * @see inRange, range - * @example - * - * rangeRight(4) - * // => [3, 2, 1, 0] - * - * rangeRight(-4) - * // => [-3, -2, -1, 0] - * - * rangeRight(1, 5) - * // => [4, 3, 2, 1] - * - * rangeRight(0, 20, 5) - * // => [15, 10, 5, 0] - * - * rangeRight(0, -4, -1) - * // => [-3, -2, -1, 0] - * - * rangeRight(1, 4, 0) - * // => [1, 1, 1] - * - * rangeRight(0) - * // => [] - */ -const rangeRight = createRange(true) - -export default rangeRight diff --git a/reduce.js b/reduce.js deleted file mode 100644 index 0ad5dfe86b..0000000000 --- a/reduce.js +++ /dev/null @@ -1,44 +0,0 @@ -import arrayReduce from './.internal/arrayReduce.js' -import baseEach from './.internal/baseEach.js' -import baseReduce from './.internal/baseReduce.js' - -/** - * Reduces `collection` to a value which is the accumulated result of running - * each element in `collection` thru `iteratee`, where each successive - * invocation is supplied the return value of the previous. If `accumulator` - * is not given, the first element of `collection` is used as the initial - * value. The iteratee is invoked with four arguments: - * (accumulator, value, index|key, collection). - * - * Many lodash methods are guarded to work as iteratees for methods like - * `reduce`, `reduceRight`, and `transform`. - * - * The guarded methods are: - * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, - * and `sortBy` - * - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @returns {*} Returns the accumulated value. - * @see reduceRight, transform - * @example - * - * reduce([1, 2], (sum, n) => sum + n, 0) - * // => 3 - * - * reduce({ 'a': 1, 'b': 2, 'c': 1 }, (result, value, key) => { - * (result[value] || (result[value] = [])).push(key) - * return result - * }, {}) - * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) - */ -function reduce(collection, iteratee, accumulator) { - const func = Array.isArray(collection) ? arrayReduce : baseReduce - const initAccum = arguments.length < 3 - return func(collection, iteratee, accumulator, initAccum, baseEach) -} - -export default reduce diff --git a/reduceRight.js b/reduceRight.js deleted file mode 100644 index 810c96dae5..0000000000 --- a/reduceRight.js +++ /dev/null @@ -1,29 +0,0 @@ -import arrayReduceRight from './.internal/arrayReduceRight.js' -import baseEachRight from './.internal/baseEachRight.js' -import baseReduce from './.internal/baseReduce.js' - -/** - * This method is like `reduce` except that it iterates over elements of - * `collection` from right to left. - * - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The initial value. - * @returns {*} Returns the accumulated value. - * @see reduce - * @example - * - * const array = [[0, 1], [2, 3], [4, 5]] - * - * reduceRight(array, (flattened, other) => flattened.concat(other), []) - * // => [4, 5, 2, 3, 0, 1] - */ -function reduceRight(collection, iteratee, accumulator) { - const func = Array.isArray(collection) ? arrayReduceRight : baseReduce - const initAccum = arguments.length < 3 - return func(collection, iteratee, accumulator, initAccum, baseEachRight) -} - -export default reduceRight diff --git a/reject.js b/reject.js deleted file mode 100644 index 726d975ff9..0000000000 --- a/reject.js +++ /dev/null @@ -1,30 +0,0 @@ -import filter from './filter.js' -import filterObject from './filterObject.js' -import negate from './negate.js' - -/** - * The opposite of `filter` this method returns the elements of `collection` - * that `predicate` does **not** return truthy for. - * - * @since 0.1.0 - * @category Collection - * @param {Array|Object} collection The collection to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new filtered array. - * @see pull, pullAll, pullAllBy, pullAllWith, pullAt, remove, filter - * @example - * - * const users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': false } - * ] - * - * reject(users, ({ active }) => active) - * // => objects for ['fred'] - */ -function reject(collection, predicate) { - const func = Array.isArray(collection) ? filter : filterObject - return func(collection, negate(predicate)) -} - -export default reject diff --git a/remove.js b/remove.js deleted file mode 100644 index e09cd9ef26..0000000000 --- a/remove.js +++ /dev/null @@ -1,48 +0,0 @@ -import basePullAt from './.internal/basePullAt.js' - -/** - * Removes all elements from `array` that `predicate` returns truthy for - * and returns an array of the removed elements. The predicate is invoked - * with three arguments: (value, index, array). - * - * **Note:** Unlike `filter`, this method mutates `array`. Use `pull` - * to pull elements from an array by value. - * - * @since 2.0.0 - * @category Array - * @param {Array} array The array to modify. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the new array of removed elements. - * @see pull, pullAll, pullAllBy, pullAllWith, pullAt, reject, filter - * @example - * - * const array = [1, 2, 3, 4] - * const evens = remove(array, n => n % 2 == 0) - * - * console.log(array) - * // => [1, 3] - * - * console.log(evens) - * // => [2, 4] - */ -function remove(array, predicate) { - const result = [] - if (!(array != null && array.length)) { - return result - } - let index = -1 - const indexes = [] - const { length } = array - - while (++index < length) { - const value = array[index] - if (predicate(value, index, array)) { - result.push(value) - indexes.push(index) - } - } - basePullAt(array, indexes) - return result -} - -export default remove diff --git a/repeat.js b/repeat.js deleted file mode 100644 index 89d8403dab..0000000000 --- a/repeat.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Repeats the given string `n` times. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to repeat. - * @param {number} [n=1] The number of times to repeat the string. - * @returns {string} Returns the repeated string. - * @example - * - * repeat('*', 3) - * // => '***' - * - * repeat('abc', 2) - * // => 'abcabc' - * - * repeat('abc', 0) - * // => '' - */ -function repeat(string, n) { - let result = '' - if (!string || n < 1 || n > Number.MAX_SAFE_INTEGER) { - return result - } - // Leverage the exponentiation by squaring algorithm for a faster repeat. - // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. - do { - if (n % 2) { - result += string - } - n = Math.floor(n / 2) - if (n) { - string += string - } - } while (n) - - return result -} - -export default repeat diff --git a/replace.js b/replace.js deleted file mode 100644 index d53d0fdb6f..0000000000 --- a/replace.js +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Replaces matches for `pattern` in `string` with `replacement`. - * - * **Note:** This method is based on - * [`String#replace`](https://mdn.io/String/replace). - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to modify. - * @param {RegExp|string} pattern The pattern to replace. - * @param {Function|string} replacement The match replacement. - * @returns {string} Returns the modified string. - * @see truncate, trim - * @example - * - * replace('Hi Fred', 'Fred', 'Barney') - * // => 'Hi Barney' - */ -function replace(...args) { - const string = `${args[0]}` - return args.length < 3 ? string : string.replace(args[1], args[2]) -} - -export default replace diff --git a/result.js b/result.js deleted file mode 100644 index e121780091..0000000000 --- a/result.js +++ /dev/null @@ -1,53 +0,0 @@ -import castPath from './.internal/castPath.js' -import toKey from './.internal/toKey.js' - -/** - * This method is like `get` except that if the resolved value is a - * function it's invoked with the `this` binding of its parent object and - * its result is returned. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The object to query. - * @param {Array|string} path The path of the property to resolve. - * @param {*} [defaultValue] The value returned for `undefined` resolved values. - * @returns {*} Returns the resolved value. - * @example - * - * const object = { 'a': [{ 'b': { 'c1': 3, 'c2': () => 4 } }] } - * - * result(object, 'a[0].b.c1') - * // => 3 - * - * result(object, 'a[0].b.c2') - * // => 4 - * - * result(object, 'a[0].b.c3', 'default') - * // => 'default' - * - * result(object, 'a[0].b.c3', () => 'default') - * // => 'default' - */ -function result(object, path, defaultValue) { - path = castPath(path, object) - - let index = -1 - let length = path.length - - // Ensure the loop is entered when path is empty. - if (!length) { - length = 1 - object = undefined - } - while (++index < length) { - let value = object == null ? undefined : object[toKey(path[index])] - if (value === undefined) { - index = length - value = defaultValue - } - object = typeof value === 'function' ? value.call(object) : value - } - return object -} - -export default result diff --git a/round.js b/round.js deleted file mode 100644 index b8be056216..0000000000 --- a/round.js +++ /dev/null @@ -1,24 +0,0 @@ -import createRound from './.internal/createRound.js' - -/** - * Computes `number` rounded to `precision`. - * - * @since 3.10.0 - * @category Math - * @param {number} number The number to round. - * @param {number} [precision=0] The precision to round to. - * @returns {number} Returns the rounded number. - * @example - * - * round(4.006) - * // => 4 - * - * round(4.006, 2) - * // => 4.01 - * - * round(4060, -2) - * // => 4100 - */ -const round = createRound('round') - -export default round diff --git a/sample.js b/sample.js deleted file mode 100644 index 60e2d8921b..0000000000 --- a/sample.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Gets a random element from `array`. - * - * @since 2.0.0 - * @category Array - * @param {Array} array The array to sample. - * @returns {*} Returns the random element. - * @example - * - * sample([1, 2, 3, 4]) - * // => 2 - */ -function sample(array) { - const length = array == null ? 0 : array.length - return length ? array[Math.floor(Math.random() * length)] : undefined -} - -export default sample diff --git a/sampleSize.js b/sampleSize.js deleted file mode 100644 index 265713cbf5..0000000000 --- a/sampleSize.js +++ /dev/null @@ -1,40 +0,0 @@ -import copyArray from './.internal/copyArray.js' -import slice from './slice.js' - -/** - * Gets `n` random elements at unique keys from `array` up to the - * size of `array`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to sample. - * @param {number} [n=1] The number of elements to sample. - * @returns {Array} Returns the random elements. - * @example - * - * sampleSize([1, 2, 3], 2) - * // => [3, 1] - * - * sampleSize([1, 2, 3], 4) - * // => [2, 3, 1] - */ -function sampleSize(array, n) { - n = n == null ? 1 : n - const length = array == null ? 0 : array.length - if (!length || n < 1) { - return [] - } - n = n > length ? length : n - let index = -1 - const lastIndex = length - 1 - const result = copyArray(array) - while (++index < n) { - const rand = index + Math.floor(Math.random() * (lastIndex - index + 1)) - const value = result[rand] - result[rand] = result[index] - result[index] = value - } - return slice(result, 0, n) -} - -export default sampleSize diff --git a/set.js b/set.js deleted file mode 100644 index 3004ec5b95..0000000000 --- a/set.js +++ /dev/null @@ -1,34 +0,0 @@ -import baseSet from './.internal/baseSet.js' - -/** - * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, - * it's created. Arrays are created for missing index properties while objects - * are created for all other missing properties. Use `setWith` to customize - * `path` creation. - * - * **Note:** This method mutates `object`. - * - * @since 3.7.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @returns {Object} Returns `object`. - * @see has, hasIn, get, unset - * @example - * - * const object = { 'a': [{ 'b': { 'c': 3 } }] } - * - * set(object, 'a[0].b.c', 4) - * console.log(object.a[0].b.c) - * // => 4 - * - * set(object, ['x', '0', 'y', 'z'], 5) - * console.log(object.x[0].y.z) - * // => 5 - */ -function set(object, path, value) { - return object == null ? object : baseSet(object, path, value) -} - -export default set diff --git a/setWith.js b/setWith.js deleted file mode 100644 index b070940ca1..0000000000 --- a/setWith.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseSet from './.internal/baseSet.js' - -/** - * This method is like `set` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {*} value The value to set. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * const object = {} - * - * setWith(object, '[0][1]', 'a', Object) - * // => { '0': { '1': 'a' } } - */ -function setWith(object, path, value, customizer) { - customizer = typeof customizer === 'function' ? customizer : undefined - return object == null ? object : baseSet(object, path, value, customizer) -} - -export default setWith diff --git a/shuffle.js b/shuffle.js deleted file mode 100644 index dc45d12302..0000000000 --- a/shuffle.js +++ /dev/null @@ -1,33 +0,0 @@ -import copyArray from './.internal/copyArray.js' - -/** - * Creates an array of shuffled values, using a version of the - * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to shuffle. - * @returns {Array} Returns the new shuffled array. - * @example - * - * shuffle([1, 2, 3, 4]) - * // => [4, 1, 3, 2] - */ -function shuffle(array) { - const length = array == null ? 0 : array.length - if (!length) { - return [] - } - let index = -1 - const lastIndex = length - 1 - const result = copyArray(array) - while (++index < length) { - const rand = index + Math.floor(Math.random() * (lastIndex - index + 1)) - const value = result[rand] - result[rand] = result[index] - result[index] = value - } - return result -} - -export default shuffle diff --git a/size.js b/size.js deleted file mode 100644 index 831011e568..0000000000 --- a/size.js +++ /dev/null @@ -1,43 +0,0 @@ -import getTag from './.internal/getTag.js' -import isArrayLike from './isArrayLike.js' -import isString from './isString.js' -import stringSize from './.internal/stringSize.js' - -/** `Object#toString` result references. */ -const mapTag = '[object Map]' -const setTag = '[object Set]' - -/** - * Gets the size of `collection` by returning its length for array-like - * values or the number of own enumerable string keyed properties for objects. - * - * @since 0.1.0 - * @category Collection - * @param {Array|Object|string} collection The collection to inspect. - * @returns {number} Returns the collection size. - * @example - * - * size([1, 2, 3]) - * // => 3 - * - * size({ 'a': 1, 'b': 2 }) - * // => 2 - * - * size('pebbles') - * // => 7 - */ -function size(collection) { - if (collection == null) { - return 0 - } - if (isArrayLike(collection)) { - return isString(collection) ? stringSize(collection) : collection.length - } - const tag = getTag(collection) - if (tag == mapTag || tag == setTag) { - return collection.size - } - return Object.keys(collection).length -} - -export default size diff --git a/slice.js b/slice.js deleted file mode 100644 index 9c24d4dc18..0000000000 --- a/slice.js +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Creates a slice of `array` from `start` up to, but not including, `end`. - * - * **Note:** This method is used instead of - * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are - * returned. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to slice. - * @param {number} [start=0] The start position. A negative index will be treated as an offset from the end. - * @param {number} [end=array.length] The end position. A negative index will be treated as an offset from the end. - * @returns {Array} Returns the slice of `array`. - * @example - * - * var array = [1, 2, 3, 4] - * - * _.slice(array, 2) - * // => [3, 4] - */ -function slice(array, start, end) { - let length = array == null ? 0 : array.length - if (!length) { - return [] - } - start = start == null ? 0 : start - end = end === undefined ? length : end - - if (start < 0) { - start = -start > length ? 0 : (length + start) - } - end = end > length ? length : end - if (end < 0) { - end += length - } - length = start > end ? 0 : ((end - start) >>> 0) - start >>>= 0 - - let index = -1 - const result = new Array(length) - while (++index < length) { - result[index] = array[index + start] - } - return result -} - -export default slice diff --git a/snakeCase.js b/snakeCase.js deleted file mode 100644 index 8ddde6e41b..0000000000 --- a/snakeCase.js +++ /dev/null @@ -1,33 +0,0 @@ -import words from './words.js' -import toString from './toString.js' - -/** - * Converts `string` to - * [snake case](https://en.wikipedia.org/wiki/Snake_case). - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the snake cased string. - * @see camelCase, lowerCase, kebabCase, startCase, upperCase, upperFirst - * @example - * - * snakeCase('Foo Bar') - * // => 'foo_bar' - * - * snakeCase('fooBar') - * // => 'foo_bar' - * - * snakeCase('--FOO-BAR--') - * // => 'foo_bar' - * - * snakeCase('foo2bar') - * // => 'foo_2_bar' - */ -const snakeCase = (string) => ( - words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => ( - result + (index ? '_' : '') + word.toLowerCase() - ), '') -) - -export default snakeCase diff --git a/some.js b/some.js deleted file mode 100644 index 6f8eebcadf..0000000000 --- a/some.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Checks if `predicate` returns truthy for **any** element of `array`. - * Iteration is stopped once `predicate` returns truthy. The predicate is - * invoked with three arguments: (value, index, array). - * - * @since 5.0.0 - * @category Array - * @param {Array} array The array to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - * @example - * - * some([null, 0, 'yes', false], Boolean) - * // => true - */ -function some(array, predicate) { - let index = -1 - const length = array == null ? 0 : array.length - - while (++index < length) { - if (predicate(array[index], index, array)) { - return true - } - } - return false -} - -export default some diff --git a/someValue.js b/someValue.js deleted file mode 100644 index a85f0028a0..0000000000 --- a/someValue.js +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Checks if `predicate` returns truthy for **any** element of `object`. - * Iteration is stopped once `predicate` returns truthy. The predicate is - * invoked with three arguments: (value, key, object). - * - * @since 5.0.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} predicate The function invoked per iteration. - * @returns {boolean} Returns `true` if any element passes the predicate check, - * else `false`. - * @example - * - * someValues({ 'a': 0, 'b': 'yes', 'c': false }, Boolean) - * // => true - */ -function someValues(object, predicate) { - object = Object(object) - const props = Object.keys(object) - - for (const key of props) { - if (predicate(object[key], key, object)) { - return true - } - } - return false -} - -export default someValues diff --git a/sortedIndex.js b/sortedIndex.js deleted file mode 100644 index fa84fe9b7c..0000000000 --- a/sortedIndex.js +++ /dev/null @@ -1,22 +0,0 @@ -import baseSortedIndex from './.internal/baseSortedIndex.js' - -/** - * Uses a binary search to determine the lowest index at which `value` - * should be inserted into `array` in order to maintain its sort order. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * sortedIndex([30, 50], 40) - * // => 1 - */ -function sortedIndex(array, value) { - return baseSortedIndex(array, value) -} - -export default sortedIndex diff --git a/sortedIndexBy.js b/sortedIndexBy.js deleted file mode 100644 index 61c1c6fae6..0000000000 --- a/sortedIndexBy.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseSortedIndexBy from './.internal/baseSortedIndexBy.js' - -/** - * This method is like `sortedIndex` except that it accepts `iteratee` - * which is invoked for `value` and each element of `array` to compute their - * sort ranking. The iteratee is invoked with one argument: (value). - * - * @since 4.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * const objects = [{ 'n': 4 }, { 'n': 5 }] - * - * sortedIndexBy(objects, { 'n': 4 }, ({ n }) => n) - * // => 0 - */ -function sortedIndexBy(array, value, iteratee) { - return baseSortedIndexBy(array, value, iteratee) -} - -export default sortedIndexBy diff --git a/sortedIndexOf.js b/sortedIndexOf.js deleted file mode 100644 index d30324091f..0000000000 --- a/sortedIndexOf.js +++ /dev/null @@ -1,29 +0,0 @@ -import baseSortedIndex from './.internal/baseSortedIndex.js' -import eq from './eq.js' - -/** - * This method is like `indexOf` except that it performs a binary - * search on a sorted `array`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * sortedIndexOf([4, 5, 5, 5, 6], 5) - * // => 1 - */ -function sortedIndexOf(array, value) { - const length = array == null ? 0 : array.length - if (length) { - const index = baseSortedIndex(array, value) - if (index < length && eq(array[index], value)) { - return index - } - } - return -1 -} - -export default sortedIndexOf diff --git a/sortedLastIndex.js b/sortedLastIndex.js deleted file mode 100644 index aee639fcae..0000000000 --- a/sortedLastIndex.js +++ /dev/null @@ -1,23 +0,0 @@ -import baseSortedIndex from './.internal/baseSortedIndex.js' - -/** - * This method is like `sortedIndex` except that it returns the highest - * index at which `value` should be inserted into `array` in order to - * maintain its sort order. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * sortedLastIndex([4, 5, 5, 5, 6], 5) - * // => 4 - */ -function sortedLastIndex(array, value) { - return baseSortedIndex(array, value, true) -} - -export default sortedLastIndex diff --git a/sortedLastIndexBy.js b/sortedLastIndexBy.js deleted file mode 100644 index a2a14dd063..0000000000 --- a/sortedLastIndexBy.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseSortedIndexBy from './.internal/baseSortedIndexBy.js' - -/** - * This method is like `sortedLastIndex` except that it accepts `iteratee` - * which is invoked for `value` and each element of `array` to compute their - * sort ranking. The iteratee is invoked with one argument: (value). - * - * @since 4.0.0 - * @category Array - * @param {Array} array The sorted array to inspect. - * @param {*} value The value to evaluate. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {number} Returns the index at which `value` should be inserted - * into `array`. - * @example - * - * const objects = [{ 'n': 4 }, { 'n': 5 }] - * - * sortedLastIndexBy(objects, { 'n': 4 }, ({ n }) => n) - * // => 1 - */ -function sortedLastIndexBy(array, value, iteratee) { - return baseSortedIndexBy(array, value, iteratee, true) -} - -export default sortedLastIndexBy diff --git a/sortedLastIndexOf.js b/sortedLastIndexOf.js deleted file mode 100644 index f4d4ea3357..0000000000 --- a/sortedLastIndexOf.js +++ /dev/null @@ -1,29 +0,0 @@ -import baseSortedIndex from './.internal/baseSortedIndex.js' -import eq from './eq.js' - -/** - * This method is like `lastIndexOf` except that it performs a binary - * search on a sorted `array`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {*} value The value to search for. - * @returns {number} Returns the index of the matched value, else `-1`. - * @example - * - * sortedLastIndexOf([4, 5, 5, 5, 6], 5) - * // => 3 - */ -function sortedLastIndexOf(array, value) { - const length = array == null ? 0 : array.length - if (length) { - const index = baseSortedIndex(array, value, true) - 1 - if (eq(array[index], value)) { - return index - } - } - return -1 -} - -export default sortedLastIndexOf diff --git a/sortedUniq.js b/sortedUniq.js deleted file mode 100644 index 31d1e94bd8..0000000000 --- a/sortedUniq.js +++ /dev/null @@ -1,24 +0,0 @@ -import baseSortedUniq from './.internal/baseSortedUniq.js' - -/** - * This method is like `uniq` except that it only works - * for sorted arrays. - * If the input array is known to be sorted `sortedUniq` is - * faster than `uniq`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * sortedUniq([1, 1, 2]) - * // => [1, 2] - */ -function sortedUniq(array) { - return (array != null && array.length) - ? baseSortedUniq(array) - : [] -} - -export default sortedUniq diff --git a/sortedUniqBy.js b/sortedUniqBy.js deleted file mode 100644 index 32ca129c8d..0000000000 --- a/sortedUniqBy.js +++ /dev/null @@ -1,23 +0,0 @@ -import baseSortedUniq from './.internal/baseSortedUniq.js' - -/** - * This method is like `uniqBy` except that it's designed and optimized - * for sorted arrays. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @example - * - * sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor) - * // => [1.1, 2.3] - */ -function sortedUniqBy(array, iteratee) { - return (array != null && array.length) - ? baseSortedUniq(array, iteratee) - : [] -} - -export default sortedUniqBy diff --git a/split.js b/split.js deleted file mode 100644 index e997c4a7fc..0000000000 --- a/split.js +++ /dev/null @@ -1,42 +0,0 @@ -import castSlice from './.internal/castSlice.js' -import hasUnicode from './.internal/hasUnicode.js' -import isRegExp from './isRegExp.js' -import stringToArray from './.internal/stringToArray.js' - -/** Used as references for the maximum length and index of an array. */ -const MAX_ARRAY_LENGTH = 4294967295 - -/** - * Splits `string` by `separator`. - * - * **Note:** This method is based on - * [`String#split`](https://mdn.io/String/split). - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to split. - * @param {RegExp|string} separator The separator pattern to split by. - * @param {number} [limit] The length to truncate results to. - * @returns {Array} Returns the string segments. - * @example - * - * split('a-b-c', '-', 2) - * // => ['a', 'b'] - */ -function split(string, separator, limit) { - limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0 - if (!limit) { - return [] - } - if (string && ( - typeof separator === 'string' || - (separator != null && !isRegExp(separator)) - )) { - if (!separator && hasUnicode(string)) { - return castSlice(stringToArray(string), 0, limit) - } - } - return string.split(separator, limit) -} - -export default split diff --git a/src/.eslintrc.cjs b/src/.eslintrc.cjs new file mode 100644 index 0000000000..66ca1ac783 --- /dev/null +++ b/src/.eslintrc.cjs @@ -0,0 +1,18 @@ +'use strict'; + +const path = require('node:path'); + +module.exports = { + overrides: [ + { + files: ['**/*.{ts}'], + rules: { + 'import/no-extraneous-dependencies': [ + 'error', + // Use package.json from both this package folder and root. + { packageDir: [__dirname, path.join(__dirname, '../')] }, + ], + }, + }, + ], +}; diff --git a/.internal/Hash.js b/src/.internal/Hash.ts similarity index 100% rename from .internal/Hash.js rename to src/.internal/Hash.ts diff --git a/src/.internal/ListCache.ts b/src/.internal/ListCache.ts new file mode 100644 index 0000000000..5473945b66 --- /dev/null +++ b/src/.internal/ListCache.ts @@ -0,0 +1,103 @@ +import assocIndexOf from './assocIndexOf.js' + +class ListCache { + + /** + * Creates an list cache object. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + constructor(entries) { + let index = -1 + const length = entries == null ? 0 : entries.length + + this.clear() + while (++index < length) { + const entry = entries[index] + this.set(entry[0], entry[1]) + } + } + + /** + * Removes all key-value entries from the list cache. + * + * @memberOf ListCache + */ + clear() { + this.__data__ = [] + this.size = 0 + } + + /** + * Removes `key` and its value from the list cache. + * + * @memberOf ListCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + delete(key) { + const data = this.__data__ + const index = assocIndexOf(data, key) + + if (index < 0) { + return false + } + const lastIndex = data.length - 1 + if (index === lastIndex) { + data.pop() + } else { + data.splice(index, 1) + } + --this.size + return true + } + + /** + * Gets the list cache value for `key`. + * + * @memberOf ListCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + get(key) { + const data = this.__data__ + const index = assocIndexOf(data, key) + return index < 0 ? undefined : data[index][1] + } + + /** + * Checks if a list cache value for `key` exists. + * + * @memberOf ListCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + has(key) { + return assocIndexOf(this.__data__, key) > -1 + } + + /** + * Sets the list cache `key` to `value`. + * + * @memberOf ListCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the list cache instance. + */ + set(key, value) { + const data = this.__data__ + const index = assocIndexOf(data, key) + + if (index < 0) { + ++this.size + data.push([key, value]) + } else { + data[index][1] = value + } + return this + } +} + +export default ListCache diff --git a/src/.internal/MapCache.ts b/src/.internal/MapCache.ts new file mode 100644 index 0000000000..d031ff14ec --- /dev/null +++ b/src/.internal/MapCache.ts @@ -0,0 +1,120 @@ + +import Hash from './Hash.js' + +/** + * Gets the data for `map`. + * + * @private + * @param {Object} map The map to query. + * @param {string} key The reference key. + * @returns {*} Returns the map data. + */ +function getMapData({ __data__ }, key) { + const data = __data__ + return isKeyable(key) + ? data[typeof key === 'string' ? 'string' : 'hash'] + : data.map +} + +/** + * Checks if `value` is suitable for use as unique object key. + * + * @private + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is suitable, else `false`. + */ +function isKeyable(value) { + const type = typeof value + return (type === 'string' || type === 'number' || type === 'symbol' || type === 'boolean') + ? (value !== '__proto__') + : (value === null) +} + +class MapCache { + + /** + * Creates a map cache object to store key-value pairs. + * + * @private + * @constructor + * @param {Array} [entries] The key-value pairs to cache. + */ + constructor(entries) { + let index = -1 + const length = entries == null ? 0 : entries.length + + this.clear() + while (++index < length) { + const entry = entries[index] + this.set(entry[0], entry[1]) + } + } + + /** + * Removes all key-value entries from the map. + * + * @memberOf MapCache + */ + clear() { + this.size = 0 + this.__data__ = { + 'hash': new Hash, + 'map': new Map, + 'string': new Hash + } + } + + /** + * Removes `key` and its value from the map. + * + * @memberOf MapCache + * @param {string} key The key of the value to remove. + * @returns {boolean} Returns `true` if the entry was removed, else `false`. + */ + delete(key) { + const result = getMapData(this, key)['delete'](key) + this.size -= result ? 1 : 0 + return result + } + + /** + * Gets the map value for `key`. + * + * @memberOf MapCache + * @param {string} key The key of the value to get. + * @returns {*} Returns the entry value. + */ + get(key) { + return getMapData(this, key).get(key) + } + + /** + * Checks if a map value for `key` exists. + * + * @memberOf MapCache + * @param {string} key The key of the entry to check. + * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`. + */ + has(key) { + return getMapData(this, key).has(key) + } + + /** + * Sets the map `key` to `value`. + * + * @memberOf MapCache + * @param {string} key The key of the value to set. + * @param {*} value The value to set. + * @returns {Object} Returns the map cache instance. + */ + set(key, value) { + const data = getMapData(this, key) + const size = data.size + + data.set(key, value) + this.size += data.size === size ? 0 : 1 + return this + } +} + +export default MapCache diff --git a/.internal/SetCache.js b/src/.internal/SetCache.ts similarity index 100% rename from .internal/SetCache.js rename to src/.internal/SetCache.ts diff --git a/.internal/Stack.js b/src/.internal/Stack.ts similarity index 100% rename from .internal/Stack.js rename to src/.internal/Stack.ts diff --git a/.internal/addMapEntry.js b/src/.internal/addMapEntry.ts similarity index 100% rename from .internal/addMapEntry.js rename to src/.internal/addMapEntry.ts diff --git a/.internal/addSetEntry.js b/src/.internal/addSetEntry.ts similarity index 100% rename from .internal/addSetEntry.js rename to src/.internal/addSetEntry.ts diff --git a/.internal/arrayEach.js b/src/.internal/arrayEach.ts similarity index 100% rename from .internal/arrayEach.js rename to src/.internal/arrayEach.ts diff --git a/.internal/arrayEachRight.js b/src/.internal/arrayEachRight.ts similarity index 100% rename from .internal/arrayEachRight.js rename to src/.internal/arrayEachRight.ts diff --git a/.internal/arrayIncludes.js b/src/.internal/arrayIncludes.ts similarity index 100% rename from .internal/arrayIncludes.js rename to src/.internal/arrayIncludes.ts diff --git a/.internal/arrayIncludesWith.js b/src/.internal/arrayIncludesWith.ts similarity index 100% rename from .internal/arrayIncludesWith.js rename to src/.internal/arrayIncludesWith.ts diff --git a/.internal/arrayLikeKeys.js b/src/.internal/arrayLikeKeys.ts similarity index 100% rename from .internal/arrayLikeKeys.js rename to src/.internal/arrayLikeKeys.ts diff --git a/.internal/arrayReduce.js b/src/.internal/arrayReduce.ts similarity index 100% rename from .internal/arrayReduce.js rename to src/.internal/arrayReduce.ts diff --git a/.internal/arrayReduceRight.js b/src/.internal/arrayReduceRight.ts similarity index 100% rename from .internal/arrayReduceRight.js rename to src/.internal/arrayReduceRight.ts diff --git a/.internal/asciiSize.js b/src/.internal/asciiSize.ts similarity index 100% rename from .internal/asciiSize.js rename to src/.internal/asciiSize.ts diff --git a/.internal/asciiToArray.js b/src/.internal/asciiToArray.ts similarity index 100% rename from .internal/asciiToArray.js rename to src/.internal/asciiToArray.ts diff --git a/.internal/assignMergeValue.js b/src/.internal/assignMergeValue.ts similarity index 100% rename from .internal/assignMergeValue.js rename to src/.internal/assignMergeValue.ts diff --git a/.internal/assignValue.js b/src/.internal/assignValue.ts similarity index 100% rename from .internal/assignValue.js rename to src/.internal/assignValue.ts diff --git a/.internal/assocIndexOf.js b/src/.internal/assocIndexOf.ts similarity index 100% rename from .internal/assocIndexOf.js rename to src/.internal/assocIndexOf.ts diff --git a/src/.internal/baseAssignValue.ts b/src/.internal/baseAssignValue.ts new file mode 100644 index 0000000000..43eeed4fd7 --- /dev/null +++ b/src/.internal/baseAssignValue.ts @@ -0,0 +1,23 @@ +/** + * The base implementation of `assignValue` and `assignMergeValue` without + * value checks. + * + * @private + * @param {Object} object The object to modify. + * @param {string} key The key of the property to assign. + * @param {*} value The value to assign. + */ +function baseAssignValue(object, key, value) { + if (key === '__proto__') { + Object.defineProperty(object, key, { + 'configurable': true, + 'enumerable': true, + 'value': value, + 'writable': true + }) + } else { + object[key] = value + } +} + +export default baseAssignValue diff --git a/.internal/baseAt.js b/src/.internal/baseAt.ts similarity index 100% rename from .internal/baseAt.js rename to src/.internal/baseAt.ts diff --git a/src/.internal/baseClone.ts b/src/.internal/baseClone.ts new file mode 100644 index 0000000000..9b8f773f77 --- /dev/null +++ b/src/.internal/baseClone.ts @@ -0,0 +1,241 @@ +import Stack from './Stack.js' +import arrayEach from './arrayEach.js' +import assignValue from './assignValue.js' +import cloneBuffer from './cloneBuffer.js' +import copyArray from './copyArray.js' +import copyObject from './copyObject.js' +import cloneArrayBuffer from './cloneArrayBuffer.js' +import cloneDataView from './cloneDataView.js' +import cloneRegExp from './cloneRegExp.js' +import cloneSymbol from './cloneSymbol.js' +import cloneTypedArray from './cloneTypedArray.js' +import copySymbols from './copySymbols.js' +import copySymbolsIn from './copySymbolsIn.js' +import getAllKeys from './getAllKeys.js' +import getAllKeysIn from './getAllKeysIn.js' +import getTag from './getTag.js' +import initCloneObject from './initCloneObject.js' +import isBuffer from '../isBuffer.js' +import isObject from '../isObject.js' +import isTypedArray from '../isTypedArray.js' +import keys from '../keys.js' +import keysIn from '../keysIn.js' + +/** Used to compose bitmasks for cloning. */ +const CLONE_DEEP_FLAG = 1 +const CLONE_FLAT_FLAG = 2 +const CLONE_SYMBOLS_FLAG = 4 + +/** `Object#toString` result references. */ +const argsTag = '[object Arguments]' +const arrayTag = '[object Array]' +const boolTag = '[object Boolean]' +const dateTag = '[object Date]' +const errorTag = '[object Error]' +const mapTag = '[object Map]' +const numberTag = '[object Number]' +const objectTag = '[object Object]' +const regexpTag = '[object RegExp]' +const setTag = '[object Set]' +const stringTag = '[object String]' +const symbolTag = '[object Symbol]' +const weakMapTag = '[object WeakMap]' + +const arrayBufferTag = '[object ArrayBuffer]' +const dataViewTag = '[object DataView]' +const float32Tag = '[object Float32Array]' +const float64Tag = '[object Float64Array]' +const int8Tag = '[object Int8Array]' +const int16Tag = '[object Int16Array]' +const int32Tag = '[object Int32Array]' +const uint8Tag = '[object Uint8Array]' +const uint8ClampedTag = '[object Uint8ClampedArray]' +const uint16Tag = '[object Uint16Array]' +const uint32Tag = '[object Uint32Array]' + +/** Used to identify `toStringTag` values supported by `clone`. */ +const cloneableTags = {} +cloneableTags[argsTag] = cloneableTags[arrayTag] = +cloneableTags[arrayBufferTag] = cloneableTags[dataViewTag] = +cloneableTags[boolTag] = cloneableTags[dateTag] = +cloneableTags[float32Tag] = cloneableTags[float64Tag] = +cloneableTags[int8Tag] = cloneableTags[int16Tag] = +cloneableTags[int32Tag] = cloneableTags[mapTag] = +cloneableTags[numberTag] = cloneableTags[objectTag] = +cloneableTags[regexpTag] = cloneableTags[setTag] = +cloneableTags[stringTag] = cloneableTags[symbolTag] = +cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = +cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true +cloneableTags[errorTag] = cloneableTags[weakMapTag] = false + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty + +/** + * Initializes an object clone based on its `toStringTag`. + * + * **Note:** This function only supports cloning values with tags of + * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`. + * + * @private + * @param {Object} object The object to clone. + * @param {string} tag The `toStringTag` of the object to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Object} Returns the initialized clone. + */ +function initCloneByTag(object, tag, isDeep) { + const Ctor = object.constructor + switch (tag) { + case arrayBufferTag: + return cloneArrayBuffer(object) + + case boolTag: + case dateTag: + return new Ctor(+object) + + case dataViewTag: + return cloneDataView(object, isDeep) + + case float32Tag: case float64Tag: + case int8Tag: case int16Tag: case int32Tag: + case uint8Tag: case uint8ClampedTag: case uint16Tag: case uint32Tag: + return cloneTypedArray(object, isDeep) + + case mapTag: + return new Ctor + + case numberTag: + case stringTag: + return new Ctor(object) + + case regexpTag: + return cloneRegExp(object) + + case setTag: + return new Ctor + + case symbolTag: + return cloneSymbol(object) + } +} + +/** + * Initializes an array clone. + * + * @private + * @param {Array} array The array to clone. + * @returns {Array} Returns the initialized clone. + */ +function initCloneArray(array) { + const { length } = array + const result = new array.constructor(length) + + // Add properties assigned by `RegExp#exec`. + if (length && typeof array[0] === 'string' && hasOwnProperty.call(array, 'index')) { + result.index = array.index + result.input = array.input + } + return result +} + +/** + * The base implementation of `clone` and `cloneDeep` which tracks + * traversed objects. + * + * @private + * @param {*} value The value to clone. + * @param {number} bitmask The bitmask flags. + * 1 - Deep clone + * 2 - Flatten inherited properties + * 4 - Clone symbols + * @param {Function} [customizer] The function to customize cloning. + * @param {string} [key] The key of `value`. + * @param {Object} [object] The parent object of `value`. + * @param {Object} [stack] Tracks traversed objects and their clone counterparts. + * @returns {*} Returns the cloned value. + */ +function baseClone(value, bitmask, customizer, key, object, stack) { + let result + const isDeep = bitmask & CLONE_DEEP_FLAG + const isFlat = bitmask & CLONE_FLAT_FLAG + const isFull = bitmask & CLONE_SYMBOLS_FLAG + + if (customizer) { + result = object ? customizer(value, key, object, stack) : customizer(value) + } + if (result !== undefined) { + return result + } + if (!isObject(value)) { + return value + } + const isArr = Array.isArray(value) + const tag = getTag(value) + if (isArr) { + result = initCloneArray(value) + if (!isDeep) { + return copyArray(value, result) + } + } else { + const isFunc = typeof value === 'function' + + if (isBuffer(value)) { + return cloneBuffer(value, isDeep) + } + if (tag === objectTag || tag === argsTag || (isFunc && !object)) { + result = (isFlat || isFunc) ? {} : initCloneObject(value) + if (!isDeep) { + return isFlat + ? copySymbolsIn(value, copyObject(value, keysIn(value), result)) + : copySymbols(value, Object.assign(result, value)) + } + } else { + if (isFunc || !cloneableTags[tag]) { + return object ? value : {} + } + result = initCloneByTag(value, tag, isDeep) + } + } + // Check for circular references and return its corresponding clone. + stack || (stack = new Stack) + const stacked = stack.get(value) + if (stacked) { + return stacked + } + stack.set(value, result) + + if (tag === mapTag) { + value.forEach((subValue, key) => { + result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack)) + }) + return result + } + + if (tag === setTag) { + value.forEach((subValue) => { + result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack)) + }) + return result + } + + if (isTypedArray(value)) { + return result + } + + const keysFunc = isFull + ? (isFlat ? getAllKeysIn : getAllKeys) + : (isFlat ? keysIn : keys) + + const props = isArr ? undefined : keysFunc(value) + arrayEach(props || value, (subValue, key) => { + if (props) { + key = subValue + subValue = value[key] + } + // Recursively populate clone (susceptible to call stack limits). + assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack)) + }) + return result +} + +export default baseClone diff --git a/.internal/baseConforms.js b/src/.internal/baseConforms.ts similarity index 100% rename from .internal/baseConforms.js rename to src/.internal/baseConforms.ts diff --git a/.internal/baseConformsTo.js b/src/.internal/baseConformsTo.ts similarity index 100% rename from .internal/baseConformsTo.js rename to src/.internal/baseConformsTo.ts diff --git a/.internal/baseDifference.js b/src/.internal/baseDifference.ts similarity index 100% rename from .internal/baseDifference.js rename to src/.internal/baseDifference.ts diff --git a/.internal/baseEach.js b/src/.internal/baseEach.ts similarity index 100% rename from .internal/baseEach.js rename to src/.internal/baseEach.ts diff --git a/.internal/baseEachRight.js b/src/.internal/baseEachRight.ts similarity index 100% rename from .internal/baseEachRight.js rename to src/.internal/baseEachRight.ts diff --git a/.internal/baseFindIndex.js b/src/.internal/baseFindIndex.ts similarity index 100% rename from .internal/baseFindIndex.js rename to src/.internal/baseFindIndex.ts diff --git a/.internal/baseFindKey.js b/src/.internal/baseFindKey.ts similarity index 100% rename from .internal/baseFindKey.js rename to src/.internal/baseFindKey.ts diff --git a/.internal/baseFlatten.js b/src/.internal/baseFlatten.ts similarity index 100% rename from .internal/baseFlatten.js rename to src/.internal/baseFlatten.ts diff --git a/.internal/baseFor.js b/src/.internal/baseFor.ts similarity index 100% rename from .internal/baseFor.js rename to src/.internal/baseFor.ts diff --git a/.internal/baseForOwn.js b/src/.internal/baseForOwn.ts similarity index 100% rename from .internal/baseForOwn.js rename to src/.internal/baseForOwn.ts diff --git a/.internal/baseForOwnRight.js b/src/.internal/baseForOwnRight.ts similarity index 100% rename from .internal/baseForOwnRight.js rename to src/.internal/baseForOwnRight.ts diff --git a/.internal/baseForRight.js b/src/.internal/baseForRight.ts similarity index 100% rename from .internal/baseForRight.js rename to src/.internal/baseForRight.ts diff --git a/src/.internal/baseGet.ts b/src/.internal/baseGet.ts new file mode 100644 index 0000000000..ebbeff64a8 --- /dev/null +++ b/src/.internal/baseGet.ts @@ -0,0 +1,24 @@ +import castPath from './castPath.js' +import toKey from './toKey.js' + +/** + * The base implementation of `get` without support for default values. + * + * @private + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @returns {*} Returns the resolved value. + */ +function baseGet(object, path) { + path = castPath(path, object) + + let index = 0 + const length = path.length + + while (object != null && index < length) { + object = object[toKey(path[index++])] + } + return (index && index === length) ? object : undefined +} + +export default baseGet diff --git a/.internal/baseInRange.js b/src/.internal/baseInRange.ts similarity index 100% rename from .internal/baseInRange.js rename to src/.internal/baseInRange.ts diff --git a/.internal/baseIndexOf.js b/src/.internal/baseIndexOf.ts similarity index 100% rename from .internal/baseIndexOf.js rename to src/.internal/baseIndexOf.ts diff --git a/.internal/baseIndexOfWith.js b/src/.internal/baseIndexOfWith.ts similarity index 100% rename from .internal/baseIndexOfWith.js rename to src/.internal/baseIndexOfWith.ts diff --git a/.internal/baseIntersection.js b/src/.internal/baseIntersection.ts similarity index 100% rename from .internal/baseIntersection.js rename to src/.internal/baseIntersection.ts diff --git a/.internal/baseIsEqual.js b/src/.internal/baseIsEqual.ts similarity index 100% rename from .internal/baseIsEqual.js rename to src/.internal/baseIsEqual.ts diff --git a/src/.internal/baseIsEqualDeep.ts b/src/.internal/baseIsEqualDeep.ts new file mode 100644 index 0000000000..4c1fe2a9dc --- /dev/null +++ b/src/.internal/baseIsEqualDeep.ts @@ -0,0 +1,79 @@ +import Stack from './Stack.js' +import equalArrays from './equalArrays.js' +import equalByTag from './equalByTag.js' +import equalObjects from './equalObjects.js' +import getTag from './getTag.js' +import isBuffer from '../isBuffer.js' +import isTypedArray from '../isTypedArray.js' + +/** Used to compose bitmasks for value comparisons. */ +const COMPARE_PARTIAL_FLAG = 1 + +/** `Object#toString` result references. */ +const argsTag = '[object Arguments]' +const arrayTag = '[object Array]' +const objectTag = '[object Object]' + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty + +/** + * A specialized version of `baseIsEqual` for arrays and objects which performs + * deep comparisons and tracks traversed objects enabling objects with circular + * references to be compared. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} [stack] Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ +function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) { + let objIsArr = Array.isArray(object) + const othIsArr = Array.isArray(other) + let objTag = objIsArr ? arrayTag : getTag(object) + let othTag = othIsArr ? arrayTag : getTag(other) + + objTag = objTag === argsTag ? objectTag : objTag + othTag = othTag === argsTag ? objectTag : othTag + + let objIsObj = objTag === objectTag + const othIsObj = othTag === objectTag + const isSameTag = objTag === othTag + + if (isSameTag && isBuffer(object)) { + if (!isBuffer(other)) { + return false + } + objIsArr = true + objIsObj = false + } + if (isSameTag && !objIsObj) { + stack || (stack = new Stack) + return (objIsArr || isTypedArray(object)) + ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) + : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack) + } + if (!(bitmask & COMPARE_PARTIAL_FLAG)) { + const objIsWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__') + const othIsWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__') + + if (objIsWrapped || othIsWrapped) { + const objUnwrapped = objIsWrapped ? object.value() : object + const othUnwrapped = othIsWrapped ? other.value() : other + + stack || (stack = new Stack) + return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack) + } + } + if (!isSameTag) { + return false + } + stack || (stack = new Stack) + return equalObjects(object, other, bitmask, customizer, equalFunc, stack) +} + +export default baseIsEqualDeep diff --git a/.internal/baseIsMatch.js b/src/.internal/baseIsMatch.ts similarity index 100% rename from .internal/baseIsMatch.js rename to src/.internal/baseIsMatch.ts diff --git a/.internal/baseIsNaN.js b/src/.internal/baseIsNaN.ts similarity index 100% rename from .internal/baseIsNaN.js rename to src/.internal/baseIsNaN.ts diff --git a/.internal/baseMatches.js b/src/.internal/baseMatches.ts similarity index 100% rename from .internal/baseMatches.js rename to src/.internal/baseMatches.ts diff --git a/.internal/baseMatchesProperty.js b/src/.internal/baseMatchesProperty.ts similarity index 100% rename from .internal/baseMatchesProperty.js rename to src/.internal/baseMatchesProperty.ts diff --git a/.internal/baseMerge.js b/src/.internal/baseMerge.ts similarity index 100% rename from .internal/baseMerge.js rename to src/.internal/baseMerge.ts diff --git a/.internal/baseMergeDeep.js b/src/.internal/baseMergeDeep.ts similarity index 100% rename from .internal/baseMergeDeep.js rename to src/.internal/baseMergeDeep.ts diff --git a/.internal/baseOrderBy.js b/src/.internal/baseOrderBy.ts similarity index 100% rename from .internal/baseOrderBy.js rename to src/.internal/baseOrderBy.ts diff --git a/.internal/basePick.js b/src/.internal/basePick.ts similarity index 100% rename from .internal/basePick.js rename to src/.internal/basePick.ts diff --git a/.internal/basePickBy.js b/src/.internal/basePickBy.ts similarity index 100% rename from .internal/basePickBy.js rename to src/.internal/basePickBy.ts diff --git a/.internal/baseProperty.js b/src/.internal/baseProperty.ts similarity index 100% rename from .internal/baseProperty.js rename to src/.internal/baseProperty.ts diff --git a/.internal/basePropertyDeep.js b/src/.internal/basePropertyDeep.ts similarity index 100% rename from .internal/basePropertyDeep.js rename to src/.internal/basePropertyDeep.ts diff --git a/.internal/basePropertyOf.js b/src/.internal/basePropertyOf.ts similarity index 100% rename from .internal/basePropertyOf.js rename to src/.internal/basePropertyOf.ts diff --git a/.internal/basePullAll.js b/src/.internal/basePullAll.ts similarity index 100% rename from .internal/basePullAll.js rename to src/.internal/basePullAll.ts diff --git a/.internal/basePullAt.js b/src/.internal/basePullAt.ts similarity index 100% rename from .internal/basePullAt.js rename to src/.internal/basePullAt.ts diff --git a/.internal/baseRange.js b/src/.internal/baseRange.ts similarity index 100% rename from .internal/baseRange.js rename to src/.internal/baseRange.ts diff --git a/.internal/baseReduce.js b/src/.internal/baseReduce.ts similarity index 100% rename from .internal/baseReduce.js rename to src/.internal/baseReduce.ts diff --git a/src/.internal/baseSet.ts b/src/.internal/baseSet.ts new file mode 100644 index 0000000000..a1de66f346 --- /dev/null +++ b/src/.internal/baseSet.ts @@ -0,0 +1,48 @@ +import assignValue from './assignValue.js' +import castPath from './castPath.js' +import isIndex from './isIndex.js' +import isObject from '../isObject.js' +import toKey from './toKey.js' + +/** + * The base implementation of `set`. + * + * @private + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize path creation. + * @returns {Object} Returns `object`. + */ +function baseSet(object, path, value, customizer) { + if (!isObject(object)) { + return object + } + path = castPath(path, object) + + const length = path.length + const lastIndex = length - 1 + + let index = -1 + let nested = object + + while (nested != null && ++index < length) { + const key = toKey(path[index]) + let newValue = value + + if (index !== lastIndex) { + const objValue = nested[key] + newValue = customizer ? customizer(objValue, key, nested) : undefined + if (newValue === undefined) { + newValue = isObject(objValue) + ? objValue + : (isIndex(path[index + 1]) ? [] : {}) + } + } + assignValue(nested, key, newValue) + nested = nested[key] + } + return object +} + +export default baseSet diff --git a/.internal/baseSortBy.js b/src/.internal/baseSortBy.ts similarity index 100% rename from .internal/baseSortBy.js rename to src/.internal/baseSortBy.ts diff --git a/.internal/baseSortedIndex.js b/src/.internal/baseSortedIndex.ts similarity index 100% rename from .internal/baseSortedIndex.js rename to src/.internal/baseSortedIndex.ts diff --git a/src/.internal/baseSortedIndexBy.ts b/src/.internal/baseSortedIndexBy.ts new file mode 100644 index 0000000000..98be104a14 --- /dev/null +++ b/src/.internal/baseSortedIndexBy.ts @@ -0,0 +1,65 @@ +import isSymbol from '../isSymbol.js' + +/** Used as references for the maximum length and index of an array. */ +const MAX_ARRAY_LENGTH = 4294967295 +const MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1 + +/** + * The base implementation of `sortedIndexBy` and `sortedLastIndexBy` + * which invokes `iteratee` for `value` and each element of `array` to compute + * their sort ranking. The iteratee is invoked with one argument (value). + * + * @private + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @param {boolean} [retHighest] Specify returning the highest qualified index. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + */ +function baseSortedIndexBy(array, value, iteratee, retHighest) { + let low = 0 + let high = array == null ? 0 : array.length + if (high === 0) { + return 0 + } + + value = iteratee(value) + + const valIsNaN = value !== value + const valIsNull = value === null + const valIsSymbol = isSymbol(value) + const valIsUndefined = value === undefined + + while (low < high) { + let setLow + const mid = Math.floor((low + high) / 2) + const computed = iteratee(array[mid]) + const othIsDefined = computed !== undefined + const othIsNull = computed === null + const othIsReflexive = computed === computed + const othIsSymbol = isSymbol(computed) + + if (valIsNaN) { + setLow = retHighest || othIsReflexive + } else if (valIsUndefined) { + setLow = othIsReflexive && (retHighest || othIsDefined) + } else if (valIsNull) { + setLow = othIsReflexive && othIsDefined && (retHighest || !othIsNull) + } else if (valIsSymbol) { + setLow = othIsReflexive && othIsDefined && !othIsNull && (retHighest || !othIsSymbol) + } else if (othIsNull || othIsSymbol) { + setLow = false + } else { + setLow = retHighest ? (computed <= value) : (computed < value) + } + if (setLow) { + low = mid + 1 + } else { + high = mid + } + } + return Math.min(high, MAX_ARRAY_INDEX) +} + +export default baseSortedIndexBy diff --git a/.internal/baseSortedUniq.js b/src/.internal/baseSortedUniq.ts similarity index 100% rename from .internal/baseSortedUniq.js rename to src/.internal/baseSortedUniq.ts diff --git a/.internal/baseSum.js b/src/.internal/baseSum.ts similarity index 100% rename from .internal/baseSum.js rename to src/.internal/baseSum.ts diff --git a/.internal/baseToNumber.js b/src/.internal/baseToNumber.ts similarity index 100% rename from .internal/baseToNumber.js rename to src/.internal/baseToNumber.ts diff --git a/.internal/baseToString.js b/src/.internal/baseToString.ts similarity index 100% rename from .internal/baseToString.js rename to src/.internal/baseToString.ts diff --git a/.internal/baseUniq.js b/src/.internal/baseUniq.ts similarity index 100% rename from .internal/baseUniq.js rename to src/.internal/baseUniq.ts diff --git a/.internal/baseUnset.js b/src/.internal/baseUnset.ts similarity index 100% rename from .internal/baseUnset.js rename to src/.internal/baseUnset.ts diff --git a/.internal/baseUpdate.js b/src/.internal/baseUpdate.ts similarity index 100% rename from .internal/baseUpdate.js rename to src/.internal/baseUpdate.ts diff --git a/.internal/baseValues.js b/src/.internal/baseValues.ts similarity index 100% rename from .internal/baseValues.js rename to src/.internal/baseValues.ts diff --git a/.internal/baseWhile.js b/src/.internal/baseWhile.ts similarity index 100% rename from .internal/baseWhile.js rename to src/.internal/baseWhile.ts diff --git a/src/.internal/baseXor.ts b/src/.internal/baseXor.ts new file mode 100644 index 0000000000..6d5f1b3a73 --- /dev/null +++ b/src/.internal/baseXor.ts @@ -0,0 +1,36 @@ +import baseDifference from './baseDifference.js' +import baseFlatten from './baseFlatten.js' +import baseUniq from './baseUniq.js' + +/** + * The base implementation of methods like `xor` which accepts an array of + * arrays to inspect. + * + * @private + * @param {Array} arrays The arrays to inspect. + * @param {Function} [iteratee] The iteratee invoked per element. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of values. + */ +function baseXor(arrays, iteratee, comparator) { + const length = arrays.length + if (length < 2) { + return length ? baseUniq(arrays[0]) : [] + } + let index = -1 + const result = new Array(length) + + while (++index < length) { + const array = arrays[index] + let othIndex = -1 + + while (++othIndex < length) { + if (othIndex !== index) { + result[index] = baseDifference(result[index] || array, arrays[othIndex], iteratee, comparator) + } + } + } + return baseUniq(baseFlatten(result, 1), iteratee, comparator) +} + +export default baseXor diff --git a/.internal/baseZipObject.js b/src/.internal/baseZipObject.ts similarity index 100% rename from .internal/baseZipObject.js rename to src/.internal/baseZipObject.ts diff --git a/.internal/cacheHas.js b/src/.internal/cacheHas.ts similarity index 100% rename from .internal/cacheHas.js rename to src/.internal/cacheHas.ts diff --git a/.internal/castArrayLikeObject.js b/src/.internal/castArrayLikeObject.ts similarity index 100% rename from .internal/castArrayLikeObject.js rename to src/.internal/castArrayLikeObject.ts diff --git a/.internal/castPath.js b/src/.internal/castPath.ts similarity index 100% rename from .internal/castPath.js rename to src/.internal/castPath.ts diff --git a/.internal/castSlice.js b/src/.internal/castSlice.ts similarity index 100% rename from .internal/castSlice.js rename to src/.internal/castSlice.ts diff --git a/.internal/charsEndIndex.js b/src/.internal/charsEndIndex.ts similarity index 100% rename from .internal/charsEndIndex.js rename to src/.internal/charsEndIndex.ts diff --git a/.internal/charsStartIndex.js b/src/.internal/charsStartIndex.ts similarity index 100% rename from .internal/charsStartIndex.js rename to src/.internal/charsStartIndex.ts diff --git a/.internal/cloneArrayBuffer.js b/src/.internal/cloneArrayBuffer.ts similarity index 100% rename from .internal/cloneArrayBuffer.js rename to src/.internal/cloneArrayBuffer.ts diff --git a/src/.internal/cloneBuffer.ts b/src/.internal/cloneBuffer.ts new file mode 100644 index 0000000000..a2b97d7e22 --- /dev/null +++ b/src/.internal/cloneBuffer.ts @@ -0,0 +1,34 @@ +import root from './root.js' + +/** Detect free variable `exports`. */ +const freeExports = typeof exports === 'object' && exports !== null && !exports.nodeType && exports + +/** Detect free variable `module`. */ +const freeModule = freeExports && typeof module === 'object' && module !== null && !module.nodeType && module + +/** Detect the popular CommonJS extension `module.exports`. */ +const moduleExports = freeModule && freeModule.exports === freeExports + +/** Built-in value references. */ +const Buffer = moduleExports ? root.Buffer : undefined, allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined + +/** + * Creates a clone of `buffer`. + * + * @private + * @param {Buffer} buffer The buffer to clone. + * @param {boolean} [isDeep] Specify a deep clone. + * @returns {Buffer} Returns the cloned buffer. + */ +function cloneBuffer(buffer, isDeep) { + if (isDeep) { + return buffer.slice() + } + const length = buffer.length + const result = allocUnsafe ? allocUnsafe(length) : buffer.constructor.alloc(length) + + buffer.copy(result) + return result +} + +export default cloneBuffer diff --git a/.internal/cloneDataView.js b/src/.internal/cloneDataView.ts similarity index 100% rename from .internal/cloneDataView.js rename to src/.internal/cloneDataView.ts diff --git a/.internal/cloneRegExp.js b/src/.internal/cloneRegExp.ts similarity index 100% rename from .internal/cloneRegExp.js rename to src/.internal/cloneRegExp.ts diff --git a/.internal/cloneSymbol.js b/src/.internal/cloneSymbol.ts similarity index 100% rename from .internal/cloneSymbol.js rename to src/.internal/cloneSymbol.ts diff --git a/.internal/cloneTypedArray.js b/src/.internal/cloneTypedArray.ts similarity index 100% rename from .internal/cloneTypedArray.js rename to src/.internal/cloneTypedArray.ts diff --git a/.internal/compareAscending.js b/src/.internal/compareAscending.ts similarity index 100% rename from .internal/compareAscending.js rename to src/.internal/compareAscending.ts diff --git a/src/.internal/compareMultiple.ts b/src/.internal/compareMultiple.ts new file mode 100644 index 0000000000..18e8444d92 --- /dev/null +++ b/src/.internal/compareMultiple.ts @@ -0,0 +1,45 @@ +import compareAscending from './compareAscending.js' + +/** + * Used by `orderBy` to compare multiple properties of a value to another + * and stable sort them. + * + * If `orders` is unspecified, all values are sorted in ascending order. Otherwise, + * specify an order of "desc" for descending or "asc" for ascending sort order + * of corresponding values. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {(string|function)[]} orders The order to sort by for each property. + * @returns {number} Returns the sort order indicator for `object`. + */ +function compareMultiple(object, other, orders) { + let index = -1 + const objCriteria = object.criteria + const othCriteria = other.criteria + const length = objCriteria.length + const ordersLength = orders.length + + while (++index < length) { + const order = index < ordersLength ? orders[index] : null + const cmpFn = (order && typeof order === 'function') ? order: compareAscending + const result = cmpFn(objCriteria[index], othCriteria[index]) + if (result) { + if (order && typeof order !== 'function') { + return result * (order === 'desc' ? -1 : 1) + } + return result + } + } + // Fixes an `Array#sort` bug in the JS engine embedded in Adobe applications + // that causes it, under certain circumstances, to provide the same value for + // `object` and `other`. See https://github.com/jashkenas/underscore/pull/1247 + // for more details. + // + // This also ensures a stable sort in V8 and other engines. + // See https://bugs.chromium.org/p/v8/issues/detail?id=90 for more details. + return object.index - other.index +} + +export default compareMultiple diff --git a/.internal/composeArgs.js b/src/.internal/composeArgs.ts similarity index 100% rename from .internal/composeArgs.js rename to src/.internal/composeArgs.ts diff --git a/.internal/composeArgsRight.js b/src/.internal/composeArgsRight.ts similarity index 100% rename from .internal/composeArgsRight.js rename to src/.internal/composeArgsRight.ts diff --git a/.internal/copyArray.js b/src/.internal/copyArray.ts similarity index 100% rename from .internal/copyArray.js rename to src/.internal/copyArray.ts diff --git a/.internal/copyObject.js b/src/.internal/copyObject.ts similarity index 100% rename from .internal/copyObject.js rename to src/.internal/copyObject.ts diff --git a/.internal/copySymbols.js b/src/.internal/copySymbols.ts similarity index 100% rename from .internal/copySymbols.js rename to src/.internal/copySymbols.ts diff --git a/.internal/copySymbolsIn.js b/src/.internal/copySymbolsIn.ts similarity index 100% rename from .internal/copySymbolsIn.js rename to src/.internal/copySymbolsIn.ts diff --git a/.internal/createAssigner.js b/src/.internal/createAssigner.ts similarity index 100% rename from .internal/createAssigner.js rename to src/.internal/createAssigner.ts diff --git a/.internal/createCaseFirst.js b/src/.internal/createCaseFirst.ts similarity index 100% rename from .internal/createCaseFirst.js rename to src/.internal/createCaseFirst.ts diff --git a/.internal/createMathOperation.js b/src/.internal/createMathOperation.ts similarity index 100% rename from .internal/createMathOperation.js rename to src/.internal/createMathOperation.ts diff --git a/.internal/createPadding.js b/src/.internal/createPadding.ts similarity index 100% rename from .internal/createPadding.js rename to src/.internal/createPadding.ts diff --git a/.internal/createRange.js b/src/.internal/createRange.ts similarity index 100% rename from .internal/createRange.js rename to src/.internal/createRange.ts diff --git a/.internal/createRound.js b/src/.internal/createRound.ts similarity index 100% rename from .internal/createRound.js rename to src/.internal/createRound.ts diff --git a/src/.internal/createSet.ts b/src/.internal/createSet.ts new file mode 100644 index 0000000000..e351c348d1 --- /dev/null +++ b/src/.internal/createSet.ts @@ -0,0 +1,17 @@ +import setToArray from './setToArray.js' + +/** Used as references for various `Number` constants. */ +const INFINITY = 1 / 0 + +/** + * Creates a set object of `values`. + * + * @private + * @param {Array} values The values to add to the set. + * @returns {Object} Returns the new set. + */ +const createSet = (Set && (1 / setToArray(new Set([,-0]))[1]) === INFINITY) + ? (values) => new Set(values) + : () => {} + +export default createSet diff --git a/.internal/customDefaultsMerge.js b/src/.internal/customDefaultsMerge.ts similarity index 100% rename from .internal/customDefaultsMerge.js rename to src/.internal/customDefaultsMerge.ts diff --git a/.internal/deburrLetter.js b/src/.internal/deburrLetter.ts similarity index 100% rename from .internal/deburrLetter.js rename to src/.internal/deburrLetter.ts diff --git a/src/.internal/equalArrays.ts b/src/.internal/equalArrays.ts new file mode 100644 index 0000000000..29bd0f4946 --- /dev/null +++ b/src/.internal/equalArrays.ts @@ -0,0 +1,84 @@ +import SetCache from './SetCache.js' +import some from '../some.js' +import cacheHas from './cacheHas.js' + +/** Used to compose bitmasks for value comparisons. */ +const COMPARE_PARTIAL_FLAG = 1 +const COMPARE_UNORDERED_FLAG = 2 + +/** + * A specialized version of `baseIsEqualDeep` for arrays with support for + * partial deep comparisons. + * + * @private + * @param {Array} array The array to compare. + * @param {Array} other The other array to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `array` and `other` objects. + * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`. + */ +function equalArrays(array, other, bitmask, customizer, equalFunc, stack) { + const isPartial = bitmask & COMPARE_PARTIAL_FLAG + const arrLength = array.length + const othLength = other.length + + if (arrLength !== othLength && !(isPartial && othLength > arrLength)) { + return false + } + // Assume cyclic values are equal. + const stacked = stack.get(array) + if (stacked && stack.get(other)) { + return stacked === other + } + let index = -1 + let result = true + const seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new SetCache : undefined + + stack.set(array, other) + stack.set(other, array) + + // Ignore non-index properties. + while (++index < arrLength) { + let compared + const arrValue = array[index] + const othValue = other[index] + + if (customizer) { + compared = isPartial + ? customizer(othValue, arrValue, index, other, array, stack) + : customizer(arrValue, othValue, index, array, other, stack) + } + if (compared !== undefined) { + if (compared) { + continue + } + result = false + break + } + // Recursively compare arrays (susceptible to call stack limits). + if (seen) { + if (!some(other, (othValue, othIndex) => { + if (!cacheHas(seen, othIndex) && + (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) { + return seen.push(othIndex) + } + })) { + result = false + break + } + } else if (!( + arrValue === othValue || + equalFunc(arrValue, othValue, bitmask, customizer, stack) + )) { + result = false + break + } + } + stack['delete'](array) + stack['delete'](other) + return result +} + +export default equalArrays diff --git a/src/.internal/equalByTag.ts b/src/.internal/equalByTag.ts new file mode 100644 index 0000000000..e909f47746 --- /dev/null +++ b/src/.internal/equalByTag.ts @@ -0,0 +1,109 @@ +import eq from '../eq.js' +import equalArrays from './equalArrays.js' +import mapToArray from './mapToArray.js' +import setToArray from './setToArray.js' + +/** Used to compose bitmasks for value comparisons. */ +const COMPARE_PARTIAL_FLAG = 1 +const COMPARE_UNORDERED_FLAG = 2 + +/** `Object#toString` result references. */ +const boolTag = '[object Boolean]' +const dateTag = '[object Date]' +const errorTag = '[object Error]' +const mapTag = '[object Map]' +const numberTag = '[object Number]' +const regexpTag = '[object RegExp]' +const setTag = '[object Set]' +const stringTag = '[object String]' +const symbolTag = '[object Symbol]' + +const arrayBufferTag = '[object ArrayBuffer]' +const dataViewTag = '[object DataView]' + +/** Used to convert symbols to primitives and strings. */ +const symbolValueOf = Symbol.prototype.valueOf + +/** + * A specialized version of `baseIsEqualDeep` for comparing objects of + * the same `toStringTag`. + * + * **Note:** This function only supports comparing values with tags of + * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {string} tag The `toStringTag` of the objects to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ +function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) { + switch (tag) { + case dataViewTag: + if ((object.byteLength !== other.byteLength) || + (object.byteOffset !== other.byteOffset)) { + return false + } + object = object.buffer + other = other.buffer + + case arrayBufferTag: + if ((object.byteLength !== other.byteLength) || + !equalFunc(new Uint8Array(object), new Uint8Array(other))) { + return false + } + return true + + case boolTag: + case dateTag: + case numberTag: + // Coerce booleans to `1` or `0` and dates to milliseconds. + // Invalid dates are coerced to `NaN`. + return eq(+object, +other) + + case errorTag: + return object.name === other.name && object.message === other.message + + case regexpTag: + case stringTag: + // Coerce regexes to strings and treat strings, primitives and objects, + // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring + // for more details. + return object === `${other}` + + case mapTag: + let convert = mapToArray + + case setTag: + const isPartial = bitmask & COMPARE_PARTIAL_FLAG + convert || (convert = setToArray) + + if (object.size !== other.size && !isPartial) { + return false + } + // Assume cyclic values are equal. + const stacked = stack.get(object) + if (stacked) { + return stacked === other + } + bitmask |= COMPARE_UNORDERED_FLAG + + // Recursively compare objects (susceptible to call stack limits). + stack.set(object, other) + const result = equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack) + stack['delete'](object) + return result + + case symbolTag: + if (symbolValueOf) { + return symbolValueOf.call(object) === symbolValueOf.call(other) + } + } + return false +} + +export default equalByTag diff --git a/src/.internal/equalObjects.ts b/src/.internal/equalObjects.ts new file mode 100644 index 0000000000..54e2f03280 --- /dev/null +++ b/src/.internal/equalObjects.ts @@ -0,0 +1,88 @@ +import getAllKeys from './getAllKeys.js' + +/** Used to compose bitmasks for value comparisons. */ +const COMPARE_PARTIAL_FLAG = 1 + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty + +/** + * A specialized version of `baseIsEqualDeep` for objects with support for + * partial deep comparisons. + * + * @private + * @param {Object} object The object to compare. + * @param {Object} other The other object to compare. + * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details. + * @param {Function} customizer The function to customize comparisons. + * @param {Function} equalFunc The function to determine equivalents of values. + * @param {Object} stack Tracks traversed `object` and `other` objects. + * @returns {boolean} Returns `true` if the objects are equivalent, else `false`. + */ +function equalObjects(object, other, bitmask, customizer, equalFunc, stack) { + const isPartial = bitmask & COMPARE_PARTIAL_FLAG + const objProps = getAllKeys(object) + const objLength = objProps.length + const othProps = getAllKeys(other) + const othLength = othProps.length + + if (objLength !== othLength && !isPartial) { + return false + } + let key + let index = objLength + while (index--) { + key = objProps[index] + if (!(isPartial ? key in other : hasOwnProperty.call(other, key))) { + return false + } + } + // Assume cyclic values are equal. + const stacked = stack.get(object) + if (stacked && stack.get(other)) { + return stacked === other + } + let result = true + stack.set(object, other) + stack.set(other, object) + + let compared + let skipCtor = isPartial + while (++index < objLength) { + key = objProps[index] + const objValue = object[key] + const othValue = other[key] + + if (customizer) { + compared = isPartial + ? customizer(othValue, objValue, key, other, object, stack) + : customizer(objValue, othValue, key, object, other, stack) + } + // Recursively compare objects (susceptible to call stack limits). + if (!(compared === undefined + ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack)) + : compared + )) { + result = false + break + } + skipCtor || (skipCtor = key === 'constructor') + } + if (result && !skipCtor) { + const objCtor = object.constructor + const othCtor = other.constructor + + // Non `Object` object instances with different constructors are not equal. + if (objCtor !== othCtor && + ('constructor' in object && 'constructor' in other) && + !(typeof objCtor === 'function' && objCtor instanceof objCtor && + typeof othCtor === 'function' && othCtor instanceof othCtor)) { + result = false + } + } + stack['delete'](object) + stack['delete'](other) + return result +} + +export default equalObjects diff --git a/.internal/freeGlobal.js b/src/.internal/freeGlobal.ts similarity index 100% rename from .internal/freeGlobal.js rename to src/.internal/freeGlobal.ts diff --git a/.internal/getAllKeys.js b/src/.internal/getAllKeys.ts similarity index 100% rename from .internal/getAllKeys.js rename to src/.internal/getAllKeys.ts diff --git a/.internal/getAllKeysIn.js b/src/.internal/getAllKeysIn.ts similarity index 100% rename from .internal/getAllKeysIn.js rename to src/.internal/getAllKeysIn.ts diff --git a/.internal/getHolder.js b/src/.internal/getHolder.ts similarity index 100% rename from .internal/getHolder.js rename to src/.internal/getHolder.ts diff --git a/.internal/getMatchData.js b/src/.internal/getMatchData.ts similarity index 100% rename from .internal/getMatchData.js rename to src/.internal/getMatchData.ts diff --git a/.internal/getSymbols.js b/src/.internal/getSymbols.ts similarity index 100% rename from .internal/getSymbols.js rename to src/.internal/getSymbols.ts diff --git a/.internal/getSymbolsIn.js b/src/.internal/getSymbolsIn.ts similarity index 100% rename from .internal/getSymbolsIn.js rename to src/.internal/getSymbolsIn.ts diff --git a/.internal/getTag.js b/src/.internal/getTag.ts similarity index 100% rename from .internal/getTag.js rename to src/.internal/getTag.ts diff --git a/.internal/hasUnicode.js b/src/.internal/hasUnicode.ts similarity index 100% rename from .internal/hasUnicode.js rename to src/.internal/hasUnicode.ts diff --git a/.internal/initCloneObject.js b/src/.internal/initCloneObject.ts similarity index 100% rename from .internal/initCloneObject.js rename to src/.internal/initCloneObject.ts diff --git a/.internal/isFlattenable.js b/src/.internal/isFlattenable.ts similarity index 100% rename from .internal/isFlattenable.js rename to src/.internal/isFlattenable.ts diff --git a/src/.internal/isIndex.ts b/src/.internal/isIndex.ts new file mode 100644 index 0000000000..bc3e3dc91f --- /dev/null +++ b/src/.internal/isIndex.ts @@ -0,0 +1,25 @@ +/** Used as references for various `Number` constants. */ +const MAX_SAFE_INTEGER = 9007199254740991 + +/** Used to detect unsigned integer values. */ +const reIsUint = /^(?:0|[1-9]\d*)$/ + +/** + * Checks if `value` is a valid array-like index. + * + * @private + * @param {*} value The value to check. + * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index. + * @returns {boolean} Returns `true` if `value` is a valid index, else `false`. + */ +function isIndex(value, length) { + const type = typeof value + length = length == null ? MAX_SAFE_INTEGER : length + + return !!length && + (type === 'number' || + (type !== 'symbol' && reIsUint.test(value))) && + (value > -1 && value % 1 === 0 && value < length) +} + +export default isIndex diff --git a/.internal/isIterateeCall.js b/src/.internal/isIterateeCall.ts similarity index 100% rename from .internal/isIterateeCall.js rename to src/.internal/isIterateeCall.ts diff --git a/.internal/isKey.js b/src/.internal/isKey.ts similarity index 100% rename from .internal/isKey.js rename to src/.internal/isKey.ts diff --git a/.internal/isPrototype.js b/src/.internal/isPrototype.ts similarity index 100% rename from .internal/isPrototype.js rename to src/.internal/isPrototype.ts diff --git a/.internal/isStrictComparable.js b/src/.internal/isStrictComparable.ts similarity index 100% rename from .internal/isStrictComparable.js rename to src/.internal/isStrictComparable.ts diff --git a/.internal/iteratorToArray.js b/src/.internal/iteratorToArray.ts similarity index 100% rename from .internal/iteratorToArray.js rename to src/.internal/iteratorToArray.ts diff --git a/.internal/mapToArray.js b/src/.internal/mapToArray.ts similarity index 100% rename from .internal/mapToArray.js rename to src/.internal/mapToArray.ts diff --git a/.internal/matchesStrictComparable.js b/src/.internal/matchesStrictComparable.ts similarity index 100% rename from .internal/matchesStrictComparable.js rename to src/.internal/matchesStrictComparable.ts diff --git a/.internal/memoizeCapped.js b/src/.internal/memoizeCapped.ts similarity index 100% rename from .internal/memoizeCapped.js rename to src/.internal/memoizeCapped.ts diff --git a/.internal/metaMap.js b/src/.internal/metaMap.ts similarity index 100% rename from .internal/metaMap.js rename to src/.internal/metaMap.ts diff --git a/.internal/nodeTypes.js b/src/.internal/nodeTypes.ts similarity index 100% rename from .internal/nodeTypes.js rename to src/.internal/nodeTypes.ts diff --git a/.internal/parent.js b/src/.internal/parent.ts similarity index 100% rename from .internal/parent.js rename to src/.internal/parent.ts diff --git a/.internal/reEscape.js b/src/.internal/reEscape.ts similarity index 100% rename from .internal/reEscape.js rename to src/.internal/reEscape.ts diff --git a/.internal/reEvaluate.js b/src/.internal/reEvaluate.ts similarity index 100% rename from .internal/reEvaluate.js rename to src/.internal/reEvaluate.ts diff --git a/.internal/reInterpolate.js b/src/.internal/reInterpolate.ts similarity index 100% rename from .internal/reInterpolate.js rename to src/.internal/reInterpolate.ts diff --git a/src/.internal/root.ts b/src/.internal/root.ts new file mode 100644 index 0000000000..8c57513aff --- /dev/null +++ b/src/.internal/root.ts @@ -0,0 +1,13 @@ +/* global globalThis, self */ +import freeGlobal from './freeGlobal.js' + +/** Detect free variable `globalThis` */ +const freeGlobalThis = typeof globalThis === 'object' && globalThis !== null && globalThis.Object === Object && globalThis + +/** Detect free variable `self`. */ +const freeSelf = typeof self === 'object' && self !== null && self.Object === Object && self + +/** Used as a reference to the global object. */ +const root = freeGlobalThis || freeGlobal || freeSelf || Function('return this')() + +export default root diff --git a/.internal/setToArray.js b/src/.internal/setToArray.ts similarity index 100% rename from .internal/setToArray.js rename to src/.internal/setToArray.ts diff --git a/.internal/setToPairs.js b/src/.internal/setToPairs.ts similarity index 100% rename from .internal/setToPairs.js rename to src/.internal/setToPairs.ts diff --git a/.internal/setToString.js b/src/.internal/setToString.ts similarity index 100% rename from .internal/setToString.js rename to src/.internal/setToString.ts diff --git a/.internal/strictIndexOf.js b/src/.internal/strictIndexOf.ts similarity index 100% rename from .internal/strictIndexOf.js rename to src/.internal/strictIndexOf.ts diff --git a/.internal/strictLastIndexOf.js b/src/.internal/strictLastIndexOf.ts similarity index 100% rename from .internal/strictLastIndexOf.js rename to src/.internal/strictLastIndexOf.ts diff --git a/.internal/stringSize.js b/src/.internal/stringSize.ts similarity index 100% rename from .internal/stringSize.js rename to src/.internal/stringSize.ts diff --git a/.internal/stringToArray.js b/src/.internal/stringToArray.ts similarity index 100% rename from .internal/stringToArray.js rename to src/.internal/stringToArray.ts diff --git a/.internal/stringToPath.js b/src/.internal/stringToPath.ts similarity index 100% rename from .internal/stringToPath.js rename to src/.internal/stringToPath.ts diff --git a/src/.internal/toKey.ts b/src/.internal/toKey.ts new file mode 100644 index 0000000000..db002e3a02 --- /dev/null +++ b/src/.internal/toKey.ts @@ -0,0 +1,21 @@ +import isSymbol from '../isSymbol.js' + +/** Used as references for various `Number` constants. */ +const INFINITY = 1 / 0 + +/** + * Converts `value` to a string key if it's not a string or symbol. + * + * @private + * @param {*} value The value to inspect. + * @returns {string|symbol} Returns the key. + */ +function toKey(value) { + if (typeof value === 'string' || isSymbol(value)) { + return value + } + const result = `${value}` + return (result === '0' && (1 / value) === -INFINITY) ? '-0' : result +} + +export default toKey diff --git a/.internal/unicodeSize.js b/src/.internal/unicodeSize.ts similarity index 100% rename from .internal/unicodeSize.js rename to src/.internal/unicodeSize.ts diff --git a/.internal/unicodeToArray.js b/src/.internal/unicodeToArray.ts similarity index 100% rename from .internal/unicodeToArray.js rename to src/.internal/unicodeToArray.ts diff --git a/.internal/unicodeWords.js b/src/.internal/unicodeWords.ts similarity index 100% rename from .internal/unicodeWords.js rename to src/.internal/unicodeWords.ts diff --git a/src/add.ts b/src/add.ts new file mode 100644 index 0000000000..4485c8dfaa --- /dev/null +++ b/src/add.ts @@ -0,0 +1,18 @@ +import createMathOperation from './.internal/createMathOperation.js'; + +/** + * Adds two numbers. + * + * @since 3.4.0 + * @category Math + * @param {number} augend The first number in an addition. + * @param {number} addend The second number in an addition. + * @returns {number} Returns the total. + * @example + * + * add(6, 4) + * // => 10 + */ +const add = createMathOperation((augend, addend) => augend + addend, 0); + +export default add; diff --git a/src/after.ts b/src/after.ts new file mode 100644 index 0000000000..82f7d5e2a2 --- /dev/null +++ b/src/after.ts @@ -0,0 +1,31 @@ +/** + * The opposite of `before`. This method creates a function that invokes + * `func` once it's called `n` or more times. + * + * @since 0.1.0 + * @category Function + * @param {number} n The number of calls before `func` is invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * const saves = ['profile', 'settings'] + * const done = after(saves.length, () => console.log('done saving!')) + * + * forEach(saves, type => asyncSave({ 'type': type, 'complete': done })) + * // => Logs 'done saving!' after the two async saves have completed. + */ +function after(n, func: Function) { + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + n = n || 0; + // eslint-disable-next-line consistent-return + return function (this: any, ...args: any[]) { + if (--n < 1) { + return func.apply(this, args); + } + }; +} + +export default after; diff --git a/src/at.ts b/src/at.ts new file mode 100644 index 0000000000..b3f2a8b9ef --- /dev/null +++ b/src/at.ts @@ -0,0 +1,21 @@ +import baseAt from './.internal/baseAt.js'; +import baseFlatten from './.internal/baseFlatten.js'; + +/** + * Creates an array of values corresponding to `paths` of `object`. + * + * @since 1.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Array} Returns the picked values. + * @example + * + * const object = { 'a': [{ 'b': { 'c': 3 } }, 4] } + * + * at(object, ['a[0].b.c', 'a[1]']) + * // => [3, 4] + */ +const at = (object, ...paths) => baseAt(object, baseFlatten(paths, 1)); + +export default at; diff --git a/src/attempt.ts b/src/attempt.ts new file mode 100644 index 0000000000..ab1dfe0718 --- /dev/null +++ b/src/attempt.ts @@ -0,0 +1,30 @@ +import isError from './isError.js'; + +/** + * Attempts to invoke `func`, returning either the result or the caught error + * object. Any additional arguments are provided to `func` when it's invoked. + * + * @since 3.0.0 + * @category Util + * @param {Function} func The function to attempt. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {*} Returns the `func` result or error object. + * @example + * + * // Avoid throwing errors for invalid selectors. + * const elements = attempt(selector => + * document.querySelectorAll(selector), '>_>') + * + * if (isError(elements)) { + * elements = [] + * } + */ +function attempt(func, ...args) { + try { + return func(...args); + } catch (e) { + return isError(e) ? e : new Error(e); + } +} + +export default attempt; diff --git a/src/before.ts b/src/before.ts new file mode 100644 index 0000000000..e861d8d307 --- /dev/null +++ b/src/before.ts @@ -0,0 +1,32 @@ +/** + * Creates a function that invokes `func`, with the `this` binding and arguments + * of the created function, while it's called less than `n` times. Subsequent + * calls to the created function return the result of the last `func` invocation. + * + * @since 3.0.0 + * @category Function + * @param {number} n The number of calls at which `func` is no longer invoked. + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * jQuery(element).on('click', before(5, addContactToList)) + * // => Allows adding up to 4 contacts to the list. + */ +function before(n, func) { + let result; + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + return function (...args) { + if (--n > 0) { + result = func.apply(this, args); + } + if (n <= 1) { + func = undefined; + } + return result; + }; +} + +export default before; diff --git a/src/camelCase.ts b/src/camelCase.ts new file mode 100644 index 0000000000..c653595e51 --- /dev/null +++ b/src/camelCase.ts @@ -0,0 +1,30 @@ +import upperFirst from './upperFirst.js'; +import words from './words.js'; +import toString from './toString.js'; + +/** + * Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase). + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the camel cased string. + * @see lowerCase, kebabCase, snakeCase, startCase, upperCase, upperFirst + * @example + * + * camelCase('Foo Bar') + * // => 'fooBar' + * + * camelCase('--foo-bar--') + * // => 'fooBar' + * + * camelCase('__FOO_BAR__') + * // => 'fooBar' + */ +const camelCase = (string) => + words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => { + word = word.toLowerCase(); + return result + (index ? upperFirst(word) : word); + }, ''); + +export default camelCase; diff --git a/src/capitalize.ts b/src/capitalize.ts new file mode 100644 index 0000000000..25ee906a61 --- /dev/null +++ b/src/capitalize.ts @@ -0,0 +1,19 @@ +import upperFirst from './upperFirst.js'; +import toString from './toString.js'; + +/** + * Converts the first character of `string` to upper case and the remaining + * to lower case. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to capitalize. + * @returns {string} Returns the capitalized string. + * @example + * + * capitalize('FRED') + * // => 'Fred' + */ +const capitalize = (string) => upperFirst(toString(string).toLowerCase()); + +export default capitalize; diff --git a/src/castArray.ts b/src/castArray.ts new file mode 100644 index 0000000000..0378acd09b --- /dev/null +++ b/src/castArray.ts @@ -0,0 +1,40 @@ +/** + * Casts `value` as an array if it's not one. + * + * @since 4.4.0 + * @category Lang + * @param {*} value The value to inspect. + * @returns {Array} Returns the cast array. + * @example + * + * castArray(1) + * // => [1] + * + * castArray({ 'a': 1 }) + * // => [{ 'a': 1 }] + * + * castArray('abc') + * // => ['abc'] + * + * castArray(null) + * // => [null] + * + * castArray(undefined) + * // => [undefined] + * + * castArray() + * // => [] + * + * const array = [1, 2, 3] + * console.log(castArray(array) === array) + * // => true + */ +function castArray(...args) { + if (!args.length) { + return []; + } + const value = args[0]; + return Array.isArray(value) ? value : [value]; +} + +export default castArray; diff --git a/src/ceil.ts b/src/ceil.ts new file mode 100644 index 0000000000..6a27e44422 --- /dev/null +++ b/src/ceil.ts @@ -0,0 +1,24 @@ +import createRound from './.internal/createRound.js'; + +/** + * Computes `number` rounded up to `precision`. (Round up: the smallest integer greater than or equal to a given number.) + * + * @since 3.10.0 + * @category Math + * @param {number} number The number to round up. + * @param {number} [precision=0] The precision to round up to. + * @returns {number} Returns the rounded up number. + * @example + * + * ceil(4.006) + * // => 5 + * + * ceil(6.004, 2) + * // => 6.01 + * + * ceil(6040, -2) + * // => 6100 + */ +const ceil = createRound('ceil'); + +export default ceil; diff --git a/src/chunk.ts b/src/chunk.ts new file mode 100644 index 0000000000..91ecfdd741 --- /dev/null +++ b/src/chunk.ts @@ -0,0 +1,38 @@ +import slice from './slice.js'; +import toInteger from './toInteger.js'; + +/** + * Creates an array of elements split into groups the length of `size`. + * If `array` can't be split evenly, the final chunk will be the remaining + * elements. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to process. + * @param {number} [size=1] The length of each chunk + * @returns {Array} Returns the new array of chunks. + * @example + * + * chunk(['a', 'b', 'c', 'd'], 2) + * // => [['a', 'b'], ['c', 'd']] + * + * chunk(['a', 'b', 'c', 'd'], 3) + * // => [['a', 'b', 'c'], ['d']] + */ +function chunk(array, size = 1) { + size = Math.max(toInteger(size), 0); + const length = array == null ? 0 : array.length; + if (!length || size < 1) { + return []; + } + let index = 0; + let resIndex = 0; + const result = new Array(Math.ceil(length / size)); + + while (index < length) { + result[resIndex++] = slice(array, index, (index += size)); + } + return result; +} + +export default chunk; diff --git a/src/clamp.ts b/src/clamp.ts new file mode 100644 index 0000000000..6aa013abcc --- /dev/null +++ b/src/clamp.ts @@ -0,0 +1,31 @@ +/** + * Clamps `number` within the inclusive `lower` and `upper` bounds. + * + * @since 4.0.0 + * @category Number + * @param {number} number The number to clamp. + * @param {number} lower The lower bound. + * @param {number} upper The upper bound. + * @returns {number} Returns the clamped number. + * @example + * + * clamp(-10, -5, 5) + * // => -5 + * + * clamp(10, -5, 5) + * // => 5 + */ +function clamp(number: number, lower: number, upper: number) { + number = +number; + lower = +lower; + upper = +upper; + lower = lower === lower ? lower : 0; + upper = upper === upper ? upper : 0; + if (number === number) { + number = number <= upper ? number : upper; + number = number >= lower ? number : lower; + } + return number; +} + +export default clamp; diff --git a/src/clone.ts b/src/clone.ts new file mode 100644 index 0000000000..7fce8feb66 --- /dev/null +++ b/src/clone.ts @@ -0,0 +1,35 @@ +import baseClone from './.internal/baseClone.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_SYMBOLS_FLAG = 4; + +/** + * Creates a shallow clone of `value`. + * + * **Note:** This method is loosely based on the + * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm) + * and supports cloning arrays, array buffers, booleans, date objects, maps, + * numbers, `Object` objects, regexes, sets, strings, symbols, and typed + * arrays. The own enumerable properties of `arguments` objects are cloned + * as plain objects. Object inheritance is preserved. An empty object is + * returned for uncloneable values such as error objects, functions, DOM nodes, + * and WeakMaps. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to clone. + * @returns {*} Returns the cloned value. + * @see cloneDeep + * @example + * + * const objects = [{ 'a': 1 }, { 'b': 2 }] + * + * const shallow = clone(objects) + * console.log(shallow[0] === objects[0]) + * // => true + */ +function clone(value) { + return baseClone(value, CLONE_SYMBOLS_FLAG); +} + +export default clone; diff --git a/src/cloneDeep.ts b/src/cloneDeep.ts new file mode 100644 index 0000000000..7cf3193b67 --- /dev/null +++ b/src/cloneDeep.ts @@ -0,0 +1,28 @@ +import baseClone from './.internal/baseClone.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_DEEP_FLAG = 1; +const CLONE_SYMBOLS_FLAG = 4; + +/** + * This method is like `clone` except that it recursively clones `value`. + * Object inheritance is preserved. + * + * @since 1.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @returns {*} Returns the deep cloned value. + * @see clone + * @example + * + * const objects = [{ 'a': 1 }, { 'b': 2 }] + * + * const deep = cloneDeep(objects) + * console.log(deep[0] === objects[0]) + * // => false + */ +function cloneDeep(value) { + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG); +} + +export default cloneDeep; diff --git a/src/cloneDeepWith.ts b/src/cloneDeepWith.ts new file mode 100644 index 0000000000..b206a9c854 --- /dev/null +++ b/src/cloneDeepWith.ts @@ -0,0 +1,40 @@ +import baseClone from './.internal/baseClone.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_DEEP_FLAG = 1; +const CLONE_SYMBOLS_FLAG = 4; + +/** + * This method is like `cloneWith` except that it recursively clones `value`. + * The customizer is invoked with up to four arguments + * (value [, index|key, object, stack]). + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to recursively clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the deep cloned value. + * @see cloneWith + * @example + * + * function customizer(value) { + * if (isElement(value)) { + * return value.cloneNode(true) + * } + * } + * + * const el = cloneDeepWith(document.body, customizer) + * + * console.log(el === document.body) + * // => false + * console.log(el.nodeName) + * // => 'BODY' + * console.log(el.childNodes.length) + * // => 20 + */ +function cloneDeepWith(value, customizer) { + customizer = typeof customizer === 'function' ? customizer : undefined; + return baseClone(value, CLONE_DEEP_FLAG | CLONE_SYMBOLS_FLAG, customizer); +} + +export default cloneDeepWith; diff --git a/src/cloneWith.ts b/src/cloneWith.ts new file mode 100644 index 0000000000..2661bcfe0b --- /dev/null +++ b/src/cloneWith.ts @@ -0,0 +1,40 @@ +import baseClone from './.internal/baseClone.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_SYMBOLS_FLAG = 4; + +/** + * This method is like `clone` except that it accepts `customizer` which + * is invoked to produce the cloned value. If `customizer` returns `undefined`, + * cloning is handled by the method instead. The `customizer` is invoked with + * one argument (value). + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to clone. + * @param {Function} [customizer] The function to customize cloning. + * @returns {*} Returns the cloned value. + * @see cloneDeepWith + * @example + * + * function customizer(value) { + * if (isElement(value)) { + * return value.cloneNode(false) + * } + * } + * + * const el = cloneWith(document.body, customizer) + * + * console.log(el === document.body) + * // => false + * console.log(el.nodeName) + * // => 'BODY' + * console.log(el.childNodes.length) + * // => 0 + */ +function cloneWith(value, customizer) { + customizer = typeof customizer === 'function' ? customizer : undefined; + return baseClone(value, CLONE_SYMBOLS_FLAG, customizer); +} + +export default cloneWith; diff --git a/src/compact.ts b/src/compact.ts new file mode 100644 index 0000000000..da1aadd07e --- /dev/null +++ b/src/compact.ts @@ -0,0 +1,30 @@ +/** + * Creates an array with all falsey values removed. The values `false`, `null`, + * `0`, `""`, `undefined`, and `NaN` are falsey. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to compact. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * compact([0, 1, false, 2, '', 3]) + * // => [1, 2, 3] + */ +function compact(array) { + let resIndex = 0; + const result = []; + + if (array == null) { + return result; + } + + for (const value of array) { + if (value) { + result[resIndex++] = value; + } + } + return result; +} + +export default compact; diff --git a/src/cond.ts b/src/cond.ts new file mode 100644 index 0000000000..26ef9d396c --- /dev/null +++ b/src/cond.ts @@ -0,0 +1,51 @@ +import map from './map.js'; + +/** + * Creates a function that iterates over `pairs` and invokes the corresponding + * function of the first predicate to return truthy. The predicate-function + * pairs are invoked with the `this` binding and arguments of the created + * function. + * + * @since 4.0.0 + * @category Util + * @param {Array} pairs The predicate-function pairs. + * @returns {Function} Returns the new composite function. + * @example + * + * const func = cond([ + * [matches({ 'a': 1 }), () => 'matches A'], + * [conforms({ 'b': isNumber }), () => 'matches B'], + * [() => true, () => 'no match'] + * ]) + * + * func({ 'a': 1, 'b': 2 }) + * // => 'matches A' + * + * func({ 'a': 0, 'b': 1 }) + * // => 'matches B' + * + * func({ 'a': '1', 'b': '2' }) + * // => 'no match' + */ +function cond(pairs: any[]) { + const length = pairs == null ? 0 : pairs.length; + + pairs = !length + ? [] + : map(pairs, (pair: any[]) => { + if (typeof pair[1] !== 'function') { + throw new TypeError('Expected a function'); + } + return [pair[0], pair[1]]; + }); + // eslint-disable-next-line consistent-return + return function (this: any, ...args: any[]) { + for (const pair of pairs) { + if (pair[0].apply(this, args)) { + return pair[1].apply(this, args); + } + } + }; +} + +export default cond; diff --git a/src/conforms.ts b/src/conforms.ts new file mode 100644 index 0000000000..23d6fc9bcc --- /dev/null +++ b/src/conforms.ts @@ -0,0 +1,33 @@ +import baseClone from './.internal/baseClone.js'; +import baseConforms from './.internal/baseConforms.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_DEEP_FLAG = 1; + +/** + * Creates a function that invokes the predicate properties of `source` with + * the corresponding property values of a given object, returning `true` if + * all predicates return truthy, else `false`. + * + * **Note:** The created function is equivalent to `conformsTo` with + * `source` partially applied. + * + * @since 4.0.0 + * @category Util + * @param {Object} source The object of property predicates to conform to. + * @returns {Function} Returns the new spec function. + * @example + * + * const objects = [ + * { 'a': 2, 'b': 1 }, + * { 'a': 1, 'b': 2 } + * ] + * + * filter(objects, conforms({ 'b': function(n) { return n > 1 } })) + * // => [{ 'a': 1, 'b': 2 }] + */ +function conforms(source) { + return baseConforms(baseClone(source, CLONE_DEEP_FLAG)); +} + +export default conforms; diff --git a/src/conformsTo.ts b/src/conformsTo.ts new file mode 100644 index 0000000000..764697dc98 --- /dev/null +++ b/src/conformsTo.ts @@ -0,0 +1,30 @@ +import baseConformsTo from './.internal/baseConformsTo.js'; +import keys from './keys.js'; + +/** + * Checks if `object` conforms to `source` by invoking the predicate + * properties of `source` with the corresponding property values of `object`. + * + * **Note:** This method is equivalent to `conforms` when `source` is + * partially applied. + * + * @since 4.14.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property predicates to conform to. + * @returns {boolean} Returns `true` if `object` conforms, else `false`. + * @example + * + * const object = { 'a': 1, 'b': 2 } + * + * conformsTo(object, { 'b': function(n) { return n > 1 } }) + * // => true + * + * conformsTo(object, { 'b': function(n) { return n > 2 } }) + * // => false + */ +function conformsTo(object, source) { + return source == null || baseConformsTo(object, source, keys(source)); +} + +export default conformsTo; diff --git a/src/countBy.ts b/src/countBy.ts new file mode 100644 index 0000000000..bf427e56b0 --- /dev/null +++ b/src/countBy.ts @@ -0,0 +1,45 @@ +import baseAssignValue from './.internal/baseAssignValue.js'; +import reduce from './reduce.js'; + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the number of times the key was returned by `iteratee`. The + * iteratee is invoked with one argument: (value). + * + * @since 0.5.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'betty', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * countBy(users, value => value.active); + * // => { 'true': 2, 'false': 1 } + */ +function countBy(collection, iteratee) { + return reduce( + collection, + (result, value, key) => { + key = iteratee(value); + if (hasOwnProperty.call(result, key)) { + ++result[key]; + } else { + baseAssignValue(result, key, 1); + } + return result; + }, + {}, + ); +} + +export default countBy; diff --git a/src/create.ts b/src/create.ts new file mode 100644 index 0000000000..217de794b6 --- /dev/null +++ b/src/create.ts @@ -0,0 +1,39 @@ +/** + * Creates an object that inherits from the `prototype` object. If a + * `properties` object is given, its own enumerable string keyed properties + * are assigned to the created object. + * + * @since 2.3.0 + * @category Object + * @param {Object} prototype The object to inherit from. + * @param {Object} [properties] The properties to assign to the object. + * @returns {Object} Returns the new object. + * @example + * + * function Shape() { + * this.x = 0 + * this.y = 0 + * } + * + * function Circle() { + * Shape.call(this) + * } + * + * Circle.prototype = create(Shape.prototype, { + * 'constructor': Circle + * }) + * + * const circle = new Circle + * circle instanceof Circle + * // => true + * + * circle instanceof Shape + * // => true + */ +function create(prototype, properties) { + prototype = prototype === null ? null : Object(prototype); + const result = Object.create(prototype); + return properties == null ? result : Object.assign(result, properties); +} + +export default create; diff --git a/src/debounce.ts b/src/debounce.ts new file mode 100644 index 0000000000..83004215fb --- /dev/null +++ b/src/debounce.ts @@ -0,0 +1,217 @@ +import isObject from './isObject.js'; +import root from './.internal/root.js'; + +/** + * Creates a debounced function that delays invoking `func` until after `wait` + * milliseconds have elapsed since the last time the debounced function was + * invoked, or until the next browser frame is drawn. The debounced function + * comes with a `cancel` method to cancel delayed `func` invocations and a + * `flush` method to immediately invoke them. Provide `options` to indicate + * whether `func` should be invoked on the leading and/or trailing edge of the + * `wait` timeout. The `func` is invoked with the last arguments provided to the + * debounced function. Subsequent calls to the debounced function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the debounced function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until the next tick, similar to `setTimeout` with a timeout of `0`. + * + * If `wait` is omitted in an environment with `requestAnimationFrame`, `func` + * invocation will be deferred until the next frame is drawn (typically about + * 16ms). + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `debounce` and `throttle`. + * + * @since 0.1.0 + * @category Function + * @param {Function} func The function to debounce. + * @param {number} [wait=0] + * The number of milliseconds to delay; if omitted, `requestAnimationFrame` is + * used (if available). + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=false] + * Specify invoking on the leading edge of the timeout. + * @param {number} [options.maxWait] + * The maximum time `func` is allowed to be delayed before it's invoked. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new debounced function. + * @example + * + * // Avoid costly calculations while the window size is in flux. + * jQuery(window).on('resize', debounce(calculateLayout, 150)) + * + * // Invoke `sendMail` when clicked, debouncing subsequent calls. + * jQuery(element).on('click', debounce(sendMail, 300, { + * 'leading': true, + * 'trailing': false + * })) + * + * // Ensure `batchLog` is invoked once after 1 second of debounced calls. + * const debounced = debounce(batchLog, 250, { 'maxWait': 1000 }) + * const source = new EventSource('/stream') + * jQuery(source).on('message', debounced) + * + * // Cancel the trailing debounced invocation. + * jQuery(window).on('popstate', debounced.cancel) + * + * // Check for pending invocations. + * const status = debounced.pending() ? "Pending..." : "Ready" + */ +function debounce(func, wait, options) { + let lastArgs; + let lastThis; + let maxWait; + let result; + let timerId; + let lastCallTime; + let lastInvokeTime = 0; + let leading = false; + let maxing = false; + let trailing = true; + + // Bypass `requestAnimationFrame` by explicitly setting `wait=0`. + const useRAF = !wait && wait !== 0 && typeof root.requestAnimationFrame === 'function'; + + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + wait = +wait || 0; + if (isObject(options)) { + leading = !!options.leading; + maxing = 'maxWait' in options; + maxWait = maxing ? Math.max(+options.maxWait || 0, wait) : maxWait; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + + function invokeFunc(time) { + const args = lastArgs; + const thisArg = lastThis; + + lastArgs = lastThis = undefined; + lastInvokeTime = time; + result = func.apply(thisArg, args); + return result; + } + + function startTimer(pendingFunc, milliseconds) { + if (useRAF) { + root.cancelAnimationFrame(timerId); + return root.requestAnimationFrame(pendingFunc); + } + // eslint-disable-next-line @typescript-eslint/no-implied-eval + return setTimeout(pendingFunc, milliseconds); + } + + function cancelTimer(id) { + if (useRAF) { + root.cancelAnimationFrame(id); + return; + } + clearTimeout(id); + } + + function leadingEdge(time) { + // Reset any `maxWait` timer. + lastInvokeTime = time; + // Start the timer for the trailing edge. + timerId = startTimer(timerExpired, wait); + // Invoke the leading edge. + return leading ? invokeFunc(time) : result; + } + + function remainingWait(time) { + const timeSinceLastCall = time - lastCallTime; + const timeSinceLastInvoke = time - lastInvokeTime; + const timeWaiting = wait - timeSinceLastCall; + + return maxing ? Math.min(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; + } + + function shouldInvoke(time) { + const timeSinceLastCall = time - lastCallTime; + const timeSinceLastInvoke = time - lastInvokeTime; + + // Either this is the first call, activity has stopped and we're at the + // trailing edge, the system time has gone backwards and we're treating + // it as the trailing edge, or we've hit the `maxWait` limit. + return ( + lastCallTime === undefined || + timeSinceLastCall >= wait || + timeSinceLastCall < 0 || + (maxing && timeSinceLastInvoke >= maxWait) + ); + } + + function timerExpired() { + const time = Date.now(); + if (shouldInvoke(time)) { + return trailingEdge(time); + } + // Restart the timer. + timerId = startTimer(timerExpired, remainingWait(time)); + return undefined; + } + + function trailingEdge(time) { + timerId = undefined; + + // Only invoke if we have `lastArgs` which means `func` has been + // debounced at least once. + if (trailing && lastArgs) { + return invokeFunc(time); + } + lastArgs = lastThis = undefined; + return result; + } + + function cancel() { + if (timerId !== undefined) { + cancelTimer(timerId); + } + lastInvokeTime = 0; + lastArgs = lastCallTime = lastThis = timerId = undefined; + } + + function flush() { + return timerId === undefined ? result : trailingEdge(Date.now()); + } + + function pending() { + return timerId !== undefined; + } + + function debounced(...args) { + const time = Date.now(); + const isInvoking = shouldInvoke(time); + + lastArgs = args; + lastThis = this; + lastCallTime = time; + + if (isInvoking) { + if (timerId === undefined) { + return leadingEdge(lastCallTime); + } + if (maxing) { + // Handle invocations in a tight loop. + timerId = startTimer(timerExpired, wait); + return invokeFunc(lastCallTime); + } + } + if (timerId === undefined) { + timerId = startTimer(timerExpired, wait); + } + return result; + } + debounced.cancel = cancel; + debounced.flush = flush; + debounced.pending = pending; + return debounced; +} + +export default debounce; diff --git a/src/deburr.ts b/src/deburr.ts new file mode 100644 index 0000000000..26dd027634 --- /dev/null +++ b/src/deburr.ts @@ -0,0 +1,49 @@ +import deburrLetter from './.internal/deburrLetter.js'; + +/** Used to match Latin Unicode letters (excluding mathematical operators). */ +const reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g; + +/** Used to compose unicode character classes. */ +const rsComboMarksRange = '\\u0300-\\u036f'; +const reComboHalfMarksRange = '\\ufe20-\\ufe2f'; +const rsComboSymbolsRange = '\\u20d0-\\u20ff'; +const rsComboMarksExtendedRange = '\\u1ab0-\\u1aff'; +const rsComboMarksSupplementRange = '\\u1dc0-\\u1dff'; +const rsComboRange = + rsComboMarksRange + + reComboHalfMarksRange + + rsComboSymbolsRange + + rsComboMarksExtendedRange + + rsComboMarksSupplementRange; + +/** Used to compose unicode capture groups. */ +const rsCombo = `[${rsComboRange}]`; + +/** + * Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and + * [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols). + */ +// eslint-disable-next-line no-misleading-character-class +const reComboMark = RegExp(rsCombo, 'g'); + +/** + * Deburrs `string` by converting + * [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table) + * and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A) + * letters to basic Latin letters and removing + * [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks). + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to deburr. + * @returns {string} Returns the deburred string. + * @example + * + * deburr('déjà vu') + * // => 'deja vu' + */ +function deburr(string) { + return string && string.replace(reLatin, deburrLetter).replace(reComboMark, ''); +} + +export default deburr; diff --git a/src/defaultTo.ts b/src/defaultTo.ts new file mode 100644 index 0000000000..a334ed7d01 --- /dev/null +++ b/src/defaultTo.ts @@ -0,0 +1,23 @@ +/** + * Checks `value` to determine whether a default value should be returned in + * its place. The `defaultValue` is returned if `value` is `NaN`, `null`, + * or `undefined`. + * + * @since 4.14.0 + * @category Util + * @param {*} value The value to check. + * @param {*} defaultValue The default value. + * @returns {*} Returns the resolved value. + * @example + * + * defaultTo(1, 10) + * // => 1 + * + * defaultTo(undefined, 10) + * // => 10 + */ +function defaultTo(value, defaultValue) { + return value == null || value !== value ? defaultValue : value; +} + +export default defaultTo; diff --git a/src/defaultToAny.ts b/src/defaultToAny.ts new file mode 100644 index 0000000000..78d807b979 --- /dev/null +++ b/src/defaultToAny.ts @@ -0,0 +1,32 @@ +import arrayReduce from './.internal/arrayReduce.js'; +import defaultTo from './defaultTo.js'; + +/** + * This method is like `defaultTo` except that it accepts multiple default values and returns the first one that is not + * `NaN`, `null`, or `undefined`. + * + * @since 5.0.0 + * @category Util + * @param {*} value The value to check. + * @param {...*} defaultValues The default values. + * @returns {*} Returns the resolved value. + * @see _.defaultTo + * @example + * + * defaultToAny(1, 10, 20) + * // => 1 + * + * defaultToAny(undefined, 10, 20) + * // => 10 + * + * defaultToAny(undefined, null, 20) + * // => 20 + * + * defaultToAny(undefined, null, NaN) + * // => NaN + */ +function defaultToAny(value, ...defaultValues) { + return arrayReduce(defaultValues, defaultTo, value); +} + +export default defaultToAny; diff --git a/src/defaults.ts b/src/defaults.ts new file mode 100644 index 0000000000..6f891919fc --- /dev/null +++ b/src/defaults.ts @@ -0,0 +1,47 @@ +import eq from './eq.js'; + +/** Used for built-in method references. */ +const objectProto = Object.prototype; + +/** Used to check objects for own properties. */ +const hasOwnProperty = objectProto.hasOwnProperty; + +/** + * Assigns own and inherited enumerable string keyed properties of source + * objects to the destination object for all destination properties that + * resolve to `undefined`. Source objects are applied from left to right. + * Once a property is set, additional values of the same property are ignored. + * + * **Note:** This method mutates `object`. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see defaultsDeep + * @example + * + * defaults({ 'a': 1 }, { 'b': 2 }, { 'a': 3 }) + * // => { 'a': 1, 'b': 2 } + */ +function defaults(object, ...sources) { + object = Object(object); + sources.forEach((source) => { + if (source != null) { + source = Object(source); + for (const key in source) { + const value = object[key]; + if ( + value === undefined || + (eq(value, objectProto[key]) && !hasOwnProperty.call(object, key)) + ) { + object[key] = source[key]; + } + } + } + }); + return object; +} + +export default defaults; diff --git a/src/defaultsDeep.ts b/src/defaultsDeep.ts new file mode 100644 index 0000000000..921b1b97cc --- /dev/null +++ b/src/defaultsDeep.ts @@ -0,0 +1,26 @@ +import customDefaultsMerge from './.internal/customDefaultsMerge.js'; +import mergeWith from './mergeWith.js'; + +/** + * This method is like `defaults` except that it recursively assigns + * default properties. + * + * **Note:** This method mutates `object`. + * + * @since 3.10.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @see defaults + * @example + * + * defaultsDeep({ 'a': { 'b': 2 } }, { 'a': { 'b': 1, 'c': 3 } }) + * // => { 'a': { 'b': 2, 'c': 3 } } + */ +function defaultsDeep(...args) { + args.push(undefined, customDefaultsMerge); + return mergeWith.apply(undefined, args); +} + +export default defaultsDeep; diff --git a/src/defer.ts b/src/defer.ts new file mode 100644 index 0000000000..5f9f1f0195 --- /dev/null +++ b/src/defer.ts @@ -0,0 +1,23 @@ +/** + * Defers invoking the `func` until the current call stack has cleared. Any + * additional arguments are provided to `func` when it's invoked. + * + * @since 0.1.0 + * @category Function + * @param {Function} func The function to defer. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * defer(text => console.log(text), 'deferred') + * // => Logs 'deferred' after one millisecond. + */ +function defer(func: Function, ...args: any[]) { + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + // eslint-disable-next-line @typescript-eslint/no-implied-eval + return setTimeout(func, 1, ...args); +} + +export default defer; diff --git a/src/delay.ts b/src/delay.ts new file mode 100644 index 0000000000..133329aa35 --- /dev/null +++ b/src/delay.ts @@ -0,0 +1,24 @@ +/** + * Invokes `func` after `wait` milliseconds. Any additional arguments are + * provided to `func` when it's invoked. + * + * @since 0.1.0 + * @category Function + * @param {Function} func The function to delay. + * @param {number} wait The number of milliseconds to delay invocation. + * @param {...*} [args] The arguments to invoke `func` with. + * @returns {number} Returns the timer id. + * @example + * + * delay(text => console.log(text), 1000, 'later') + * // => Logs 'later' after one second. + */ +function delay(func: Function, wait: number, ...args: any[]) { + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + // eslint-disable-next-line @typescript-eslint/no-implied-eval + return setTimeout(func, +wait || 0, ...args); +} + +export default delay; diff --git a/src/difference.ts b/src/difference.ts new file mode 100644 index 0000000000..61168cc581 --- /dev/null +++ b/src/difference.ts @@ -0,0 +1,30 @@ +import baseDifference from './.internal/baseDifference.js'; +import baseFlatten from './.internal/baseFlatten.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; + +/** + * Creates an array of `array` values not included in the other given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * **Note:** Unlike `pullAll`, this method returns a new array. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see union, unionBy, unionWith, without, xor, xorBy, xorWith, + * @example + * + * difference([2, 1], [2, 3]) + * // => [1] + */ +function difference(array, ...values) { + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true)) + : []; +} + +export default difference; diff --git a/src/differenceBy.ts b/src/differenceBy.ts new file mode 100644 index 0000000000..59e61d79cc --- /dev/null +++ b/src/differenceBy.ts @@ -0,0 +1,36 @@ +import baseDifference from './.internal/baseDifference.js'; +import baseFlatten from './.internal/baseFlatten.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `difference` except that it accepts `iteratee` which + * is invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * **Note:** Unlike `pullAllBy`, this method returns a new array. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor) + * // => [1.2] + */ +function differenceBy(array, ...values) { + let iteratee = last(values); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return isArrayLikeObject(array) + ? baseDifference(array, baseFlatten(values, 1, isArrayLikeObject, true), iteratee) + : []; +} + +export default differenceBy; diff --git a/src/differenceWith.ts b/src/differenceWith.ts new file mode 100644 index 0000000000..a20665b0df --- /dev/null +++ b/src/differenceWith.ts @@ -0,0 +1,42 @@ +import baseDifference from './.internal/baseDifference.js'; +import baseFlatten from './.internal/baseFlatten.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `difference` except that it accepts `comparator` + * which is invoked to compare elements of `array` to `values`. The order and + * references of result values are determined by the first array. The comparator + * is invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `pullAllWith`, this method returns a new array. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...Array} [values] The values to exclude. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @example + * + * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + * + * differenceWith(objects, [{ 'x': 1, 'y': 2 }], isEqual) + * // => [{ 'x': 2, 'y': 1 }] + */ +function differenceWith(array, ...values) { + let comparator = last(values); + if (isArrayLikeObject(comparator)) { + comparator = undefined; + } + return isArrayLikeObject(array) + ? baseDifference( + array, + baseFlatten(values, 1, isArrayLikeObject, true), + undefined, + comparator, + ) + : []; +} + +export default differenceWith; diff --git a/src/divide.ts b/src/divide.ts new file mode 100644 index 0000000000..5601534259 --- /dev/null +++ b/src/divide.ts @@ -0,0 +1,18 @@ +import createMathOperation from './.internal/createMathOperation.js'; + +/** + * Divide two numbers. + * + * @since 4.7.0 + * @category Math + * @param {number} dividend The first number in a division. + * @param {number} divisor The second number in a division. + * @returns {number} Returns the quotient. + * @example + * + * divide(6, 4) + * // => 1.5 + */ +const divide = createMathOperation((dividend, divisor) => dividend / divisor, 1); + +export default divide; diff --git a/src/drop.ts b/src/drop.ts new file mode 100644 index 0000000000..b242852f94 --- /dev/null +++ b/src/drop.ts @@ -0,0 +1,31 @@ +import slice from './slice.js'; +import toInteger from './toInteger.js'; + +/** + * Creates a slice of `array` with `n` elements dropped from the beginning. + * + * @since 0.5.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @returns {Array} Returns the slice of `array`. + * @example + * + * drop([1, 2, 3]) + * // => [2, 3] + * + * drop([1, 2, 3], 2) + * // => [3] + * + * drop([1, 2, 3], 5) + * // => [] + * + * drop([1, 2, 3], 0) + * // => [1, 2, 3] + */ +function drop(array, n = 1) { + const length = array == null ? 0 : array.length; + return length ? slice(array, n < 0 ? 0 : toInteger(n), length) : []; +} + +export default drop; diff --git a/src/dropRight.ts b/src/dropRight.ts new file mode 100644 index 0000000000..9729672db6 --- /dev/null +++ b/src/dropRight.ts @@ -0,0 +1,32 @@ +import slice from './slice.js'; +import toInteger from './toInteger.js'; + +/** + * Creates a slice of `array` with `n` elements dropped from the end. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to drop. + * @returns {Array} Returns the slice of `array`. + * @example + * + * dropRight([1, 2, 3]) + * // => [1, 2] + * + * dropRight([1, 2, 3], 2) + * // => [1] + * + * dropRight([1, 2, 3], 5) + * // => [] + * + * dropRight([1, 2, 3], 0) + * // => [1, 2, 3] + */ +function dropRight(array, n = 1) { + const length = array == null ? 0 : array.length; + n = length - toInteger(n); + return length ? slice(array, 0, n < 0 ? 0 : n) : []; +} + +export default dropRight; diff --git a/src/dropRightWhile.ts b/src/dropRightWhile.ts new file mode 100644 index 0000000000..1ce47b1d27 --- /dev/null +++ b/src/dropRightWhile.ts @@ -0,0 +1,28 @@ +import baseWhile from './.internal/baseWhile.js'; + +/** + * Creates a slice of `array` excluding elements dropped from the end. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': true }, + * { 'user': 'pebbles', 'active': true } + * ] + * + * dropRightWhile(users, ({ active }) => active) + * // => objects for ['barney'] + */ +function dropRightWhile(array, predicate) { + return array != null && array.length ? baseWhile(array, predicate, true, true) : []; +} + +export default dropRightWhile; diff --git a/src/dropWhile.ts b/src/dropWhile.ts new file mode 100644 index 0000000000..e3cb5d157f --- /dev/null +++ b/src/dropWhile.ts @@ -0,0 +1,28 @@ +import baseWhile from './.internal/baseWhile.js'; + +/** + * Creates a slice of `array` excluding elements dropped from the beginning. + * Elements are dropped until `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': true }, + * { 'user': 'pebbles', 'active': false } + * ] + * + * dropWhile(users, ({ active }) => active) + * // => objects for ['pebbles'] + */ +function dropWhile(array, predicate) { + return array != null && array.length ? baseWhile(array, predicate, true) : []; +} + +export default dropWhile; diff --git a/src/each.ts b/src/each.ts new file mode 100644 index 0000000000..5ff380dc9a --- /dev/null +++ b/src/each.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-restricted-exports +export { default } from './forEach.js'; diff --git a/src/eachRight.ts b/src/eachRight.ts new file mode 100644 index 0000000000..3c64d96f99 --- /dev/null +++ b/src/eachRight.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-restricted-exports +export { default } from './forEachRight.js'; diff --git a/src/endsWith.ts b/src/endsWith.ts new file mode 100644 index 0000000000..e900d88877 --- /dev/null +++ b/src/endsWith.ts @@ -0,0 +1,36 @@ +/** + * Checks if `string` ends with the given target string. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=string.length] The position to search up to. + * @returns {boolean} Returns `true` if `string` ends with `target`, + * else `false`. + * @see includes, startsWith + * @example + * + * endsWith('abc', 'c') + * // => true + * + * endsWith('abc', 'b') + * // => false + * + * endsWith('abc', 'b', 2) + * // => true + */ +function endsWith(string, target, position) { + const { length } = string; + position = position === undefined ? length : +position; + if (position < 0 || position !== position) { + position = 0; + } else if (position > length) { + position = length; + } + const end = position; + position -= target.length; + return position >= 0 && string.slice(position, end) === target; +} + +export default endsWith; diff --git a/src/eq.ts b/src/eq.ts new file mode 100644 index 0000000000..b63102679b --- /dev/null +++ b/src/eq.ts @@ -0,0 +1,35 @@ +/** + * Performs a + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * comparison between two values to determine if they are equivalent. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * const object = { 'a': 1 } + * const other = { 'a': 1 } + * + * eq(object, object) + * // => true + * + * eq(object, other) + * // => false + * + * eq('a', 'a') + * // => true + * + * eq('a', Object('a')) + * // => false + * + * eq(NaN, NaN) + * // => true + */ +function eq(value, other) { + return value === other || (value !== value && other !== other); +} + +export default eq; diff --git a/src/eqDeep.ts b/src/eqDeep.ts new file mode 100644 index 0000000000..64d37d2887 --- /dev/null +++ b/src/eqDeep.ts @@ -0,0 +1,33 @@ +import baseIsEqual from './.internal/baseIsEqual.js'; + +/** + * Performs a deep comparison between two values to determine if they are + * equivalent. + * + * **Note:** This method supports comparing arrays, array buffers, booleans, + * date objects, error objects, maps, numbers, `Object` objects, regexes, + * sets, strings, symbols, and typed arrays. `Object` objects are compared + * by their own, not inherited, enumerable properties. Functions and DOM + * nodes are compared by strict equality, i.e. `===`. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * const object = { 'a': 1 } + * const other = { 'a': 1 } + * + * isEqual(object, other) + * // => true + * + * object === other + * // => false + */ +function isEqual(value, other) { + return baseIsEqual(value, other); +} + +export default isEqual; diff --git a/src/escape.ts b/src/escape.ts new file mode 100644 index 0000000000..0517f42e8b --- /dev/null +++ b/src/escape.ts @@ -0,0 +1,47 @@ +/** Used to map characters to HTML entities. */ +const htmlEscapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''', +}; + +/** Used to match HTML entities and HTML characters. */ +const reUnescapedHtml = /[&<>"']/g; +const reHasUnescapedHtml = RegExp(reUnescapedHtml.source); + +/** + * Converts the characters "&", "<", ">", '"', and "'" in `string` to their + * corresponding HTML entities. + * + * **Note:** No other characters are escaped. To escape additional + * characters use a third-party library like [_he_](https://mths.be/he). + * + * Though the ">" character is escaped for symmetry, characters like + * ">" and "/" don't need escaping in HTML and have no special meaning + * unless they're part of a tag or unquoted attribute value. See + * [Mathias Bynens's article](https://mathiasbynens.be/notes/ambiguous-ampersands) + * (under "semi-related fun fact") for more details. + * + * When working with HTML you should always + * [quote attribute values](http://wonko.com/post/html-escaping) to reduce + * XSS vectors. + * + * @since 0.1.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @see escapeRegExp, unescape + * @example + * + * escape('fred, barney, & pebbles') + * // => 'fred, barney, & pebbles' + */ +function escape(string) { + return string && reHasUnescapedHtml.test(string) + ? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) + : string || ''; +} + +export default escape; diff --git a/src/escapeRegExp.ts b/src/escapeRegExp.ts new file mode 100644 index 0000000000..2d06b7d83b --- /dev/null +++ b/src/escapeRegExp.ts @@ -0,0 +1,28 @@ +/** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ +const reRegExpChar = /[\\^$.*+?()[\]{}|]/g; +const reHasRegExpChar = RegExp(reRegExpChar.source); + +/** + * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+", + * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to escape. + * @returns {string} Returns the escaped string. + * @see escape, escapeRegExp, unescape + * @example + * + * escapeRegExp('[lodash](https://lodash.com/)') + * // => '\[lodash\]\(https://lodash\.com/\)' + */ +function escapeRegExp(string) { + return string && reHasRegExpChar.test(string) + ? string.replace(reRegExpChar, '\\$&') + : string || ''; +} + +export default escapeRegExp; diff --git a/src/every.ts b/src/every.ts new file mode 100644 index 0000000000..c738e4c57c --- /dev/null +++ b/src/every.ts @@ -0,0 +1,34 @@ +/** + * Checks if `predicate` returns truthy for **all** elements of `array`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, index, array). + * + * **Note:** This method returns `true` for + * [empty arrays](https://en.wikipedia.org/wiki/Empty_set) because + * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of + * elements of empty arrays. + * + * @since 5.0.0 + * @category Array + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all elements pass the predicate check, + * else `false`. + * @example + * + * every([true, 1, null, 'yes'], Boolean) + * // => false + */ +function every(array, predicate) { + let index = -1; + const length = array == null ? 0 : array.length; + + while (++index < length) { + if (!predicate(array[index], index, array)) { + return false; + } + } + return true; +} + +export default every; diff --git a/src/everyValue.ts b/src/everyValue.ts new file mode 100644 index 0000000000..d35489594e --- /dev/null +++ b/src/everyValue.ts @@ -0,0 +1,34 @@ +/** + * Checks if `predicate` returns truthy for **all** properties of `object`. + * Iteration is stopped once `predicate` returns falsey. The predicate is + * invoked with three arguments: (value, key, object). + * + * **Note:** This method returns `true` for + * [empty objects](https://en.wikipedia.org/wiki/Empty_set) because + * [everything is true](https://en.wikipedia.org/wiki/Vacuous_truth) of + * elements of empty objects. + * + * @since 5.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if all properties pass the predicate check, + * else `false`. + * @example + * + * everyValue({ 'a': 0, 'b': 'yes', 'c': false }, Boolean) + * // => false + */ +function everyValue(object, predicate) { + object = Object(object); + const props = Object.keys(object); + + for (const key of props) { + if (!predicate(object[key], key, object)) { + return false; + } + } + return true; +} + +export default everyValue; diff --git a/src/filter.ts b/src/filter.ts new file mode 100644 index 0000000000..12f9487fa7 --- /dev/null +++ b/src/filter.ts @@ -0,0 +1,39 @@ +/** + * Iterates over elements of `array`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, index, array). + * + * **Note:** Unlike `remove`, this method returns a new array. + * + * @since 5.0.0 + * @category Array + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see pull, pullAll, pullAllBy, pullAllWith, pullAt, remove, reject + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * filter(users, ({ active }) => active) + * // => objects for ['barney'] + */ +function filter(array, predicate) { + let index = -1; + let resIndex = 0; + const length = array == null ? 0 : array.length; + const result = []; + + while (++index < length) { + const value = array[index]; + if (predicate(value, index, array)) { + result[resIndex++] = value; + } + } + return result; +} + +export default filter; diff --git a/src/filterObject.ts b/src/filterObject.ts new file mode 100644 index 0000000000..be9bec3d70 --- /dev/null +++ b/src/filterObject.ts @@ -0,0 +1,34 @@ +/** + * Iterates over properties of `object`, returning an array of all elements + * `predicate` returns truthy for. The predicate is invoked with three + * arguments: (value, key, object). + * + * If you want an object in return, consider `pickBy`. + * + * @since 5.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see pickBy, pull, pullAll, pullAllBy, pullAllWith, pullAt, remove, reject + * @example + * + * const object = { 'a': 5, 'b': 8, 'c': 10 } + * + * filterObject(object, (n) => !(n % 5)) + * // => [5, 10] + */ +function filterObject(object, predicate) { + object = Object(object); + const result = []; + + Object.keys(object).forEach((key) => { + const value = object[key]; + if (predicate(value, key, object)) { + result.push(value); + } + }); + return result; +} + +export default filterObject; diff --git a/src/findKey.ts b/src/findKey.ts new file mode 100644 index 0000000000..8553cf50af --- /dev/null +++ b/src/findKey.ts @@ -0,0 +1,38 @@ +/** + * This method is like `find` except that it returns the key of the first + * element `predicate` returns truthy for instead of the element itself. + * + * @since 1.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} predicate The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @see find, findIndex, findLast, findLastIndex, findLastKey + * @example + * + * const users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * } + * + * findKey(users, ({ age }) => age < 40) + * // => 'barney' (iteration order is not guaranteed) + */ +function findKey(object, predicate) { + if (object == null) { + return undefined; + } + const keys = Object.keys(object); + for (let i = 0, { length } = keys; i < length; i += 1) { + const key = keys[i]; + const value = object[key]; + if (predicate(value, key, object)) { + return key; + } + } + return undefined; +} + +export default findKey; diff --git a/src/findLast.ts b/src/findLast.ts new file mode 100644 index 0000000000..c57d1875d5 --- /dev/null +++ b/src/findLast.ts @@ -0,0 +1,32 @@ +import findLastIndex from './findLastIndex.js'; +import isArrayLike from './isArrayLike.js'; + +/** + * This method is like `find` except that it iterates over elements of + * `collection` from right to left. + * + * @since 2.0.0 + * @category Collection + * @param {Array|Object} collection The collection to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} [fromIndex=collection.length-1] The index to search from. + * @returns {*} Returns the matched element, else `undefined`. + * @see find, findIndex, findKey, findLastIndex, findLastKey + * @example + * + * findLast([1, 2, 3, 4], n => n % 2 === 1) + * // => 3 + */ +function findLast(collection, predicate, fromIndex) { + let iteratee; + const iterable = Object(collection); + if (!isArrayLike(collection)) { + collection = Object.keys(collection); + iteratee = predicate; + predicate = (key) => iteratee(iterable[key], key, iterable); + } + const index = findLastIndex(collection, predicate, fromIndex); + return index > -1 ? iterable[iteratee ? collection[index] : index] : undefined; +} + +export default findLast; diff --git a/src/findLastIndex.ts b/src/findLastIndex.ts new file mode 100644 index 0000000000..9b0b844d17 --- /dev/null +++ b/src/findLastIndex.ts @@ -0,0 +1,39 @@ +import baseFindIndex from './.internal/baseFindIndex.js'; +import toInteger from './toInteger.js'; + +/** + * This method is like `findIndex` except that it iterates over elements + * of `collection` from right to left. + * + * @since 2.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} predicate The function invoked per iteration. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the found element, else `-1`. + * @see find, findIndex, findKey, findLast, findLastKey + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false }, + * { 'user': 'pebbles', 'active': false } + * ] + * + * findLastIndex(users, ({ user }) => user === 'pebbles') + * // => 2 + */ +function findLastIndex(array, predicate, fromIndex) { + const length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + let index = length - 1; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = fromIndex < 0 ? Math.max(length + index, 0) : Math.min(index, length - 1); + } + return baseFindIndex(array, predicate, index, true); +} + +export default findLastIndex; diff --git a/src/findLastKey.ts b/src/findLastKey.ts new file mode 100644 index 0000000000..adef032f70 --- /dev/null +++ b/src/findLastKey.ts @@ -0,0 +1,30 @@ +import baseFindKey from './.internal/baseFindKey.js'; +import baseForOwnRight from './.internal/baseForOwnRight.js'; + +/** + * This method is like `findKey` except that it iterates over elements of + * a collection in the opposite order. + * + * @since 2.0.0 + * @category Object + * @param {Object} object The object to inspect. + * @param {Function} predicate The function invoked per iteration. + * @returns {string|undefined} Returns the key of the matched element, + * else `undefined`. + * @see find, findIndex, findKey, findLast, findLastIndex + * @example + * + * const users = { + * 'barney': { 'age': 36, 'active': true }, + * 'fred': { 'age': 40, 'active': false }, + * 'pebbles': { 'age': 1, 'active': true } + * } + * + * findLastKey(users, ({ age }) => age < 40) + * // => returns 'pebbles' assuming `findKey` returns 'barney' + */ +function findLastKey(object, predicate) { + return baseFindKey(object, predicate, baseForOwnRight); +} + +export default findLastKey; diff --git a/src/first.ts b/src/first.ts new file mode 100644 index 0000000000..275162cf27 --- /dev/null +++ b/src/first.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line no-restricted-exports +export { default } from './head.js'; diff --git a/src/flatMap.ts b/src/flatMap.ts new file mode 100644 index 0000000000..410987039e --- /dev/null +++ b/src/flatMap.ts @@ -0,0 +1,28 @@ +import baseFlatten from './.internal/baseFlatten.js'; +import map from './map.js'; + +/** + * Creates a flattened array of values by running each element in `collection` + * thru `iteratee` and flattening the mapped results. The iteratee is invoked + * with three arguments: (value, index|key, collection). + * + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @see flatMapDeep, flatMapDepth, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues + * @example + * + * function duplicate(n) { + * return [n, n] + * } + * + * flatMap([1, 2], duplicate) + * // => [1, 1, 2, 2] + */ +function flatMap(collection, iteratee) { + return baseFlatten(map(collection, iteratee), 1); +} + +export default flatMap; diff --git a/src/flatMapDeep.ts b/src/flatMapDeep.ts new file mode 100644 index 0000000000..d19abe7682 --- /dev/null +++ b/src/flatMapDeep.ts @@ -0,0 +1,30 @@ +import baseFlatten from './.internal/baseFlatten.js'; +import map from './map.js'; + +/** Used as references for various `Number` constants. */ +const INFINITY = 1 / 0; + +/** + * This method is like `flatMap` except that it recursively flattens the + * mapped results. + * + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new flattened array. + * @see flatMap, flatMapDepth, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues + * @example + * + * function duplicate(n) { + * return [[[n, n]]] + * } + * + * flatMapDeep([1, 2], duplicate) + * // => [1, 1, 2, 2] + */ +function flatMapDeep(collection, iteratee) { + return baseFlatten(map(collection, iteratee), INFINITY); +} + +export default flatMapDeep; diff --git a/src/flatMapDepth.ts b/src/flatMapDepth.ts new file mode 100644 index 0000000000..607c76f5e1 --- /dev/null +++ b/src/flatMapDepth.ts @@ -0,0 +1,29 @@ +import baseFlatten from './.internal/baseFlatten.js'; +import map from './map.js'; + +/** + * This method is like `flatMap` except that it recursively flattens the + * mapped results up to `depth` times. + * + * @since 4.7.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @see flatMap, flatMapDeep, flatten, flattenDeep, flattenDepth, map, mapKeys, mapValues + * @example + * + * function duplicate(n) { + * return [[[n, n]]] + * } + * + * flatMapDepth([1, 2], duplicate, 2) + * // => [[1, 1], [2, 2]] + */ +function flatMapDepth(collection, iteratee, depth) { + depth = depth === undefined ? 1 : +depth; + return baseFlatten(map(collection, iteratee), depth); +} + +export default flatMapDepth; diff --git a/src/flatten.ts b/src/flatten.ts new file mode 100644 index 0000000000..ff9ccfee37 --- /dev/null +++ b/src/flatten.ts @@ -0,0 +1,21 @@ +import baseFlatten from './.internal/baseFlatten.js'; + +/** + * Flattens `array` a single level deep. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @see flatMap, flatMapDeep, flatMapDepth, flattenDeep, flattenDepth + * @example + * + * flatten([1, [2, [3, [4]], 5]]) + * // => [1, 2, [3, [4]], 5] + */ +function flatten(array) { + const length = array == null ? 0 : array.length; + return length ? baseFlatten(array, 1) : []; +} + +export default flatten; diff --git a/src/flattenDeep.ts b/src/flattenDeep.ts new file mode 100644 index 0000000000..9e58ed4888 --- /dev/null +++ b/src/flattenDeep.ts @@ -0,0 +1,24 @@ +import baseFlatten from './.internal/baseFlatten.js'; + +/** Used as references for various `Number` constants. */ +const INFINITY = 1 / 0; + +/** + * Recursively flattens `array`. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to flatten. + * @returns {Array} Returns the new flattened array. + * @see flatMap, flatMapDeep, flatMapDepth, flatten, flattenDepth + * @example + * + * flattenDeep([1, [2, [3, [4]], 5]]) + * // => [1, 2, 3, 4, 5] + */ +function flattenDeep(array) { + const length = array == null ? 0 : array.length; + return length ? baseFlatten(array, INFINITY) : []; +} + +export default flattenDeep; diff --git a/src/flattenDepth.ts b/src/flattenDepth.ts new file mode 100644 index 0000000000..0844c98b6f --- /dev/null +++ b/src/flattenDepth.ts @@ -0,0 +1,31 @@ +import baseFlatten from './.internal/baseFlatten.js'; + +/** + * Recursively flatten `array` up to `depth` times. + * + * @since 4.4.0 + * @category Array + * @param {Array} array The array to flatten. + * @param {number} [depth=1] The maximum recursion depth. + * @returns {Array} Returns the new flattened array. + * @see flatMap, flatMapDeep, flatMapDepth, flattenDeep + * @example + * + * const array = [1, [2, [3, [4]], 5]] + * + * flattenDepth(array, 1) + * // => [1, 2, [3, [4]], 5] + * + * flattenDepth(array, 2) + * // => [1, 2, 3, [4], 5] + */ +function flattenDepth(array, depth) { + const length = array == null ? 0 : array.length; + if (!length) { + return []; + } + depth = depth === undefined ? 1 : +depth; + return baseFlatten(array, depth); +} + +export default flattenDepth; diff --git a/src/flip.ts b/src/flip.ts new file mode 100644 index 0000000000..dc1e099d3a --- /dev/null +++ b/src/flip.ts @@ -0,0 +1,25 @@ +/** + * Creates a function that invokes `func` with arguments reversed. + * + * @since 4.0.0 + * @category Function + * @param {Function} func The function to flip arguments for. + * @returns {Function} Returns the new flipped function. + * @see reverse + * @example + * + * const flipped = flip((...args) => args) + * + * flipped('a', 'b', 'c', 'd') + * // => ['d', 'c', 'b', 'a'] + */ +function flip(func) { + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + return function (...args) { + return func.apply(this, args.reverse()); + }; +} + +export default flip; diff --git a/src/floor.ts b/src/floor.ts new file mode 100644 index 0000000000..98ed9bcfa4 --- /dev/null +++ b/src/floor.ts @@ -0,0 +1,24 @@ +import createRound from './.internal/createRound.js'; + +/** + * Computes `number` rounded down to `precision`. + * + * @since 3.10.0 + * @category Math + * @param {number} number The number to round down. + * @param {number} [precision=0] The precision to round down to. + * @returns {number} Returns the rounded down number. + * @example + * + * floor(4.006) + * // => 4 + * + * floor(0.046, 2) + * // => 0.04 + * + * floor(4060, -2) + * // => 4000 + */ +const floor = createRound('floor'); + +export default floor; diff --git a/src/flow.ts b/src/flow.ts new file mode 100644 index 0000000000..b65711b130 --- /dev/null +++ b/src/flow.ts @@ -0,0 +1,41 @@ +/** + * Composes a function that returns the result of invoking the given functions + * with the `this` binding of the created function, where each successive + * invocation is supplied the return value of the previous. + * + * @since 3.0.0 + * @category Util + * @param {Function[]} [funcs] The functions to invoke. + * @returns {Function} Returns the new composite function. + * @see flowRight + * @example + * + * import add from 'lodash/add' + * + * function square(n) { + * return n * n + * } + * + * const addSquare = flow(add, square) + * addSquare(1, 2) + * // => 9 + */ +function flow(...funcs: Function[]) { + const length = funcs.length; + let i = length; + while (i--) { + if (typeof funcs[i] !== 'function') { + throw new TypeError('Expected a function'); + } + } + return function (this: any, ...args: any[]) { + let j = 0; + let result = length ? funcs[j].apply(this, args) : args[0]; + while (++j < length) { + result = funcs[j].call(this, result); + } + return result; + }; +} + +export default flow; diff --git a/src/flowRight.ts b/src/flowRight.ts new file mode 100644 index 0000000000..4b0d473533 --- /dev/null +++ b/src/flowRight.ts @@ -0,0 +1,28 @@ +import flow from './flow.js'; + +/** + * This method is like `flow` except that it composes a function that + * invokes the given functions from right to left. + * + * @since 3.0.0 + * @category Util + * @param {Function[]} [funcs] The functions to invoke. + * @returns {Function} Returns the new composite function. + * @see flow + * @example + * + * import add from 'lodash/add' + * + * function square(n) { + * return n * n + * } + * + * const addSquare = flowRight(square, add) + * addSquare(1, 2) + * // => 9 + */ +function flowRight(...funcs) { + return flow(...funcs.reverse()); +} + +export default flowRight; diff --git a/src/forEach.ts b/src/forEach.ts new file mode 100644 index 0000000000..91896fb3de --- /dev/null +++ b/src/forEach.ts @@ -0,0 +1,33 @@ +import arrayEach from './.internal/arrayEach.js'; +import baseEach from './.internal/baseEach.js'; + +/** + * Iterates over elements of `collection` and invokes `iteratee` for each element. + * The iteratee is invoked with three arguments: (value, index|key, collection). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * **Note:** As with other "Collections" methods, objects with a "length" + * property are iterated like arrays. To avoid this behavior use `forIn` + * or `forOwn` for object iteration. + * + * @since 0.1.0 + * @alias each + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see forEachRight, forIn, forInRight, forOwn, forOwnRight + * @example + * + * forEach([1, 2], value => console.log(value)) + * // => Logs `1` then `2`. + * + * forEach({ 'a': 1, 'b': 2 }, (value, key) => console.log(key)) + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ +function forEach(collection, iteratee) { + const func = Array.isArray(collection) ? arrayEach : baseEach; + return func(collection, iteratee); +} + +export default forEach; diff --git a/src/forEachRight.ts b/src/forEachRight.ts new file mode 100644 index 0000000000..993be6b3ef --- /dev/null +++ b/src/forEachRight.ts @@ -0,0 +1,25 @@ +import arrayEachRight from './.internal/arrayEachRight.js'; +import baseEachRight from './.internal/baseEachRight.js'; + +/** + * This method is like `forEach` except that it iterates over elements of + * `collection` from right to left. + * + * @since 2.0.0 + * @alias eachRight + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array|Object} Returns `collection`. + * @see forEach, forIn, forInRight, forOwn, forOwnRight + * @example + * + * forEachRight([1, 2], value => console.log(value)) + * // => Logs `2` then `1`. + */ +function forEachRight(collection, iteratee) { + const func = Array.isArray(collection) ? arrayEachRight : baseEachRight; + return func(collection, iteratee); +} + +export default forEachRight; diff --git a/src/forOwn.ts b/src/forOwn.ts new file mode 100644 index 0000000000..65641e55a6 --- /dev/null +++ b/src/forOwn.ts @@ -0,0 +1,31 @@ +/** + * Iterates over own enumerable string keyed properties of an object and + * invokes `iteratee` for each property. The iteratee is invoked with three + * arguments: (value, key, object). Iteratee functions may exit iteration + * early by explicitly returning `false`. + * + * @since 0.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @see forEach, forEachRight, forIn, forInRight, forOwnRight + * @example + * + * function Foo() { + * this.a = 1 + * this.b = 2 + * } + * + * Foo.prototype.c = 3 + * + * forOwn(new Foo, function(value, key) { + * console.log(key) + * }) + * // => Logs 'a' then 'b' (iteration order is not guaranteed). + */ +function forOwn(object, iteratee) { + object = Object(object); + Object.keys(object).forEach((key) => iteratee(object[key], key, object)); +} + +export default forOwn; diff --git a/src/forOwnRight.ts b/src/forOwnRight.ts new file mode 100644 index 0000000000..7409841086 --- /dev/null +++ b/src/forOwnRight.ts @@ -0,0 +1,36 @@ +/** + * This method is like `forOwn` except that it iterates over properties of + * `object` in the opposite order. + * + * @since 2.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns `object`. + * @see forEach, forEachRight, forIn, forInRight, forOwn + * @example + * + * function Foo() { + * this.a = 1 + * this.b = 2 + * } + * + * Foo.prototype.c = 3 + * + * forOwnRight(new Foo, function(value, key) { + * console.log(key) + * }) + * // => Logs 'b' then 'a' assuming `forOwn` logs 'a' then 'b'. + */ +function forOwnRight(object, iteratee) { + if (object == null) { + return; + } + const props = Object.keys(object); + let length = props.length; + while (length--) { + iteratee(object[props[length]], iteratee, object); + } +} + +export default forOwnRight; diff --git a/src/fromEntries.ts b/src/fromEntries.ts new file mode 100644 index 0000000000..dd39c9ad2c --- /dev/null +++ b/src/fromEntries.ts @@ -0,0 +1,25 @@ +/** + * The inverse of `entries`is method returns an object composed + * from key-value `pairs`. + * + * @since 4.0.0 + * @category Array + * @param {Array} pairs The key-value pairs. + * @returns {Object} Returns the new object. + * @example + * + * fromEntries([['a', 1], ['b', 2]]) + * // => { 'a': 1, 'b': 2 } + */ +function fromEntries(pairs) { + const result = {}; + if (pairs == null) { + return result; + } + for (const pair of pairs) { + result[pair[0]] = pair[1]; + } + return result; +} + +export default fromEntries; diff --git a/src/functions.ts b/src/functions.ts new file mode 100644 index 0000000000..f4cc38267d --- /dev/null +++ b/src/functions.ts @@ -0,0 +1,29 @@ +/** + * Creates an array of function property names from own enumerable properties + * of `object`. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The object to inspect. + * @returns {Array} Returns the function names. + * @see functionsIn + * @example + * + * function Foo() { + * this.a = () => 'a' + * this.b = () => 'b' + * } + * + * Foo.prototype.c = () => 'c' + * + * functions(new Foo) + * // => ['a', 'b'] + */ +function functions(object) { + if (object == null) { + return []; + } + return Object.keys(object).filter((key) => typeof object[key] === 'function'); +} + +export default functions; diff --git a/src/get.ts b/src/get.ts new file mode 100644 index 0000000000..55c22e9cb3 --- /dev/null +++ b/src/get.ts @@ -0,0 +1,32 @@ +import baseGet from './.internal/baseGet.js'; + +/** + * Gets the value at `path` of `object`. If the resolved value is + * `undefined`, the `defaultValue` is returned in its place. + * + * @since 3.7.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to get. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @see has, hasIn, set, unset + * @example + * + * const object = { 'a': [{ 'b': { 'c': 3 } }] } + * + * get(object, 'a[0].b.c') + * // => 3 + * + * get(object, ['a', '0', 'b', 'c']) + * // => 3 + * + * get(object, 'a.b.c', 'default') + * // => 'default' + */ +function get(object, path, defaultValue) { + const result = object == null ? undefined : baseGet(object, path); + return result === undefined ? defaultValue : result; +} + +export default get; diff --git a/src/groupBy.ts b/src/groupBy.ts new file mode 100644 index 0000000000..690d215414 --- /dev/null +++ b/src/groupBy.ts @@ -0,0 +1,40 @@ +import baseAssignValue from './.internal/baseAssignValue.js'; +import reduce from './reduce.js'; + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The order of grouped values + * is determined by the order they occur in `collection`. The corresponding + * value of each key is an array of elements responsible for generating the + * key. The iteratee is invoked with one argument: (value). + * + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @example + * + * groupBy([6.1, 4.2, 6.3], Math.floor) + * // => { '4': [4.2], '6': [6.1, 6.3] } + */ +function groupBy(collection, iteratee) { + return reduce( + collection, + (result, value, key) => { + key = iteratee(value); + if (hasOwnProperty.call(result, key)) { + result[key].push(value); + } else { + baseAssignValue(result, key, [value]); + } + return result; + }, + {}, + ); +} + +export default groupBy; diff --git a/src/gt.ts b/src/gt.ts new file mode 100644 index 0000000000..d1367bc27d --- /dev/null +++ b/src/gt.ts @@ -0,0 +1,30 @@ +/** + * Checks if `value` is greater than `other`. + * + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than `other`, + * else `false`. + * @see gte, lt, lte + * @example + * + * gt(3, 1) + * // => true + * + * gt(3, 3) + * // => false + * + * gt(1, 3) + * // => false + */ +function gt(value, other) { + if (!(typeof value === 'string' && typeof other === 'string')) { + value = +value; + other = +other; + } + return value > other; +} + +export default gt; diff --git a/src/gte.ts b/src/gte.ts new file mode 100644 index 0000000000..8005034b04 --- /dev/null +++ b/src/gte.ts @@ -0,0 +1,30 @@ +/** + * Checks if `value` is greater than or equal to `other`. + * + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is greater than or equal to + * `other`, else `false`. + * @see gt, lt, lte + * @example + * + * gte(3, 1) + * // => true + * + * gte(3, 3) + * // => true + * + * gte(1, 3) + * // => false + */ +function gte(value, other) { + if (!(typeof value === 'string' && typeof other === 'string')) { + value = +value; + other = +other; + } + return value >= other; +} + +export default gte; diff --git a/src/has.ts b/src/has.ts new file mode 100644 index 0000000000..433f60a54c --- /dev/null +++ b/src/has.ts @@ -0,0 +1,28 @@ +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Checks if `key` is a direct property of `object`. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The object to query. + * @param {string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + * @see hasIn, hasPath, hasPathIn + * @example + * + * const object = { 'a': { 'b': 2 } } + * const other = create({ 'a': create({ 'b': 2 }) }) + * + * has(object, 'a') + * // => true + * + * has(other, 'a') + * // => false + */ +function has(object, key) { + return object != null && hasOwnProperty.call(object, key); +} + +export default has; diff --git a/src/hasIn.ts b/src/hasIn.ts new file mode 100644 index 0000000000..4cfb6148b0 --- /dev/null +++ b/src/hasIn.ts @@ -0,0 +1,24 @@ +/** + * Checks if `path` is a direct or inherited property of `object`. + * + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {string} key The key to check. + * @returns {boolean} Returns `true` if `key` exists, else `false`. + * @see has, hasPath, hasPathIn + * @example + * + * const object = create({ 'a': create({ 'b': 2 }) }) + * + * hasIn(object, 'a') + * // => true + * + * hasIn(object, 'b') + * // => false + */ +function hasIn(object, key) { + return object != null && key in Object(object); +} + +export default hasIn; diff --git a/src/hasPath.ts b/src/hasPath.ts new file mode 100644 index 0000000000..49f048f439 --- /dev/null +++ b/src/hasPath.ts @@ -0,0 +1,57 @@ +import castPath from './.internal/castPath.js'; +import isArguments from './isArguments.js'; +import isIndex from './.internal/isIndex.js'; +import isLength from './isLength.js'; +import toKey from './.internal/toKey.js'; + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Checks if `path` is a direct property of `object`. + * + * @since 5.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @see has, hasIn, hasPathIn + * @example + * + * const object = { 'a': { 'b': 2 } } + * const other = create({ 'a': create({ 'b': 2 }) }) + * + * hasPath(object, 'a.b') + * // => true + * + * hasPath(object, ['a', 'b']) + * // => true + */ +function hasPath(object, path) { + path = castPath(path, object); + + let index = -1; + let { length } = path; + let result = false; + let key; + + while (++index < length) { + key = toKey(path[index]); + if (!(result = object != null && hasOwnProperty.call(object, key))) { + break; + } + object = object[key]; + } + if (result || ++index !== length) { + return result; + } + length = object == null ? 0 : object.length; + return ( + !!length && + isLength(length) && + isIndex(key, length) && + (Array.isArray(object) || isArguments(object)) + ); +} + +export default hasPath; diff --git a/src/hasPathIn.ts b/src/hasPathIn.ts new file mode 100644 index 0000000000..4533df57d8 --- /dev/null +++ b/src/hasPathIn.ts @@ -0,0 +1,54 @@ +import castPath from './.internal/castPath.js'; +import isArguments from './isArguments.js'; +import isIndex from './.internal/isIndex.js'; +import isLength from './isLength.js'; +import toKey from './.internal/toKey.js'; + +/** + * Checks if `path` is a direct property of `object`. + * + * @since 5.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path to check. + * @returns {boolean} Returns `true` if `path` exists, else `false`. + * @see has, hasIn hasPath + * @example + * + * const object = { 'a': { 'b': 2 } } + * const other = create({ 'a': create({ 'b': 2 }) }) + * + * hasPathIn(object, 'a.b') + * // => true + * + * hasPathIn(object, ['a', 'b']) + * // => true + */ +function hasPathIn(object, path) { + path = castPath(path, object); + + let index = -1; + let { length } = path; + let result = false; + let key; + + while (++index < length) { + key = toKey(path[index]); + if (!(result = object != null && key in Object(object))) { + break; + } + object = object[key]; + } + if (result || ++index !== length) { + return result; + } + length = object == null ? 0 : object.length; + return ( + !!length && + isLength(length) && + isIndex(key, length) && + (Array.isArray(object) || isArguments(object)) + ); +} + +export default hasPathIn; diff --git a/src/head.ts b/src/head.ts new file mode 100644 index 0000000000..061d1d309c --- /dev/null +++ b/src/head.ts @@ -0,0 +1,22 @@ +/** + * Gets the first element of `array`. + * + * @since 0.1.0 + * @alias first + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the first element of `array`. + * @see last + * @example + * + * head([1, 2, 3]) + * // => 1 + * + * head([]) + * // => undefined + */ +function head(array) { + return array != null && array.length ? array[0] : undefined; +} + +export default head; diff --git a/src/inRange.ts b/src/inRange.ts new file mode 100644 index 0000000000..1bf950e7ea --- /dev/null +++ b/src/inRange.ts @@ -0,0 +1,47 @@ +import baseInRange from './.internal/baseInRange.js'; + +/** + * Checks if `number` is between `start` and up to, but not including, `end`. If + * `end` is not specified, it's set to `start` with `start` then set to `0`. + * If `start` is greater than `end` the params are swapped to support + * negative ranges. + * + * @since 3.3.0 + * @category Number + * @param {number} number The number to check. + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @returns {boolean} Returns `true` if `number` is in the range, else `false`. + * @see range, rangeRight + * @example + * + * inRange(3, 2, 4) + * // => true + * + * inRange(4, 8) + * // => true + * + * inRange(4, 2) + * // => false + * + * inRange(2, 2) + * // => false + * + * inRange(1.2, 2) + * // => true + * + * inRange(5.2, 4) + * // => false + * + * inRange(-3, -2, -6) + * // => true + */ +function inRange(number, start, end) { + if (end === undefined) { + end = start; + start = 0; + } + return baseInRange(+number, +start, +end); +} + +export default inRange; diff --git a/src/indexOf.ts b/src/indexOf.ts new file mode 100644 index 0000000000..897c593e2f --- /dev/null +++ b/src/indexOf.ts @@ -0,0 +1,37 @@ +import baseIndexOf from './.internal/baseIndexOf.js'; +import toInteger from './toInteger.js'; + +/** + * Gets the index at which the first occurrence of `value` is found in `array` + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. If `fromIndex` is negative, it's used as the + * offset from the end of `array`. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=0] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * indexOf([1, 2, 1, 2], 2) + * // => 1 + * + * // Search from the `fromIndex`. + * indexOf([1, 2, 1, 2], 2, 2) + * // => 3 + */ +function indexOf(array, value, fromIndex) { + const length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + let index = fromIndex == null ? 0 : toInteger(fromIndex); + if (index < 0) { + index = Math.max(length + index, 0); + } + return baseIndexOf(array, value, index); +} + +export default indexOf; diff --git a/src/initial.ts b/src/initial.ts new file mode 100644 index 0000000000..9a2fb081a4 --- /dev/null +++ b/src/initial.ts @@ -0,0 +1,20 @@ +import slice from './slice.js'; + +/** + * Gets all but the last element of `array`. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * initial([1, 2, 3]) + * // => [1, 2] + */ +function initial(array) { + const length = array == null ? 0 : array.length; + return length ? slice(array, 0, -1) : []; +} + +export default initial; diff --git a/src/intersection.ts b/src/intersection.ts new file mode 100644 index 0000000000..a78c37e6b8 --- /dev/null +++ b/src/intersection.ts @@ -0,0 +1,25 @@ +import map from './map.js'; +import baseIntersection from './.internal/baseIntersection.js'; +import castArrayLikeObject from './.internal/castArrayLikeObject.js'; + +/** + * Creates an array of unique values that are included in all given arrays + * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. The order and references of result values are + * determined by the first array. + * + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * intersection([2, 1], [2, 3]) + * // => [2] + */ +function intersection(...arrays) { + const mapped = map(arrays, castArrayLikeObject); + return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped) : []; +} + +export default intersection; diff --git a/src/intersectionBy.ts b/src/intersectionBy.ts new file mode 100644 index 0000000000..d6eff943e0 --- /dev/null +++ b/src/intersectionBy.ts @@ -0,0 +1,35 @@ +import map from './map.js'; +import baseIntersection from './.internal/baseIntersection.js'; +import castArrayLikeObject from './.internal/castArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `intersection` except that it accepts `iteratee` + * which is invoked for each element of each `arrays` to generate the criterion + * by which they're compared. The order and references of result values are + * determined by the first array. The iteratee is invoked with one argument: + * (value). + * + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor) + * // => [2.1] + */ +function intersectionBy(...arrays) { + let iteratee = last(arrays); + const mapped = map(arrays, castArrayLikeObject); + + if (iteratee === last(mapped)) { + iteratee = undefined; + } else { + mapped.pop(); + } + return mapped.length && mapped[0] === arrays[0] ? baseIntersection(mapped, iteratee) : []; +} + +export default intersectionBy; diff --git a/src/intersectionWith.ts b/src/intersectionWith.ts new file mode 100644 index 0000000000..ae92386b26 --- /dev/null +++ b/src/intersectionWith.ts @@ -0,0 +1,38 @@ +import map from './map.js'; +import baseIntersection from './.internal/baseIntersection.js'; +import castArrayLikeObject from './.internal/castArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `intersection` except that it accepts `comparator` + * which is invoked to compare elements of `arrays`. The order and references + * of result values are determined by the first array. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of intersecting values. + * @example + * + * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + * const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }] + * + * intersectionWith(objects, others, isEqual) + * // => [{ 'x': 1, 'y': 2 }] + */ +function intersectionWith(...arrays) { + let comparator = last(arrays); + const mapped = map(arrays, castArrayLikeObject); + + comparator = typeof comparator === 'function' ? comparator : undefined; + if (comparator) { + mapped.pop(); + } + return mapped.length && mapped[0] === arrays[0] + ? baseIntersection(mapped, undefined, comparator) + : []; +} + +export default intersectionWith; diff --git a/src/invert.ts b/src/invert.ts new file mode 100644 index 0000000000..e730613e79 --- /dev/null +++ b/src/invert.ts @@ -0,0 +1,31 @@ +const toString = Object.prototype.toString; + +/** + * Creates an object composed of the inverted keys and values of `object`. + * If `object` contains duplicate values, subsequent values overwrite + * property assignments of previous values. + * + * @since 0.7.0 + * @category Object + * @param {Object} object The object to invert. + * @returns {Object} Returns the new inverted object. + * @example + * + * const object = { 'a': 1, 'b': 2, 'c': 1 } + * + * invert(object) + * // => { '1': 'c', '2': 'b' } + */ +function invert(object) { + const result = {}; + Object.keys(object).forEach((key) => { + let value = object[key]; + if (value != null && typeof value.toString !== 'function') { + value = toString.call(value); + } + result[value] = key; + }); + return result; +} + +export default invert; diff --git a/src/invertBy.ts b/src/invertBy.ts new file mode 100644 index 0000000000..30ba885632 --- /dev/null +++ b/src/invertBy.ts @@ -0,0 +1,36 @@ +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * This method is like `invert` except that the inverted object is generated + * from the results of running each element of `object` thru `iteratee`. The + * corresponding inverted value of each inverted key is an array of keys + * responsible for generating the inverted value. The iteratee is invoked + * with one argument: (value). + * + * @since 4.1.0 + * @category Object + * @param {Object} object The object to invert. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Object} Returns the new inverted object. + * @example + * + * const object = { 'a': 1, 'b': 2, 'c': 1 } + * + * invertBy(object, value => `group${value}`) + * // => { 'group1': ['a', 'c'], 'group2': ['b'] } + */ +function invertBy(object, iteratee) { + const result = {}; + Object.keys(object).forEach((key) => { + const value = iteratee(object[key]); + if (hasOwnProperty.call(result, value)) { + result[value].push(key); + } else { + result[value] = [key]; + } + }); + return result; +} + +export default invertBy; diff --git a/src/invoke.ts b/src/invoke.ts new file mode 100644 index 0000000000..968b9395a6 --- /dev/null +++ b/src/invoke.ts @@ -0,0 +1,29 @@ +import castPath from './.internal/castPath.js'; +import last from './last.js'; +import parent from './.internal/parent.js'; +import toKey from './.internal/toKey.js'; + +/** + * Invokes the method at `path` of `object`. + * + * @since 4.0.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the method to invoke. + * @param {Array} [args] The arguments to invoke the method with. + * @returns {*} Returns the result of the invoked method. + * @example + * + * const object = { 'a': [{ 'b': { 'c': [1, 2, 3, 4] } }] } + * + * invoke(object, 'a[0].b.c.slice', [1, 3]) + * // => [2, 3] + */ +function invoke(object, path, args) { + path = castPath(path, object); + object = parent(object, path); + const func = object == null ? object : object[toKey(last(path))]; + return func == null ? undefined : func.apply(object, args); +} + +export default invoke; diff --git a/src/invokeMap.ts b/src/invokeMap.ts new file mode 100644 index 0000000000..2e29479214 --- /dev/null +++ b/src/invokeMap.ts @@ -0,0 +1,37 @@ +import baseEach from './.internal/baseEach.js'; +import invoke from './invoke.js'; +import isArrayLike from './isArrayLike.js'; + +/** + * Invokes the method at `path` of each element in `collection`, returning + * an array of the results of each invoked method. Any additional arguments + * are provided to each invoked method. If `path` is a function, it's invoked + * for, and `this` bound to, each element in `collection`. + * + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array|Function|string} path The path of the method to invoke or + * the function invoked per iteration. + * @param {Array} [args] The arguments to invoke each method with. + * @returns {Array} Returns the array of results. + * @example + * + * invokeMap([[5, 1, 7], [3, 2, 1]], 'sort') + * // => [[1, 5, 7], [1, 2, 3]] + * + * invokeMap([123, 456], String.prototype.split, ['']) + * // => [['1', '2', '3'], ['4', '5', '6']] + */ +function invokeMap(collection, path, args) { + let index = -1; + const isFunc = typeof path === 'function'; + const result = isArrayLike(collection) ? new Array(collection.length) : []; + + baseEach(collection, (value) => { + result[++index] = isFunc ? path.apply(value, args) : invoke(value, path, args); + }); + return result; +} + +export default invokeMap; diff --git a/src/isArguments.ts b/src/isArguments.ts new file mode 100644 index 0000000000..d5bbd074d0 --- /dev/null +++ b/src/isArguments.ts @@ -0,0 +1,23 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * Checks if `value` is likely an `arguments` object. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an `arguments` object, else `false`. + * @example + * + * isArguments(function() { return arguments }()) + * // => true + * + * isArguments([1, 2, 3]) + * // => false + */ +function isArguments(value) { + return isObjectLike(value) && getTag(value) === '[object Arguments]'; +} + +export default isArguments; diff --git a/src/isArrayBuffer.ts b/src/isArrayBuffer.ts new file mode 100644 index 0000000000..5b81958d78 --- /dev/null +++ b/src/isArrayBuffer.ts @@ -0,0 +1,27 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; +import nodeTypes from './.internal/nodeTypes.js'; + +/* Node.js helper references. */ +const nodeIsArrayBuffer = nodeTypes && nodeTypes.isArrayBuffer; + +/** + * Checks if `value` is classified as an `ArrayBuffer` object. + * + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array buffer, else `false`. + * @example + * + * isArrayBuffer(new ArrayBuffer(2)) + * // => true + * + * isArrayBuffer(new Array(2)) + * // => false + */ +const isArrayBuffer = nodeIsArrayBuffer + ? (value) => nodeIsArrayBuffer(value) + : (value) => isObjectLike(value) && getTag(value) === '[object ArrayBuffer]'; + +export default isArrayBuffer; diff --git a/src/isArrayLike.ts b/src/isArrayLike.ts new file mode 100644 index 0000000000..102bfe4641 --- /dev/null +++ b/src/isArrayLike.ts @@ -0,0 +1,30 @@ +import isLength from './isLength.js'; + +/** + * Checks if `value` is array-like. A value is considered array-like if it's + * not a function and has a `value.length` that's an integer greater than or + * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is array-like, else `false`. + * @example + * + * isArrayLike([1, 2, 3]) + * // => true + * + * isArrayLike(document.body.children) + * // => true + * + * isArrayLike('abc') + * // => true + * + * isArrayLike(Function) + * // => false + */ +function isArrayLike(value) { + return value != null && typeof value !== 'function' && isLength(value.length); +} + +export default isArrayLike; diff --git a/src/isArrayLikeObject.ts b/src/isArrayLikeObject.ts new file mode 100644 index 0000000000..1b975cb837 --- /dev/null +++ b/src/isArrayLikeObject.ts @@ -0,0 +1,31 @@ +import isArrayLike from './isArrayLike.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * This method is like `isArrayLike` except that it also checks if `value` + * is an object. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an array-like object, + * else `false`. + * @example + * + * isArrayLikeObject([1, 2, 3]) + * // => true + * + * isArrayLikeObject(document.body.children) + * // => true + * + * isArrayLikeObject('abc') + * // => false + * + * isArrayLikeObject(Function) + * // => false + */ +function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); +} + +export default isArrayLikeObject; diff --git a/src/isBoolean.ts b/src/isBoolean.ts new file mode 100644 index 0000000000..328b09f183 --- /dev/null +++ b/src/isBoolean.ts @@ -0,0 +1,27 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * Checks if `value` is classified as a boolean primitive or object. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a boolean, else `false`. + * @example + * + * isBoolean(false) + * // => true + * + * isBoolean(null) + * // => false + */ +function isBoolean(value) { + return ( + value === true || + value === false || + (isObjectLike(value) && getTag(value) === '[object Boolean]') + ); +} + +export default isBoolean; diff --git a/src/isBuffer.ts b/src/isBuffer.ts new file mode 100644 index 0000000000..76e2e9313a --- /dev/null +++ b/src/isBuffer.ts @@ -0,0 +1,23 @@ +import root from './.internal/root.js'; + +/* Built-in method references for those with the same name as other `lodash` methods. */ +const nativeIsBuffer = root?.Buffer?.isBuffer; + +/** + * Checks if `value` is a buffer. + * + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a buffer, else `false`. + * @example + * + * isBuffer(Buffer.alloc(2)) + * // => true + * + * isBuffer(new Uint8Array(2)) + * // => false + */ +const isBuffer = typeof nativeIsBuffer === 'function' ? nativeIsBuffer : () => false; + +export default isBuffer; diff --git a/src/isDate.ts b/src/isDate.ts new file mode 100644 index 0000000000..a68c82afa8 --- /dev/null +++ b/src/isDate.ts @@ -0,0 +1,27 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; +import nodeTypes from './.internal/nodeTypes.js'; + +/* Node.js helper references. */ +const nodeIsDate = nodeTypes && nodeTypes.isDate; + +/** + * Checks if `value` is classified as a `Date` object. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a date object, else `false`. + * @example + * + * isDate(new Date) + * // => true + * + * isDate('Mon April 23 2012') + * // => false + */ +const isDate = nodeIsDate + ? (value) => nodeIsDate(value) + : (value) => isObjectLike(value) && getTag(value) === '[object Date]'; + +export default isDate; diff --git a/src/isElement.ts b/src/isElement.ts new file mode 100644 index 0000000000..7b88528d52 --- /dev/null +++ b/src/isElement.ts @@ -0,0 +1,23 @@ +import isObjectLike from './isObjectLike.js'; +import isPlainObject from './isPlainObject.js'; + +/** + * Checks if `value` is likely a DOM element. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`. + * @example + * + * isElement(document.body) + * // => true + * + * isElement('') + * // => false + */ +function isElement(value) { + return isObjectLike(value) && value.nodeType === 1 && !isPlainObject(value); +} + +export default isElement; diff --git a/src/isEmpty.ts b/src/isEmpty.ts new file mode 100644 index 0000000000..ca9afa356a --- /dev/null +++ b/src/isEmpty.ts @@ -0,0 +1,75 @@ +import getTag from './.internal/getTag.js'; +import isArguments from './isArguments.js'; +import isArrayLike from './isArrayLike.js'; +import isBuffer from './isBuffer.js'; +import isPrototype from './.internal/isPrototype.js'; +import isTypedArray from './isTypedArray.js'; + +/** Used to check objects for own properties. */ +const hasOwnProperty = Object.prototype.hasOwnProperty; + +/** + * Checks if `value` is an empty object, collection, map, or set. + * + * Objects are considered empty if they have no own enumerable string keyed + * properties. + * + * Array-like values such as `arguments` objects, arrays, buffers, strings, or + * jQuery-like collections are considered empty if they have a `length` of `0`. + * Similarly, maps and sets are considered empty if they have a `size` of `0`. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is empty, else `false`. + * @example + * + * isEmpty(null) + * // => true + * + * isEmpty(true) + * // => true + * + * isEmpty(1) + * // => true + * + * isEmpty([1, 2, 3]) + * // => false + * + * isEmpty('abc') + * // => false + * + * isEmpty({ 'a': 1 }) + * // => false + */ +function isEmpty(value) { + if (value == null) { + return true; + } + if ( + isArrayLike(value) && + (Array.isArray(value) || + typeof value === 'string' || + typeof value.splice === 'function' || + isBuffer(value) || + isTypedArray(value) || + isArguments(value)) + ) { + return !value.length; + } + const tag = getTag(value); + if (tag === '[object Map]' || tag === '[object Set]') { + return !value.size; + } + if (isPrototype(value)) { + return !Object.keys(value).length; + } + for (const key in value) { + if (hasOwnProperty.call(value, key)) { + return false; + } + } + return true; +} + +export default isEmpty; diff --git a/src/isEqualWith.ts b/src/isEqualWith.ts new file mode 100644 index 0000000000..7c7b3e0d79 --- /dev/null +++ b/src/isEqualWith.ts @@ -0,0 +1,39 @@ +import baseIsEqual from './.internal/baseIsEqual.js'; + +/** + * This method is like `isEqual` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with up to + * six arguments: (objValue, othValue [, index|key, object, other, stack]). + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if the values are equivalent, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value) + * } + * + * function customizer(objValue, othValue) { + * if (isGreeting(objValue) && isGreeting(othValue)) { + * return true + * } + * } + * + * const array = ['hello', 'goodbye'] + * const other = ['hi', 'goodbye'] + * + * isEqualWith(array, other, customizer) + * // => true + */ +function isEqualWith(value, other, customizer) { + customizer = typeof customizer === 'function' ? customizer : undefined; + const result = customizer ? customizer(value, other) : undefined; + return result === undefined ? baseIsEqual(value, other, undefined, customizer) : !!result; +} + +export default isEqualWith; diff --git a/src/isError.ts b/src/isError.ts new file mode 100644 index 0000000000..34a0b27702 --- /dev/null +++ b/src/isError.ts @@ -0,0 +1,35 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; +import isPlainObject from './isPlainObject.js'; + +/** + * Checks if `value` is an `Error`, `EvalError`, `RangeError`, `ReferenceError`, + * `SyntaxError`, `TypeError`, or `URIError` object. + * + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an error object, else `false`. + * @example + * + * isError(new Error) + * // => true + * + * isError(Error) + * // => false + */ +function isError(value) { + if (!isObjectLike(value)) { + return false; + } + const tag = getTag(value); + return ( + tag === '[object Error]' || + tag === '[object DOMException]' || + (typeof value.message === 'string' && + typeof value.name === 'string' && + !isPlainObject(value)) + ); +} + +export default isError; diff --git a/src/isFunction.ts b/src/isFunction.ts new file mode 100644 index 0000000000..07a03754ec --- /dev/null +++ b/src/isFunction.ts @@ -0,0 +1,32 @@ +/** + * Checks if `value` is classified as a `Function` object. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a function, else `false`. + * @example + * + * isFunction(class Any{}) + * // => true + * + * isFunction(() => {}) + * // => true + * + * isFunction(async () => {}) + * // => true + * + * isFunction(function * Any() {}) + * // => true + * + * isFunction(Math.round) + * // => true + * + * isFunction(/abc/) + * // => false + */ +function isFunction(value) { + return typeof value === 'function'; +} + +export default isFunction; diff --git a/src/isLength.ts b/src/isLength.ts new file mode 100644 index 0000000000..2f0b0fde46 --- /dev/null +++ b/src/isLength.ts @@ -0,0 +1,32 @@ +/** Used as references for various `Number` constants. */ +const MAX_SAFE_INTEGER = 9007199254740991; + +/** + * Checks if `value` is a valid array-like length. + * + * **Note:** This method is loosely based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a valid length, else `false`. + * @example + * + * isLength(3) + * // => true + * + * isLength(Number.MIN_VALUE) + * // => false + * + * isLength(Infinity) + * // => false + * + * isLength('3') + * // => false + */ +function isLength(value) { + return typeof value === 'number' && value > -1 && value % 1 === 0 && value <= MAX_SAFE_INTEGER; +} + +export default isLength; diff --git a/src/isMap.ts b/src/isMap.ts new file mode 100644 index 0000000000..39fa070950 --- /dev/null +++ b/src/isMap.ts @@ -0,0 +1,27 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; +import nodeTypes from './.internal/nodeTypes.js'; + +/* Node.js helper references. */ +const nodeIsMap = nodeTypes && nodeTypes.isMap; + +/** + * Checks if `value` is classified as a `Map` object. + * + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a map, else `false`. + * @example + * + * isMap(new Map) + * // => true + * + * isMap(new WeakMap) + * // => false + */ +const isMap = nodeIsMap + ? (value) => nodeIsMap(value) + : (value) => isObjectLike(value) && getTag(value) === '[object Map]'; + +export default isMap; diff --git a/src/isMatch.ts b/src/isMatch.ts new file mode 100644 index 0000000000..1d28085bc9 --- /dev/null +++ b/src/isMatch.ts @@ -0,0 +1,34 @@ +import baseIsMatch from './.internal/baseIsMatch.js'; +import getMatchData from './.internal/getMatchData.js'; + +/** + * Performs a partial deep comparison between `object` and `source` to + * determine if `object` contains equivalent property values. + * + * **Note:** This method is equivalent to `matches` when `source` is + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `isEqual` + * for a list of supported value comparisons. + * + * @since 3.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * const object = { 'a': 1, 'b': 2 } + * + * isMatch(object, { 'b': 2 }) + * // => true + * + * isMatch(object, { 'b': 1 }) + * // => false + */ +function isMatch(object, source) { + return object === source || baseIsMatch(object, source, getMatchData(source)); +} + +export default isMatch; diff --git a/src/isMatchWith.ts b/src/isMatchWith.ts new file mode 100644 index 0000000000..f2ec262470 --- /dev/null +++ b/src/isMatchWith.ts @@ -0,0 +1,39 @@ +import baseIsMatch from './.internal/baseIsMatch.js'; +import getMatchData from './.internal/getMatchData.js'; + +/** + * This method is like `isMatch` except that it accepts `customizer` which + * is invoked to compare values. If `customizer` returns `undefined`, comparisons + * are handled by the method instead. The `customizer` is invoked with five + * arguments: (objValue, srcValue, index|key, object, source). + * + * @since 4.0.0 + * @category Lang + * @param {Object} object The object to inspect. + * @param {Object} source The object of property values to match. + * @param {Function} [customizer] The function to customize comparisons. + * @returns {boolean} Returns `true` if `object` is a match, else `false`. + * @example + * + * function isGreeting(value) { + * return /^h(?:i|ello)$/.test(value) + * } + * + * function customizer(objValue, srcValue) { + * if (isGreeting(objValue) && isGreeting(srcValue)) { + * return true + * } + * } + * + * const object = { 'greeting': 'hello' } + * const source = { 'greeting': 'hi' } + * + * isMatchWith(object, source, customizer) + * // => true + */ +function isMatchWith(object, source, customizer) { + customizer = typeof customizer === 'function' ? customizer : undefined; + return baseIsMatch(object, source, getMatchData(source), customizer); +} + +export default isMatchWith; diff --git a/src/isNative.ts b/src/isNative.ts new file mode 100644 index 0000000000..4b7a6cc07d --- /dev/null +++ b/src/isNative.ts @@ -0,0 +1,37 @@ +import isObject from './isObject.js'; + +/** + * Used to match `RegExp` + * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns). + */ +const reRegExpChar = /[\\^$.*+?()[\]{}|]/g; + +/** Used to detect if a method is native. */ +const reIsNative = RegExp( + `^${Function.prototype.toString + .call(Object.prototype.hasOwnProperty) + .replace(reRegExpChar, '\\$&') + .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?')}$`, +); + +/** + * Checks if `value` is a pristine native function. + * + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a native function, + * else `false`. + * @example + * + * isNative(Array.prototype.push) + * // => true + * + * isNative(isDate) + * // => false + */ +function isNative(value) { + return isObject(value) && reIsNative.test(value); +} + +export default isNative; diff --git a/src/isNil.ts b/src/isNil.ts new file mode 100644 index 0000000000..85559616d5 --- /dev/null +++ b/src/isNil.ts @@ -0,0 +1,23 @@ +/** + * Checks if `value` is `null` or `undefined`. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is nullish, else `false`. + * @example + * + * isNil(null) + * // => true + * + * isNil(void 0) + * // => true + * + * isNil(NaN) + * // => false + */ +function isNil(value) { + return value == null; +} + +export default isNil; diff --git a/src/isNull.ts b/src/isNull.ts new file mode 100644 index 0000000000..d2e068e3e1 --- /dev/null +++ b/src/isNull.ts @@ -0,0 +1,20 @@ +/** + * Checks if `value` is `null`. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `null`, else `false`. + * @example + * + * isNull(null) + * // => true + * + * isNull(void 0) + * // => false + */ +function isNull(value) { + return value === null; +} + +export default isNull; diff --git a/src/isNumber.ts b/src/isNumber.ts new file mode 100644 index 0000000000..5276c76028 --- /dev/null +++ b/src/isNumber.ts @@ -0,0 +1,35 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * Checks if `value` is classified as a `Number` primitive or object. + * + * **Note:** To exclude `Infinity`, `-Infinity`, and `NaN`, which are + * classified as numbers, use the `Number.isFinite` method. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a number, else `false`. + * @see isInteger, toInteger, toNumber + * @example + * + * isNumber(3) + * // => true + * + * isNumber(Number.MIN_VALUE) + * // => true + * + * isNumber(Infinity) + * // => true + * + * isNumber('3') + * // => false + */ +function isNumber(value) { + return ( + typeof value === 'number' || (isObjectLike(value) && getTag(value) === '[object Number]') + ); +} + +export default isNumber; diff --git a/src/isObject.ts b/src/isObject.ts new file mode 100644 index 0000000000..f5bb8e8065 --- /dev/null +++ b/src/isObject.ts @@ -0,0 +1,29 @@ +/** + * Checks if `value` is the + * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types) + * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`) + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is an object, else `false`. + * @example + * + * isObject({}) + * // => true + * + * isObject([1, 2, 3]) + * // => true + * + * isObject(Function) + * // => true + * + * isObject(null) + * // => false + */ +function isObject(value) { + const type = typeof value; + return value != null && (type === 'object' || type === 'function'); +} + +export default isObject; diff --git a/src/isObjectLike.ts b/src/isObjectLike.ts new file mode 100644 index 0000000000..7ed0773005 --- /dev/null +++ b/src/isObjectLike.ts @@ -0,0 +1,27 @@ +/** + * Checks if `value` is object-like. A value is object-like if it's not `null` + * and has a `typeof` result of "object". + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is object-like, else `false`. + * @example + * + * isObjectLike({}) + * // => true + * + * isObjectLike([1, 2, 3]) + * // => true + * + * isObjectLike(Function) + * // => false + * + * isObjectLike(null) + * // => false + */ +function isObjectLike(value) { + return typeof value === 'object' && value !== null; +} + +export default isObjectLike; diff --git a/src/isPlainObject.ts b/src/isPlainObject.ts new file mode 100644 index 0000000000..dfcf0365ce --- /dev/null +++ b/src/isPlainObject.ts @@ -0,0 +1,44 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * Checks if `value` is a plain object, that is, an object created by the + * `Object` constructor or one with a `[[Prototype]]` of `null`. + * + * @since 0.8.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a plain object, else `false`. + * @example + * + * function Foo() { + * this.a = 1 + * } + * + * isPlainObject(new Foo) + * // => false + * + * isPlainObject([1, 2, 3]) + * // => false + * + * isPlainObject({ 'x': 0, 'y': 0 }) + * // => true + * + * isPlainObject(Object.create(null)) + * // => true + */ +function isPlainObject(value) { + if (!isObjectLike(value) || getTag(value) !== '[object Object]') { + return false; + } + if (Object.getPrototypeOf(value) === null) { + return true; + } + let proto = value; + while (Object.getPrototypeOf(proto) !== null) { + proto = Object.getPrototypeOf(proto); + } + return Object.getPrototypeOf(value) === proto; +} + +export default isPlainObject; diff --git a/src/isRegExp.ts b/src/isRegExp.ts new file mode 100644 index 0000000000..773506621b --- /dev/null +++ b/src/isRegExp.ts @@ -0,0 +1,27 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; +import nodeTypes from './.internal/nodeTypes.js'; + +/* Node.js helper references. */ +const nodeIsRegExp = nodeTypes && nodeTypes.isRegExp; + +/** + * Checks if `value` is classified as a `RegExp` object. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a regexp, else `false`. + * @example + * + * isRegExp(/abc/) + * // => true + * + * isRegExp('/abc/') + * // => false + */ +const isRegExp = nodeIsRegExp + ? (value) => nodeIsRegExp(value) + : (value) => isObjectLike(value) && getTag(value) === '[object RegExp]'; + +export default isRegExp; diff --git a/src/isSet.ts b/src/isSet.ts new file mode 100644 index 0000000000..67c2862875 --- /dev/null +++ b/src/isSet.ts @@ -0,0 +1,27 @@ +import getTag from './.internal/getTag.js'; +import nodeTypes from './.internal/nodeTypes.js'; +import isObjectLike from './isObjectLike.js'; + +/* Node.js helper references. */ +const nodeIsSet = nodeTypes && nodeTypes.isSet; + +/** + * Checks if `value` is classified as a `Set` object. + * + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a set, else `false`. + * @example + * + * isSet(new Set) + * // => true + * + * isSet(new WeakSet) + * // => false + */ +const isSet = nodeIsSet + ? (value) => nodeIsSet(value) + : (value) => isObjectLike(value) && getTag(value) === '[object Set]'; + +export default isSet; diff --git a/src/isString.ts b/src/isString.ts new file mode 100644 index 0000000000..9b7c6b566f --- /dev/null +++ b/src/isString.ts @@ -0,0 +1,29 @@ +import getTag from './.internal/getTag.js'; + +/** + * Checks if `value` is classified as a `String` primitive or object. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a string, else `false`. + * @example + * + * isString('abc') + * // => true + * + * isString(1) + * // => false + */ +function isString(value) { + const type = typeof value; + return ( + type === 'string' || + (type === 'object' && + value != null && + !Array.isArray(value) && + getTag(value) === '[object String]') + ); +} + +export default isString; diff --git a/src/isSymbol.ts b/src/isSymbol.ts new file mode 100644 index 0000000000..859e47a4d7 --- /dev/null +++ b/src/isSymbol.ts @@ -0,0 +1,26 @@ +import getTag from './.internal/getTag.js'; + +/** + * Checks if `value` is classified as a `Symbol` primitive or object. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a symbol, else `false`. + * @example + * + * isSymbol(Symbol.iterator) + * // => true + * + * isSymbol('abc') + * // => false + */ +function isSymbol(value) { + const type = typeof value; + return ( + type === 'symbol' || + (type === 'object' && value != null && getTag(value) === '[object Symbol]') + ); +} + +export default isSymbol; diff --git a/src/isTypedArray.ts b/src/isTypedArray.ts new file mode 100644 index 0000000000..28db4518e9 --- /dev/null +++ b/src/isTypedArray.ts @@ -0,0 +1,30 @@ +import getTag from './.internal/getTag.js'; +import nodeTypes from './.internal/nodeTypes.js'; +import isObjectLike from './isObjectLike.js'; + +/** Used to match `toStringTag` values of typed arrays. */ +const reTypedTag = /^\[object (?:Float(?:32|64)|(?:Int|Uint)(?:8|16|32)|Uint8Clamped)Array\]$/; + +/* Node.js helper references. */ +const nodeIsTypedArray = nodeTypes && nodeTypes.isTypedArray; + +/** + * Checks if `value` is classified as a typed array. + * + * @since 3.0.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a typed array, else `false`. + * @example + * + * isTypedArray(new Uint8Array) + * // => true + * + * isTypedArray([]) + * // => false + */ +const isTypedArray = nodeIsTypedArray + ? (value) => nodeIsTypedArray(value) + : (value) => isObjectLike(value) && reTypedTag.test(getTag(value)); + +export default isTypedArray; diff --git a/src/isUndefined.ts b/src/isUndefined.ts new file mode 100644 index 0000000000..b178950a9b --- /dev/null +++ b/src/isUndefined.ts @@ -0,0 +1,20 @@ +/** + * Checks if `value` is `undefined`. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. + * @example + * + * isUndefined(void 0) + * // => true + * + * isUndefined(null) + * // => false + */ +function isUndefined(value) { + return value === undefined; +} + +export default isUndefined; diff --git a/src/isWeakMap.ts b/src/isWeakMap.ts new file mode 100644 index 0000000000..c5a4a231d3 --- /dev/null +++ b/src/isWeakMap.ts @@ -0,0 +1,23 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * Checks if `value` is classified as a `WeakMap` object. + * + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak map, else `false`. + * @example + * + * isWeakMap(new WeakMap) + * // => true + * + * isWeakMap(new Map) + * // => false + */ +function isWeakMap(value) { + return isObjectLike(value) && getTag(value) === '[object WeakMap]'; +} + +export default isWeakMap; diff --git a/src/isWeakSet.ts b/src/isWeakSet.ts new file mode 100644 index 0000000000..5995369b7d --- /dev/null +++ b/src/isWeakSet.ts @@ -0,0 +1,23 @@ +import getTag from './.internal/getTag.js'; +import isObjectLike from './isObjectLike.js'; + +/** + * Checks if `value` is classified as a `WeakSet` object. + * + * @since 4.3.0 + * @category Lang + * @param {*} value The value to check. + * @returns {boolean} Returns `true` if `value` is a weak set, else `false`. + * @example + * + * isWeakSet(new WeakSet) + * // => true + * + * isWeakSet(new Set) + * // => false + */ +function isWeakSet(value) { + return isObjectLike(value) && getTag(value) === '[object WeakSet]'; +} + +export default isWeakSet; diff --git a/src/kebabCase.ts b/src/kebabCase.ts new file mode 100644 index 0000000000..f1e0d30b15 --- /dev/null +++ b/src/kebabCase.ts @@ -0,0 +1,30 @@ +import words from './words.js'; +import toString from './toString.js'; + +/** + * Converts `string` to + * [kebab case](https://en.wikipedia.org/wiki/Letter_case#Special_case_styles). + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the kebab cased string. + * @see camelCase, lowerCase, snakeCase, startCase, upperCase, upperFirst + * @example + * + * kebabCase('Foo Bar') + * // => 'foo-bar' + * + * kebabCase('fooBar') + * // => 'foo-bar' + * + * kebabCase('__FOO_BAR__') + * // => 'foo-bar' + */ +const kebabCase = (string) => + words(toString(string).replace(/['\u2019]/g, '')).reduce( + (result, word, index) => result + (index ? '-' : '') + word.toLowerCase(), + '', + ); + +export default kebabCase; diff --git a/src/keyBy.ts b/src/keyBy.ts new file mode 100644 index 0000000000..5a516b5257 --- /dev/null +++ b/src/keyBy.ts @@ -0,0 +1,37 @@ +import baseAssignValue from './.internal/baseAssignValue.js'; +import reduce from './reduce.js'; + +/** + * Creates an object composed of keys generated from the results of running + * each element of `collection` thru `iteratee`. The corresponding value of + * each key is the last element responsible for generating the key. The + * iteratee is invoked with one argument: (value). + * + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The iteratee to transform keys. + * @returns {Object} Returns the composed aggregate object. + * @see groupBy, partition + * @example + * + * const array = [ + * { 'dir': 'left', 'code': 97 }, + * { 'dir': 'right', 'code': 100 } + * ] + * + * keyBy(array, ({ code }) => String.fromCharCode(code)) + * // => { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } } + */ +function keyBy(collection: any, iteratee: Function) { + return reduce( + collection, + (result: object, value: any) => { + baseAssignValue(result, iteratee(value), value); + return result; + }, + {}, + ); +} + +export default keyBy; diff --git a/src/keys.ts b/src/keys.ts new file mode 100644 index 0000000000..af973bf16d --- /dev/null +++ b/src/keys.ts @@ -0,0 +1,35 @@ +import arrayLikeKeys from './.internal/arrayLikeKeys.js'; +import isArrayLike from './isArrayLike.js'; + +/** + * Creates an array of the own enumerable property names of `object`. + * + * **Note:** Non-object values are coerced to objects. See the + * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys) + * for more details. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @see values, valuesIn + * @example + * + * function Foo() { + * this.a = 1 + * this.b = 2 + * } + * + * Foo.prototype.c = 3 + * + * keys(new Foo) + * // => ['a', 'b'] (iteration order is not guaranteed) + * + * keys('hi') + * // => ['0', '1'] + */ +function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : Object.keys(Object(object)); +} + +export default keys; diff --git a/src/keysIn.ts b/src/keysIn.ts new file mode 100644 index 0000000000..367d775a81 --- /dev/null +++ b/src/keysIn.ts @@ -0,0 +1,31 @@ +/** + * Creates an array of the own and inherited enumerable property names of `object`. + * + * + * @static + * @memberOf _ + * @since 3.0.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property names. + * @example + * + * function Foo() { + * this.a = 1; + * this.b = 2; + * } + * + * Foo.prototype.c = 3; + * + * _.keysIn(new Foo); + * // => ['a', 'b', 'c'] (iteration order is not guaranteed) + */ +function keysIn(object) { + const result = []; + for (const key in object) { + result.push(key); + } + return result; +} + +export default keysIn; diff --git a/src/last.ts b/src/last.ts new file mode 100644 index 0000000000..4656704378 --- /dev/null +++ b/src/last.ts @@ -0,0 +1,18 @@ +/** + * Gets the last element of `array`. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @returns {*} Returns the last element of `array`. + * @example + * + * last([1, 2, 3]) + * // => 3 + */ +function last(array) { + const length = array == null ? 0 : array.length; + return length ? array[length - 1] : undefined; +} + +export default last; diff --git a/src/lastIndexOf.ts b/src/lastIndexOf.ts new file mode 100644 index 0000000000..7623b175c8 --- /dev/null +++ b/src/lastIndexOf.ts @@ -0,0 +1,40 @@ +import baseFindIndex from './.internal/baseFindIndex.js'; +import baseIsNaN from './.internal/baseIsNaN.js'; +import strictLastIndexOf from './.internal/strictLastIndexOf.js'; +import toInteger from './toInteger.js'; + +/** + * This method is like `indexOf` except that it iterates over elements of + * `array` from right to left. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @param {number} [fromIndex=array.length-1] The index to search from. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * lastIndexOf([1, 2, 1, 2], 2) + * // => 3 + * + * // Search from the `fromIndex`. + * lastIndexOf([1, 2, 1, 2], 2, 2) + * // => 1 + */ +function lastIndexOf(array, value, fromIndex) { + const length = array == null ? 0 : array.length; + if (!length) { + return -1; + } + let index = length; + if (fromIndex !== undefined) { + index = toInteger(fromIndex); + index = index < 0 ? Math.max(length + index, 0) : Math.min(index, length - 1); + } + return value === value + ? strictLastIndexOf(array, value, index) + : baseFindIndex(array, baseIsNaN, index, true); +} + +export default lastIndexOf; diff --git a/src/lowerCase.ts b/src/lowerCase.ts new file mode 100644 index 0000000000..533c1e0a29 --- /dev/null +++ b/src/lowerCase.ts @@ -0,0 +1,31 @@ +import words from './words.js'; +import toString from './toString.js'; + +const reQuotes = /['\u2019]/g; + +/** + * Converts `string`, as space separated words, to lower case. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the lower cased string. + * @see camelCase, kebabCase, snakeCase, startCase, upperCase, upperFirst + * @example + * + * lowerCase('--Foo-Bar--') + * // => 'foo bar' + * + * lowerCase('fooBar') + * // => 'foo bar' + * + * lowerCase('__FOO_BAR__') + * // => 'foo bar' + */ +const lowerCase = (string) => + words(toString(string).replace(reQuotes, '')).reduce( + (result, word, index) => result + (index ? ' ' : '') + word.toLowerCase(), + '', + ); + +export default lowerCase; diff --git a/src/lowerFirst.ts b/src/lowerFirst.ts new file mode 100644 index 0000000000..ad63b84d50 --- /dev/null +++ b/src/lowerFirst.ts @@ -0,0 +1,20 @@ +import createCaseFirst from './.internal/createCaseFirst.js'; + +/** + * Converts the first character of `string` to lower case. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @example + * + * lowerFirst('Fred') + * // => 'fred' + * + * lowerFirst('FRED') + * // => 'fRED' + */ +const lowerFirst = createCaseFirst('toLowerCase'); + +export default lowerFirst; diff --git a/src/lt.ts b/src/lt.ts new file mode 100644 index 0000000000..29d7a2e770 --- /dev/null +++ b/src/lt.ts @@ -0,0 +1,30 @@ +/** + * Checks if `value` is less than `other`. + * + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than `other`, + * else `false`. + * @see gt, gte, lte + * @example + * + * lt(1, 3) + * // => true + * + * lt(3, 3) + * // => false + * + * lt(3, 1) + * // => false + */ +function lt(value, other) { + if (!(typeof value === 'string' && typeof other === 'string')) { + value = +value; + other = +other; + } + return value < other; +} + +export default lt; diff --git a/src/lte.ts b/src/lte.ts new file mode 100644 index 0000000000..2bd548df98 --- /dev/null +++ b/src/lte.ts @@ -0,0 +1,30 @@ +/** + * Checks if `value` is less than or equal to `other`. + * + * @since 3.9.0 + * @category Lang + * @param {*} value The value to compare. + * @param {*} other The other value to compare. + * @returns {boolean} Returns `true` if `value` is less than or equal to + * `other`, else `false`. + * @see gt, gte, lt + * @example + * + * lte(1, 3) + * // => true + * + * lte(3, 3) + * // => true + * + * lte(3, 1) + * // => false + */ +function lte(value, other) { + if (!(typeof value === 'string' && typeof other === 'string')) { + value = +value; + other = +other; + } + return value <= other; +} + +export default lte; diff --git a/src/map.ts b/src/map.ts new file mode 100644 index 0000000000..04bc6a51a6 --- /dev/null +++ b/src/map.ts @@ -0,0 +1,30 @@ +/** + * Creates an array of values by running each element of `array` thru `iteratee`. + * The iteratee is invoked with three arguments: (value, index, array). + * + * @since 5.0.0 + * @category Array + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n + * } + * + * map([4, 8], square) + * // => [16, 64] + */ +function map(array, iteratee) { + let index = -1; + const length = array == null ? 0 : array.length; + const result = new Array(length); + + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; +} + +export default map; diff --git a/src/mapKey.ts b/src/mapKey.ts new file mode 100644 index 0000000000..26bbce7c66 --- /dev/null +++ b/src/mapKey.ts @@ -0,0 +1,31 @@ +/** + * The opposite of `mapValue` this method creates an object with the + * same values as `object` and keys generated by running each own enumerable + * string keyed property of `object` thru `iteratee`. The iteratee is invoked + * with three arguments: (value, key, object). + * + * @since 3.8.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see mapValue + * @example + * + * mapKey({ 'a': 1, 'b': 2 }, function(value, key) { + * return key + value + * }) + * // => { 'a1': 1, 'b2': 2 } + */ +function mapKey(object, iteratee) { + object = Object(object); + const result = {}; + + Object.keys(object).forEach((key) => { + const value = object[key]; + result[iteratee(value, key, object)] = value; + }); + return result; +} + +export default mapKey; diff --git a/src/mapObject.ts b/src/mapObject.ts new file mode 100644 index 0000000000..f474b0a671 --- /dev/null +++ b/src/mapObject.ts @@ -0,0 +1,29 @@ +/** + * Creates an array of values by running each property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: (value, key, object). + * + * @since 5.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the new mapped array. + * @example + * + * function square(n) { + * return n * n + * } + * + * map({ 'a': 4, 'b': 8 }, square) + * // => [16, 64] (iteration order is not guaranteed) + */ +function mapObject(object, iteratee) { + const props = Object.keys(object); + const result = new Array(props.length); + + props.forEach((key, index) => { + result[index] = iteratee(object[key], key, object); + }); + return result; +} + +export default mapObject; diff --git a/src/mapValue.ts b/src/mapValue.ts new file mode 100644 index 0000000000..05160f48ec --- /dev/null +++ b/src/mapValue.ts @@ -0,0 +1,33 @@ +/** + * Creates an object with the same keys as `object` and values generated + * by running each own enumerable string keyed property of `object` thru + * `iteratee`. The iteratee is invoked with three arguments: + * (value, key, object). + * + * @since 2.4.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Object} Returns the new mapped object. + * @see mapKeys + * @example + * + * const users = { + * 'fred': { 'user': 'fred', 'age': 40 }, + * 'pebbles': { 'user': 'pebbles', 'age': 1 } + * } + * + * mapValue(users, ({ age }) => age) + * // => { 'fred': 40, 'pebbles': 1 } (iteration order is not guaranteed) + */ +function mapValue(object, iteratee) { + object = Object(object); + const result = {}; + + Object.keys(object).forEach((key) => { + result[key] = iteratee(object[key], key, object); + }); + return result; +} + +export default mapValue; diff --git a/src/matches.ts b/src/matches.ts new file mode 100644 index 0000000000..1aa08732ef --- /dev/null +++ b/src/matches.ts @@ -0,0 +1,37 @@ +import baseClone from './.internal/baseClone.js'; +import baseMatches from './.internal/baseMatches.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_DEEP_FLAG = 1; + +/** + * Creates a function that performs a partial deep comparison between a given + * object and `source`, returning `true` if the given object has equivalent + * property values, else `false`. + * + * **Note:** The created function is equivalent to `isMatch` with `source` + * partially applied. + * + * Partial comparisons will match empty array and empty object `source` + * values against any array or object value, respectively. See `isEqual` + * for a list of supported value comparisons. + * + * @since 3.0.0 + * @category Util + * @param {Object} source The object of property values to match. + * @returns {Function} Returns the new spec function. + * @example + * + * const objects = [ + * { 'a': 1, 'b': 2, 'c': 3 }, + * { 'a': 4, 'b': 5, 'c': 6 } + * ] + * + * filter(objects, matches({ 'a': 4, 'c': 6 })) + * // => [{ 'a': 4, 'b': 5, 'c': 6 }] + */ +function matches(source) { + return baseMatches(baseClone(source, CLONE_DEEP_FLAG)); +} + +export default matches; diff --git a/src/matchesProperty.ts b/src/matchesProperty.ts new file mode 100644 index 0000000000..5739637db0 --- /dev/null +++ b/src/matchesProperty.ts @@ -0,0 +1,35 @@ +import baseClone from './.internal/baseClone.js'; +import baseMatchesProperty from './.internal/baseMatchesProperty.js'; + +/** Used to compose bitmasks for cloning. */ +const CLONE_DEEP_FLAG = 1; + +/** + * Creates a function that performs a partial deep comparison between the + * value at `path` of a given object to `srcValue`, returning `true` if the + * object value is equivalent, else `false`. + * + * **Note:** Partial comparisons will match empty array and empty object + * `srcValue` values against any array or object value, respectively. See + * `isEqual` for a list of supported value comparisons. + * + * @since 3.2.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @param {*} srcValue The value to match. + * @returns {Function} Returns the new spec function. + * @example + * + * const objects = [ + * { 'a': 1, 'b': 2, 'c': 3 }, + * { 'a': 4, 'b': 5, 'c': 6 } + * ] + * + * find(objects, matchesProperty('a', 4)) + * // => { 'a': 4, 'b': 5, 'c': 6 } + */ +function matchesProperty(path, srcValue) { + return baseMatchesProperty(path, baseClone(srcValue, CLONE_DEEP_FLAG)); +} + +export default matchesProperty; diff --git a/src/maxBy.ts b/src/maxBy.ts new file mode 100644 index 0000000000..b3317edce5 --- /dev/null +++ b/src/maxBy.ts @@ -0,0 +1,42 @@ +import isSymbol from './isSymbol.js'; + +/** + * This method is like `max` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * the value is ranked. The iteratee is invoked with one argument: (value). + * + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {*} Returns the maximum value. + * @example + * + * const objects = [{ 'n': 1 }, { 'n': 2 }] + * + * maxBy(objects, ({ n }) => n) + * // => { 'n': 2 } + */ +function maxBy(array, iteratee) { + let result; + if (array == null) { + return result; + } + let computed; + for (const value of array) { + const current = iteratee(value); + + if ( + current != null && + (computed === undefined + ? current === current && !isSymbol(current) + : current > computed) + ) { + computed = current; + result = value; + } + } + return result; +} + +export default maxBy; diff --git a/src/mean.ts b/src/mean.ts new file mode 100644 index 0000000000..e0ac81ea7b --- /dev/null +++ b/src/mean.ts @@ -0,0 +1,19 @@ +import baseMean from './meanBy.js'; + +/** + * Computes the mean of the values in `array`. + * + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @returns {number} Returns the mean. + * @example + * + * mean([4, 2, 8, 6]) + * // => 5 + */ +function mean(array) { + return baseMean(array, (value) => value); +} + +export default mean; diff --git a/src/meanBy.ts b/src/meanBy.ts new file mode 100644 index 0000000000..af130b2267 --- /dev/null +++ b/src/meanBy.ts @@ -0,0 +1,28 @@ +import baseSum from './.internal/baseSum.js'; + +/** Used as references for various `Number` constants. */ +const NAN = 0 / 0; + +/** + * This method is like `mean` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the value to be averaged. + * The iteratee is invoked with one argument: (value). + * + * @since 4.7.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {number} Returns the mean. + * @example + * + * const objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }] + * + * meanBy(objects, ({ n }) => n) + * // => 5 + */ +function meanBy(array, iteratee) { + const length = array == null ? 0 : array.length; + return length ? baseSum(array, iteratee) / length : NAN; +} + +export default meanBy; diff --git a/src/memoize.ts b/src/memoize.ts new file mode 100644 index 0000000000..7a4da23dc7 --- /dev/null +++ b/src/memoize.ts @@ -0,0 +1,64 @@ +/** + * Creates a function that memoizes the result of `func`. If `resolver` is + * provided, it determines the cache key for storing the result based on the + * arguments provided to the memoized function. By default, the first argument + * provided to the memoized function is used as the map cache key. The `func` + * is invoked with the `this` binding of the memoized function. + * + * **Note:** The cache is exposed as the `cache` property on the memoized + * function. Its creation may be customized by replacing the `memoize.Cache` + * constructor with one whose instances implement the + * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object) + * method interface of `clear`, `delete`, `get`, `has`, and `set`. + * + * @since 0.1.0 + * @category Function + * @param {Function} func The function to have its output memoized. + * @param {Function} [resolver] The function to resolve the cache key. + * @returns {Function} Returns the new memoized function. + * @example + * + * const object = { 'a': 1, 'b': 2 } + * const other = { 'c': 3, 'd': 4 } + * + * const values = memoize(values) + * values(object) + * // => [1, 2] + * + * values(other) + * // => [3, 4] + * + * object.a = 2 + * values(object) + * // => [1, 2] + * + * // Modify the result cache. + * values.cache.set(object, ['a', 'b']) + * values(object) + * // => ['a', 'b'] + * + * // Replace `memoize.Cache`. + * memoize.Cache = WeakMap + */ +function memoize(func, resolver) { + if (typeof func !== 'function' || (resolver != null && typeof resolver !== 'function')) { + throw new TypeError('Expected a function'); + } + const memoized = function (...args) { + const key = resolver ? resolver.apply(this, args) : args[0]; + const cache = memoized.cache; + + if (cache.has(key)) { + return cache.get(key); + } + const result = func.apply(this, args); + memoized.cache = cache.set(key, result) || cache; + return result; + }; + memoized.cache = new (memoize.Cache || Map)(); + return memoized; +} + +memoize.Cache = Map; + +export default memoize; diff --git a/src/merge.ts b/src/merge.ts new file mode 100644 index 0000000000..108385e873 --- /dev/null +++ b/src/merge.ts @@ -0,0 +1,37 @@ +import baseMerge from './.internal/baseMerge.js'; +import createAssigner from './.internal/createAssigner.js'; + +/** + * This method is like `assign` except that it recursively merges own and + * inherited enumerable string keyed properties of source objects into the + * destination object. Source properties that resolve to `undefined` are + * skipped if a destination value exists. Array and plain object properties + * are merged recursively. Other objects and value types are overridden by + * assignment. Source objects are applied from left to right. Subsequent + * sources overwrite property assignments of previous sources. + * + * **Note:** This method mutates `object`. + * + * @since 0.5.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} [sources] The source objects. + * @returns {Object} Returns `object`. + * @example + * + * const object = { + * 'a': [{ 'b': 2 }, { 'd': 4 }] + * } + * + * const other = { + * 'a': [{ 'c': 3 }, { 'e': 5 }] + * } + * + * merge(object, other) + * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] } + */ +const merge = createAssigner((object, source, srcIndex) => { + baseMerge(object, source, srcIndex); +}); + +export default merge; diff --git a/src/mergeWith.ts b/src/mergeWith.ts new file mode 100644 index 0000000000..149f8757a3 --- /dev/null +++ b/src/mergeWith.ts @@ -0,0 +1,37 @@ +import baseMerge from './.internal/baseMerge.js'; +import createAssigner from './.internal/createAssigner.js'; + +/** + * This method is like `merge` except that it accepts `customizer` which + * is invoked to produce the merged values of the destination and source + * properties. If `customizer` returns `undefined`, merging is handled by the + * method instead. The `customizer` is invoked with six arguments: + * (objValue, srcValue, key, object, source, stack). + * + * **Note:** This method mutates `object`. + * + * @since 4.0.0 + * @category Object + * @param {Object} object The destination object. + * @param {...Object} sources The source objects. + * @param {Function} customizer The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * function customizer(objValue, srcValue) { + * if (Array.isArray(objValue)) { + * return objValue.concat(srcValue) + * } + * } + * + * const object = { 'a': [1], 'b': [2] } + * const other = { 'a': [3], 'b': [4] } + * + * mergeWith(object, other, customizer) + * // => { 'a': [1, 3], 'b': [2, 4] } + */ +const mergeWith = createAssigner((object, source, srcIndex, customizer) => { + baseMerge(object, source, srcIndex, customizer); +}); + +export default mergeWith; diff --git a/src/method.ts b/src/method.ts new file mode 100644 index 0000000000..5b66f54b09 --- /dev/null +++ b/src/method.ts @@ -0,0 +1,29 @@ +import invoke from './invoke.js'; + +/** + * Creates a function that invokes the method at `path` of a given object. + * Any additional arguments are provided to the invoked method. + * + * @since 3.7.0 + * @category Util + * @param {Array|string} path The path of the method to invoke. + * @param {Array} [args] The arguments to invoke the method with. + * @returns {Function} Returns the new invoker function. + * @example + * + * const objects = [ + * { 'a': { 'b': () => 2 } }, + * { 'a': { 'b': () => 1 } } + * ] + * + * map(objects, method('a.b')) + * // => [2, 1] + * + * map(objects, method(['a', 'b'])) + * // => [2, 1] + */ +function method(path, args) { + return (object) => invoke(object, path, args); +} + +export default method; diff --git a/src/methodOf.ts b/src/methodOf.ts new file mode 100644 index 0000000000..dda40ec751 --- /dev/null +++ b/src/methodOf.ts @@ -0,0 +1,28 @@ +import invoke from './invoke.js'; + +/** + * The opposite of `method` this method creates a function that invokes + * the method at a given path of `object`. Any additional arguments are + * provided to the invoked method. + * + * @since 3.7.0 + * @category Util + * @param {Object} object The object to query. + * @param {Array} [args] The arguments to invoke the method with. + * @returns {Function} Returns the new invoker function. + * @example + * + * const array = times(3, i => () => i) + * const object = { 'a': array, 'b': array, 'c': array } + * + * map(['a[2]', 'c[0]'], methodOf(object)) + * // => [2, 0] + * + * map([['a', '2'], ['c', '0']], methodOf(object)) + * // => [2, 0]f + */ +function methodOf(object, args) { + return (path) => invoke(object, path, args); +} + +export default methodOf; diff --git a/src/minBy.ts b/src/minBy.ts new file mode 100644 index 0000000000..4e5298981d --- /dev/null +++ b/src/minBy.ts @@ -0,0 +1,42 @@ +import isSymbol from './isSymbol.js'; + +/** + * This method is like `min` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * the value is ranked. The iteratee is invoked with one argument: (value). + * + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {*} Returns the minimum value. + * @example + * + * const objects = [{ 'n': 1 }, { 'n': 2 }] + * + * minBy(objects, ({ n }) => n) + * // => { 'n': 1 } + */ +function minBy(array, iteratee) { + let result; + if (array == null) { + return result; + } + let computed; + for (const value of array) { + const current = iteratee(value); + + if ( + current != null && + (computed === undefined + ? current === current && !isSymbol(current) + : current < computed) + ) { + computed = current; + result = value; + } + } + return result; +} + +export default minBy; diff --git a/src/multiply.ts b/src/multiply.ts new file mode 100644 index 0000000000..5c6b8e40cb --- /dev/null +++ b/src/multiply.ts @@ -0,0 +1,18 @@ +import createMathOperation from './.internal/createMathOperation.js'; + +/** + * Multiply two numbers. + * + * @since 4.7.0 + * @category Math + * @param {number} multiplier The first number in a multiplication. + * @param {number} multiplicand The second number in a multiplication. + * @returns {number} Returns the product. + * @example + * + * multiply(6, 4) + * // => 24 + */ +const multiply = createMathOperation((multiplier, multiplicand) => multiplier * multiplicand, 1); + +export default multiply; diff --git a/src/negate.ts b/src/negate.ts new file mode 100644 index 0000000000..fbb10791a2 --- /dev/null +++ b/src/negate.ts @@ -0,0 +1,28 @@ +/** + * Creates a function that negates the result of the predicate `func`. The + * `func` predicate is invoked with the `this` binding and arguments of the + * created function. + * + * @since 3.0.0 + * @category Function + * @param {Function} predicate The predicate to negate. + * @returns {Function} Returns the new negated function. + * @example + * + * function isEven(n) { + * return n % 2 === 0 + * } + * + * filter([1, 2, 3, 4, 5, 6], negate(isEven)) + * // => [1, 3, 5] + */ +function negate(predicate) { + if (typeof predicate !== 'function') { + throw new TypeError('Expected a function'); + } + return function (...args) { + return !predicate.apply(this, args); + }; +} + +export default negate; diff --git a/src/nth.ts b/src/nth.ts new file mode 100644 index 0000000000..006c9723bd --- /dev/null +++ b/src/nth.ts @@ -0,0 +1,32 @@ +import isIndex from './.internal/isIndex.js'; + +/** + * Gets the element at index `n` of `array`. If `n` is negative, the nth + * element from the end is returned. + * + * @since 4.11.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=0] The index of the element to return. + * @returns {*} Returns the nth element of `array`. + * @example + * + * const array = ['a', 'b', 'c', 'd'] + * + * nth(array, 1) + * // => 'b' + * + * nth(array, -2) + * // => 'c' + */ +function nth(array: any[], n: number) { + const length = array == null ? 0 : array.length; + if (!length) { + return; + } + n += n < 0 ? length : 0; + // eslint-disable-next-line consistent-return + return isIndex(n, length) ? array[n] : undefined; +} + +export default nth; diff --git a/src/nthArg.ts b/src/nthArg.ts new file mode 100644 index 0000000000..70b340ddea --- /dev/null +++ b/src/nthArg.ts @@ -0,0 +1,25 @@ +import nth from './nth.js'; + +/** + * Creates a function that gets the argument at index `n`. If `n` is negative, + * the nth argument from the end is returned. + * + * @since 4.0.0 + * @category Util + * @param {number} [n=0] The index of the argument to return. + * @returns {Function} Returns the new pass-thru function. + * @example + * + * const func = nthArg(1) + * func('a', 'b', 'c', 'd') + * // => 'b' + * + * const func = nthArg(-2) + * func('a', 'b', 'c', 'd') + * // => 'c' + */ +function nthArg(n) { + return (...args) => nth(args, n); +} + +export default nthArg; diff --git a/src/once.ts b/src/once.ts new file mode 100644 index 0000000000..f6a12ec2e0 --- /dev/null +++ b/src/once.ts @@ -0,0 +1,23 @@ +import before from './before.js'; + +/** + * Creates a function that is restricted to invoking `func` once. Repeat calls + * to the function return the value of the first invocation. The `func` is + * invoked with the `this` binding and arguments of the created function. + * + * @since 0.1.0 + * @category Function + * @param {Function} func The function to restrict. + * @returns {Function} Returns the new restricted function. + * @example + * + * const initialize = once(createApplication) + * initialize() + * initialize() + * // => `createApplication` is invoked once + */ +function once(func) { + return before(2, func); +} + +export default once; diff --git a/src/orderBy.ts b/src/orderBy.ts new file mode 100644 index 0000000000..0f439c7bf9 --- /dev/null +++ b/src/orderBy.ts @@ -0,0 +1,51 @@ +import baseOrderBy from './.internal/baseOrderBy.js'; + +/** + * This method is like `sortBy` except that it allows specifying the sort + * orders of the iteratees to sort by. If `orders` is unspecified, all values + * are sorted in ascending order. Otherwise, specify an order of "desc" for + * descending or "asc" for ascending sort order of corresponding values. + * You may also specify a compare function for an order. + * + * @since 4.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Array[]|Function[]|Object[]|string[]} [iteratees=[identity]] + * The iteratees to sort by. + * @param {(string|function)[]} [orders] The sort orders of `iteratees`. + * @returns {Array} Returns the new sorted array. + * @see reverse + * @example + * + * const users = [ + * { 'user': 'fred', 'age': 48 }, + * { 'user': 'barney', 'age': 34 }, + * { 'user': 'fred', 'age': 40 }, + * { 'user': 'barney', 'age': 36 } + * ] + * + * // Sort by `user` in ascending order and by `age` in descending order. + * orderBy(users, ['user', 'age'], ['asc', 'desc']) + * // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]] + * + * // Sort by `user` then by `age` using custom compare functions for each + * orderBy(users, ['user', 'age'], [ + * (a, b) => a.localeCompare(b, 'de', { sensitivity: 'base' }), + * (a, b) => a - b, + * ]) + * + */ +function orderBy(collection, iteratees, orders) { + if (collection == null) { + return []; + } + if (!Array.isArray(iteratees)) { + iteratees = iteratees == null ? [] : [iteratees]; + } + if (!Array.isArray(orders)) { + orders = orders == null ? [] : [orders]; + } + return baseOrderBy(collection, iteratees, orders); +} + +export default orderBy; diff --git a/src/over.ts b/src/over.ts new file mode 100644 index 0000000000..29ea14423f --- /dev/null +++ b/src/over.ts @@ -0,0 +1,25 @@ +import map from './map.js'; + +/** + * Creates a function that invokes `iteratees` with the arguments it receives + * and returns their results. + * + * @since 4.0.0 + * @category Util + * @param {Function[]} [iteratees=[identity]] + * The iteratees to invoke. + * @returns {Function} Returns the new function. + * @example + * + * const func = over([Math.max, Math.min]) + * + * func(1, 2, 3, 4) + * // => [4, 1] + */ +function over(iteratees) { + return function (...args) { + return map(iteratees, (iteratee) => iteratee.apply(this, args)); + }; +} + +export default over; diff --git a/src/overArgs.ts b/src/overArgs.ts new file mode 100644 index 0000000000..97572358d7 --- /dev/null +++ b/src/overArgs.ts @@ -0,0 +1,40 @@ +/** + * Creates a function that invokes `func` with its arguments transformed. + * + * @since 4.0.0 + * @category Function + * @param {Function} func The function to wrap. + * @param {Function[]} [transforms=[identity]] + * The argument transforms. + * @returns {Function} Returns the new function. + * @example + * + * function doubled(n) { + * return n * 2 + * } + * + * function square(n) { + * return n * n + * } + * + * const func = overArgs((x, y) => [x, y], [square, doubled]) + * + * func(9, 3) + * // => [81, 6] + * + * func(10, 5) + * // => [100, 10] + */ +function overArgs(func, transforms) { + const funcsLength = transforms.length; + return function (...args) { + let index = -1; + const length = Math.min(args.length, funcsLength); + while (++index < length) { + args[index] = transforms[index].call(this, args[index]); + } + return func.apply(this, args); + }; +} + +export default overArgs; diff --git a/src/overEvery.ts b/src/overEvery.ts new file mode 100644 index 0000000000..f4a9ecbc1d --- /dev/null +++ b/src/overEvery.ts @@ -0,0 +1,31 @@ +import every from './every.js'; + +/** + * Creates a function that checks if **all** of the `predicates` return + * truthy when invoked with the arguments it receives. + * + * @since 4.0.0 + * @category Util + * @param {Function[]} [predicates=[identity]] + * The predicates to check. + * @returns {Function} Returns the new function. + * @example + * + * const func = overEvery([Boolean, isFinite]) + * + * func('1') + * // => true + * + * func(null) + * // => false + * + * func(NaN) + * // => false + */ +function overEvery(iteratees) { + return function (...args) { + return every(iteratees, (iteratee) => iteratee.apply(this, args)); + }; +} + +export default overEvery; diff --git a/src/overSome.ts b/src/overSome.ts new file mode 100644 index 0000000000..573548e306 --- /dev/null +++ b/src/overSome.ts @@ -0,0 +1,31 @@ +import some from './some.js'; + +/** + * Creates a function that checks if **any** of the `predicates` return + * truthy when invoked with the arguments it receives. + * + * @since 4.0.0 + * @category Util + * @param {Function[]} [predicates=[identity]] + * The predicates to check. + * @returns {Function} Returns the new function. + * @example + * + * const func = overSome([Boolean, isFinite]) + * + * func('1') + * // => true + * + * func(null) + * // => true + * + * func(NaN) + * // => false + */ +function overSome(iteratees) { + return function (...args) { + return some(iteratees, (iteratee) => iteratee.apply(this, args)); + }; +} + +export default overSome; diff --git a/src/pad.ts b/src/pad.ts new file mode 100644 index 0000000000..b2ad485d7f --- /dev/null +++ b/src/pad.ts @@ -0,0 +1,34 @@ +import createPadding from './.internal/createPadding.js'; +import stringSize from './.internal/stringSize.js'; + +/** + * Pads `string` on the left and right sides if it's shorter than `length`. + * Padding characters are truncated if they can't be evenly divided by `length`. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * pad('abc', 8) + * // => ' abc ' + * + * pad('abc', 8, '_-') + * // => '_-abc_-_' + * + * pad('abc', 2) + * // => 'abc' + */ +function pad(string, length, chars) { + const strLength = length ? stringSize(string) : 0; + if (!length || strLength >= length) { + return string || ''; + } + const mid = (length - strLength) / 2; + return createPadding(Math.floor(mid), chars) + string + createPadding(Math.ceil(mid), chars); +} + +export default pad; diff --git a/src/padEnd.ts b/src/padEnd.ts new file mode 100644 index 0000000000..fdbce69259 --- /dev/null +++ b/src/padEnd.ts @@ -0,0 +1,32 @@ +import createPadding from './.internal/createPadding.js'; +import stringSize from './.internal/stringSize.js'; + +/** + * Pads `string` on the right side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * padEnd('abc', 6) + * // => 'abc ' + * + * padEnd('abc', 6, '_-') + * // => 'abc_-_' + * + * padEnd('abc', 2) + * // => 'abc' + */ +function padEnd(string, length, chars) { + const strLength = length ? stringSize(string) : 0; + return length && strLength < length + ? string + createPadding(length - strLength, chars) + : string || ''; +} + +export default padEnd; diff --git a/src/padStart.ts b/src/padStart.ts new file mode 100644 index 0000000000..fd45ce1e77 --- /dev/null +++ b/src/padStart.ts @@ -0,0 +1,32 @@ +import createPadding from './.internal/createPadding.js'; +import stringSize from './.internal/stringSize.js'; + +/** + * Pads `string` on the left side if it's shorter than `length`. Padding + * characters are truncated if they exceed `length`. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to pad. + * @param {number} [length=0] The padding length. + * @param {string} [chars=' '] The string used as padding. + * @returns {string} Returns the padded string. + * @example + * + * padStart('abc', 6) + * // => ' abc' + * + * padStart('abc', 6, '_-') + * // => '_-_abc' + * + * padStart('abc', 2) + * // => 'abc' + */ +function padStart(string, length, chars) { + const strLength = length ? stringSize(string) : 0; + return length && strLength < length + ? createPadding(length - strLength, chars) + string + : string || ''; +} + +export default padStart; diff --git a/src/parseInt.ts b/src/parseInt.ts new file mode 100644 index 0000000000..2467e1f54f --- /dev/null +++ b/src/parseInt.ts @@ -0,0 +1,36 @@ +import root from './.internal/root.js'; + +/** Used to match leading and trailing whitespace. */ +const reTrimStart = /^\s+/; + +/* Built-in method references for those with the same name as other `lodash` methods. */ +const nativeParseInt = root.parseInt; + +/** + * Converts `string` to an integer of the specified radix. If `radix` is + * `undefined` or `0`, a `radix` of `10` is used unless `string` is a + * hexadecimal, in which case a `radix` of `16` is used. + * + * **Note:** This method aligns with the + * [ES5 implementation](https://es5.github.io/#x15.1.2.2) of `parseInt`. + * + * @since 1.1.0 + * @category String + * @param {string} string The string to convert. + * @param {number} [radix=10] The radix to interpret `string` by. + * @returns {number} Returns the converted integer. + * @example + * + * parseInt('08') + * // => 8 + */ +function parseInt(string, radix) { + if (radix == null) { + radix = 0; + } else if (radix) { + radix = +radix; + } + return nativeParseInt(`${string}`.replace(reTrimStart, ''), radix || 0); +} + +export default parseInt; diff --git a/src/partition.ts b/src/partition.ts new file mode 100644 index 0000000000..6782f63dbf --- /dev/null +++ b/src/partition.ts @@ -0,0 +1,37 @@ +import reduce from './reduce.js'; + +/** + * Creates an array of elements split into two groups, the first of which + * contains elements `predicate` returns truthy for, the second of which + * contains elements `predicate` returns falsey for. The predicate is + * invoked with one argument: (value). + * + * @since 3.0.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the array of grouped elements. + * @see groupBy, keyBy + * @example + * + * const users = [ + * { 'user': 'barney', 'age': 36, 'active': false }, + * { 'user': 'fred', 'age': 40, 'active': true }, + * { 'user': 'pebbles', 'age': 1, 'active': false } + * ] + * + * partition(users, ({ active }) => active) + * // => objects for [['fred'], ['barney', 'pebbles']] + */ +function partition(collection, predicate) { + return reduce( + collection, + (result, value) => { + result[predicate(value) ? 0 : 1].push(value); + return result; + }, + [[], []], + ); +} + +export default partition; diff --git a/src/pick.ts b/src/pick.ts new file mode 100644 index 0000000000..b9ab9a7d1e --- /dev/null +++ b/src/pick.ts @@ -0,0 +1,22 @@ +import basePick from './.internal/basePick.js'; + +/** + * Creates an object composed of the picked `object` properties. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The source object. + * @param {...(string|string[])} [paths] The property paths to pick. + * @returns {Object} Returns the new object. + * @example + * + * const object = { 'a': 1, 'b': '2', 'c': 3 } + * + * pick(object, ['a', 'c']) + * // => { 'a': 1, 'c': 3 } + */ +function pick(object, ...paths) { + return object == null ? {} : basePick(object, paths); +} + +export default pick; diff --git a/src/pickBy.ts b/src/pickBy.ts new file mode 100644 index 0000000000..aaca9ea6f0 --- /dev/null +++ b/src/pickBy.ts @@ -0,0 +1,29 @@ +import map from './map.js'; +import basePickBy from './.internal/basePickBy.js'; +import getAllKeysIn from './.internal/getAllKeysIn.js'; + +/** + * Creates an object composed of the `object` properties `predicate` returns + * truthy for. The predicate is invoked with two arguments: (value, key). + * + * @since 4.0.0 + * @category Object + * @param {Object} object The source object. + * @param {Function} predicate The function invoked per property. + * @returns {Object} Returns the new object. + * @example + * + * const object = { 'a': 1, 'b': '2', 'c': 3 } + * + * pickBy(object, isNumber) + * // => { 'a': 1, 'c': 3 } + */ +function pickBy(object, predicate) { + if (object == null) { + return {}; + } + const props = map(getAllKeysIn(object), (prop) => [prop]); + return basePickBy(object, props, (value, path) => predicate(value, path[0])); +} + +export default pickBy; diff --git a/src/property.ts b/src/property.ts new file mode 100644 index 0000000000..3d9a99e60a --- /dev/null +++ b/src/property.ts @@ -0,0 +1,30 @@ +import baseProperty from './.internal/baseProperty.js'; +import basePropertyDeep from './.internal/basePropertyDeep.js'; +import isKey from './.internal/isKey.js'; +import toKey from './.internal/toKey.js'; + +/** + * Creates a function that returns the value at `path` of a given object. + * + * @since 2.4.0 + * @category Util + * @param {Array|string} path The path of the property to get. + * @returns {Function} Returns the new accessor function. + * @example + * + * const objects = [ + * { 'a': { 'b': 2 } }, + * { 'a': { 'b': 1 } } + * ] + * + * map(objects, property('a.b')) + * // => [2, 1] + * + * map(sortBy(objects, property(['a', 'b'])), 'a.b') + * // => [1, 2] + */ +function property(path) { + return isKey(path) ? baseProperty(toKey(path)) : basePropertyDeep(path); +} + +export default property; diff --git a/src/propertyOf.ts b/src/propertyOf.ts new file mode 100644 index 0000000000..990b09a75f --- /dev/null +++ b/src/propertyOf.ts @@ -0,0 +1,26 @@ +import baseGet from './.internal/baseGet.js'; + +/** + * The opposite of `property`s method creates a function that returns + * the value at a given path of `object`. + * + * @since 3.0.0 + * @category Util + * @param {Object} object The object to query. + * @returns {Function} Returns the new accessor function. + * @example + * + * const array = [0, 1, 2] + * const object = { 'a': array, 'b': array, 'c': array } + * + * map(['a[2]', 'c[0]'], propertyOf(object)) + * // => [2, 0] + * + * map([['a', '2'], ['c', '0']], propertyOf(object)) + * // => [2, 0] + */ +function propertyOf(object) { + return (path) => (object == null ? undefined : baseGet(object, path)); +} + +export default propertyOf; diff --git a/src/pull.ts b/src/pull.ts new file mode 100644 index 0000000000..3950734c0e --- /dev/null +++ b/src/pull.ts @@ -0,0 +1,29 @@ +import pullAll from './pullAll.js'; + +/** + * Removes all given values from `array` using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `without`, this method mutates `array`. Use `remove` + * to remove elements from an array by predicate. + * + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...*} [values] The values to remove. + * @returns {Array} Returns `array`. + * @see pullAll, pullAllBy, pullAllWith, pullAt, remove, reject + * @example + * + * const array = ['a', 'b', 'c', 'a', 'b', 'c'] + * + * pull(array, 'a', 'c') + * console.log(array) + * // => ['b', 'b'] + */ +function pull(array, ...values) { + return pullAll(array, values); +} + +export default pull; diff --git a/src/pullAll.ts b/src/pullAll.ts new file mode 100644 index 0000000000..2fe42f41bb --- /dev/null +++ b/src/pullAll.ts @@ -0,0 +1,28 @@ +import basePullAll from './.internal/basePullAll.js'; + +/** + * This method is like `pull` except that it accepts an array of values to remove. + * + * **Note:** Unlike `difference`, this method mutates `array`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @returns {Array} Returns `array`. + * @see pull, pullAllBy, pullAllWith, pullAt, remove, reject + * @example + * + * const array = ['a', 'b', 'c', 'a', 'b', 'c'] + * + * pullAll(array, ['a', 'c']) + * console.log(array) + * // => ['b', 'b'] + */ +function pullAll(array, values) { + return array != null && array.length && values != null && values.length + ? basePullAll(array, values) + : array; +} + +export default pullAll; diff --git a/src/pullAllBy.ts b/src/pullAllBy.ts new file mode 100644 index 0000000000..5d4a2beaa5 --- /dev/null +++ b/src/pullAllBy.ts @@ -0,0 +1,31 @@ +import basePullAll from './.internal/basePullAll.js'; + +/** + * This method is like `pullAll` except that it accepts `iteratee` which is + * invoked for each element of `array` and `values` to generate the criterion + * by which they're compared. The iteratee is invoked with one argument: (value). + * + * **Note:** Unlike `differenceBy`, this method mutates `array`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns `array`. + * @see pull, pullAll, pullAllWith, pullAt, remove, reject + * @example + * + * const array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }] + * + * pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], 'x') + * console.log(array) + * // => [{ 'x': 2 }] + */ +function pullAllBy(array, values, iteratee) { + return array != null && array.length && values != null && values.length + ? basePullAll(array, values, iteratee) + : array; +} + +export default pullAllBy; diff --git a/src/pullAllWith.ts b/src/pullAllWith.ts new file mode 100644 index 0000000000..69546ed20a --- /dev/null +++ b/src/pullAllWith.ts @@ -0,0 +1,31 @@ +import basePullAll from './.internal/basePullAll.js'; + +/** + * This method is like `pullAll` except that it accepts `comparator` which + * is invoked to compare elements of `array` to `values`. The comparator is + * invoked with two arguments: (arrVal, othVal). + * + * **Note:** Unlike `differenceWith`, this method mutates `array`. + * + * @since 4.6.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Array} values The values to remove. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns `array`. + * @see pull, pullAll, pullAllBy, pullAt, remove, reject + * @example + * + * const array = [{ 'x': 1, 'y': 2 }, { 'x': 3, 'y': 4 }, { 'x': 5, 'y': 6 }] + * + * pullAllWith(array, [{ 'x': 3, 'y': 4 }], isEqual) + * console.log(array) + * // => [{ 'x': 1, 'y': 2 }, { 'x': 5, 'y': 6 }] + */ +function pullAllWith(array, values, comparator) { + return array != null && array.length && values != null && values.length + ? basePullAll(array, values, undefined, comparator) + : array; +} + +export default pullAllWith; diff --git a/src/pullAt.ts b/src/pullAt.ts new file mode 100644 index 0000000000..17e47dc915 --- /dev/null +++ b/src/pullAt.ts @@ -0,0 +1,41 @@ +import map from './map.js'; +import baseAt from './.internal/baseAt.js'; +import basePullAt from './.internal/basePullAt.js'; +import compareAscending from './.internal/compareAscending.js'; +import isIndex from './.internal/isIndex.js'; + +/** + * Removes elements from `array` corresponding to `indexes` and returns an + * array of removed elements. + * + * **Note:** Unlike `at`, this method mutates `array`. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {...(number|number[])} [indexes] The indexes of elements to remove. + * @returns {Array} Returns the new array of removed elements. + * @see pull, pullAll, pullAllBy, pullAllWith, remove, reject + * @example + * + * const array = ['a', 'b', 'c', 'd'] + * const pulled = pullAt(array, [1, 3]) + * + * console.log(array) + * // => ['a', 'c'] + * + * console.log(pulled) + * // => ['b', 'd'] + */ +function pullAt(array, ...indexes) { + const length = array == null ? 0 : array.length; + const result = baseAt(array, indexes); + + basePullAt( + array, + map(indexes, (index) => (isIndex(index, length) ? +index : index)).sort(compareAscending), + ); + return result; +} + +export default pullAt; diff --git a/src/random.ts b/src/random.ts new file mode 100644 index 0000000000..182b0c8f42 --- /dev/null +++ b/src/random.ts @@ -0,0 +1,71 @@ +import toFinite from './toFinite.js'; + +/** Built-in method references without a dependency on `root`. */ +const freeParseFloat = parseFloat; + +/** + * Produces a random number between the inclusive `lower` and `upper` bounds. + * If only one argument is provided a number between `0` and the given number + * is returned. If `floating` is `true`, or either `lower` or `upper` are + * floats, a floating-point number is returned instead of an integer. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @since 0.7.0 + * @category Number + * @param {number} [lower=0] The lower bound. + * @param {number} [upper=1] The upper bound. + * @param {boolean} [floating] Specify returning a floating-point number. + * @returns {number} Returns the random number. + * @see uniqueId + * @example + * + * random(0, 5) + * // => an integer between 0 and 5 + * + * random(5) + * // => also an integer between 0 and 5 + * + * random(5, true) + * // => a floating-point number between 0 and 5 + * + * random(1.2, 5.2) + * // => a floating-point number between 1.2 and 5.2 + */ +function random(lower, upper, floating) { + if (floating === undefined) { + if (typeof upper === 'boolean') { + floating = upper; + upper = undefined; + } else if (typeof lower === 'boolean') { + floating = lower; + lower = undefined; + } + } + if (lower === undefined && upper === undefined) { + lower = 0; + upper = 1; + } else { + lower = toFinite(lower); + if (upper === undefined) { + upper = lower; + lower = 0; + } else { + upper = toFinite(upper); + } + } + if (lower > upper) { + const temp = lower; + lower = upper; + upper = temp; + } + if (floating || lower % 1 || upper % 1) { + const rand = Math.random(); + const randLength = `${rand}`.length - 1; + return Math.min(lower + rand * (upper - lower + freeParseFloat(`1e-${randLength}`)), upper); + } + return lower + Math.floor(Math.random() * (upper - lower + 1)); +} + +export default random; diff --git a/src/range.ts b/src/range.ts new file mode 100644 index 0000000000..ebefffd022 --- /dev/null +++ b/src/range.ts @@ -0,0 +1,44 @@ +import createRange from './.internal/createRange.js'; + +/** + * Creates an array of numbers (positive and/or negative) progressing from + * `start` up to, but not including, `end`. A step of `-1` is used if a negative + * `start` is specified without an `end` or `step`. If `end` is not specified, + * it's set to `start`, and `start` is then set to `0`. + * + * **Note:** JavaScript follows the IEEE-754 standard for resolving + * floating-point values which can produce unexpected results. + * + * @since 0.1.0 + * @category Util + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the range of numbers. + * @see inRange, rangeRight + * @example + * + * range(4) + * // => [0, 1, 2, 3] + * + * range(-4) + * // => [0, -1, -2, -3] + * + * range(1, 5) + * // => [1, 2, 3, 4] + * + * range(0, 20, 5) + * // => [0, 5, 10, 15] + * + * range(0, -4, -1) + * // => [0, -1, -2, -3] + * + * range(1, 4, 0) + * // => [1, 1, 1] + * + * range(0) + * // => [] + */ +const range = createRange(); + +export default range; diff --git a/src/rangeRight.ts b/src/rangeRight.ts new file mode 100644 index 0000000000..8c3af926f9 --- /dev/null +++ b/src/rangeRight.ts @@ -0,0 +1,39 @@ +import createRange from './.internal/createRange.js'; + +/** + * This method is like `range` except that it populates values in + * descending order. + * + * @since 4.0.0 + * @category Util + * @param {number} [start=0] The start of the range. + * @param {number} end The end of the range. + * @param {number} [step=1] The value to increment or decrement by. + * @returns {Array} Returns the range of numbers. + * @see inRange, range + * @example + * + * rangeRight(4) + * // => [3, 2, 1, 0] + * + * rangeRight(-4) + * // => [-3, -2, -1, 0] + * + * rangeRight(1, 5) + * // => [4, 3, 2, 1] + * + * rangeRight(0, 20, 5) + * // => [15, 10, 5, 0] + * + * rangeRight(0, -4, -1) + * // => [-3, -2, -1, 0] + * + * rangeRight(1, 4, 0) + * // => [1, 1, 1] + * + * rangeRight(0) + * // => [] + */ +const rangeRight = createRange(true); + +export default rangeRight; diff --git a/src/reduce.ts b/src/reduce.ts new file mode 100644 index 0000000000..e8540b79f3 --- /dev/null +++ b/src/reduce.ts @@ -0,0 +1,44 @@ +import arrayReduce from './.internal/arrayReduce.js'; +import baseEach from './.internal/baseEach.js'; +import baseReduce from './.internal/baseReduce.js'; + +/** + * Reduces `collection` to a value which is the accumulated result of running + * each element in `collection` thru `iteratee`, where each successive + * invocation is supplied the return value of the previous. If `accumulator` + * is not given, the first element of `collection` is used as the initial + * value. The iteratee is invoked with four arguments: + * (accumulator, value, index|key, collection). + * + * Many lodash methods are guarded to work as iteratees for methods like + * `reduce`, `reduceRight`, and `transform`. + * + * The guarded methods are: + * `assign`, `defaults`, `defaultsDeep`, `includes`, `merge`, `orderBy`, + * and `sortBy` + * + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see reduceRight, transform + * @example + * + * reduce([1, 2], (sum, n) => sum + n, 0) + * // => 3 + * + * reduce({ 'a': 1, 'b': 2, 'c': 1 }, (result, value, key) => { + * (result[value] || (result[value] = [])).push(key) + * return result + * }, {}) + * // => { '1': ['a', 'c'], '2': ['b'] } (iteration order is not guaranteed) + */ +function reduce(collection, iteratee, accumulator) { + const func = Array.isArray(collection) ? arrayReduce : baseReduce; + const initAccum = arguments.length < 3; + return func(collection, iteratee, accumulator, initAccum, baseEach); +} + +export default reduce; diff --git a/src/reduceRight.ts b/src/reduceRight.ts new file mode 100644 index 0000000000..e812f67ebe --- /dev/null +++ b/src/reduceRight.ts @@ -0,0 +1,29 @@ +import arrayReduceRight from './.internal/arrayReduceRight.js'; +import baseEachRight from './.internal/baseEachRight.js'; +import baseReduce from './.internal/baseReduce.js'; + +/** + * This method is like `reduce` except that it iterates over elements of + * `collection` from right to left. + * + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The initial value. + * @returns {*} Returns the accumulated value. + * @see reduce + * @example + * + * const array = [[0, 1], [2, 3], [4, 5]] + * + * reduceRight(array, (flattened, other) => flattened.concat(other), []) + * // => [4, 5, 2, 3, 0, 1] + */ +function reduceRight(collection, iteratee, accumulator) { + const func = Array.isArray(collection) ? arrayReduceRight : baseReduce; + const initAccum = arguments.length < 3; + return func(collection, iteratee, accumulator, initAccum, baseEachRight); +} + +export default reduceRight; diff --git a/src/reject.ts b/src/reject.ts new file mode 100644 index 0000000000..3b78a7142f --- /dev/null +++ b/src/reject.ts @@ -0,0 +1,30 @@ +import filter from './filter.js'; +import filterObject from './filterObject.js'; +import negate from './negate.js'; + +/** + * The opposite of `filter` this method returns the elements of `collection` + * that `predicate` does **not** return truthy for. + * + * @since 0.1.0 + * @category Collection + * @param {Array|Object} collection The collection to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new filtered array. + * @see pull, pullAll, pullAllBy, pullAllWith, pullAt, remove, filter + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': false } + * ] + * + * reject(users, ({ active }) => active) + * // => objects for ['fred'] + */ +function reject(collection, predicate) { + const func = Array.isArray(collection) ? filter : filterObject; + return func(collection, negate(predicate)); +} + +export default reject; diff --git a/src/remove.ts b/src/remove.ts new file mode 100644 index 0000000000..fd41acf20e --- /dev/null +++ b/src/remove.ts @@ -0,0 +1,48 @@ +import basePullAt from './.internal/basePullAt.js'; + +/** + * Removes all elements from `array` that `predicate` returns truthy for + * and returns an array of the removed elements. The predicate is invoked + * with three arguments: (value, index, array). + * + * **Note:** Unlike `filter`, this method mutates `array`. Use `pull` + * to pull elements from an array by value. + * + * @since 2.0.0 + * @category Array + * @param {Array} array The array to modify. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the new array of removed elements. + * @see pull, pullAll, pullAllBy, pullAllWith, pullAt, reject, filter + * @example + * + * const array = [1, 2, 3, 4] + * const evens = remove(array, n => n % 2 === 0) + * + * console.log(array) + * // => [1, 3] + * + * console.log(evens) + * // => [2, 4] + */ +function remove(array, predicate) { + const result = []; + if (!(array != null && array.length)) { + return result; + } + let index = -1; + const indexes = []; + const { length } = array; + + while (++index < length) { + const value = array[index]; + if (predicate(value, index, array)) { + result.push(value); + indexes.push(index); + } + } + basePullAt(array, indexes); + return result; +} + +export default remove; diff --git a/src/repeat.ts b/src/repeat.ts new file mode 100644 index 0000000000..fb58440145 --- /dev/null +++ b/src/repeat.ts @@ -0,0 +1,40 @@ +/** + * Repeats the given string `n` times. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to repeat. + * @param {number} [n=1] The number of times to repeat the string. + * @returns {string} Returns the repeated string. + * @example + * + * repeat('*', 3) + * // => '***' + * + * repeat('abc', 2) + * // => 'abcabc' + * + * repeat('abc', 0) + * // => '' + */ +function repeat(string, n) { + let result = ''; + if (!string || n < 1 || n > Number.MAX_SAFE_INTEGER) { + return result; + } + // Leverage the exponentiation by squaring algorithm for a faster repeat. + // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details. + do { + if (n % 2) { + result += string; + } + n = Math.floor(n / 2); + if (n) { + string += string; + } + } while (n); + + return result; +} + +export default repeat; diff --git a/src/replace.ts b/src/replace.ts new file mode 100644 index 0000000000..c2d0b49c2a --- /dev/null +++ b/src/replace.ts @@ -0,0 +1,24 @@ +/** + * Replaces matches for `pattern` in `string` with `replacement`. + * + * **Note:** This method is based on + * [`String#replace`](https://mdn.io/String/replace). + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to modify. + * @param {RegExp|string} pattern The pattern to replace. + * @param {Function|string} replacement The match replacement. + * @returns {string} Returns the modified string. + * @see truncate, trim + * @example + * + * replace('Hi Fred', 'Fred', 'Barney') + * // => 'Hi Barney' + */ +function replace(...args) { + const string = `${args[0]}`; + return args.length < 3 ? string : string.replace(args[1], args[2]); +} + +export default replace; diff --git a/src/result.ts b/src/result.ts new file mode 100644 index 0000000000..edf4f8c4fc --- /dev/null +++ b/src/result.ts @@ -0,0 +1,53 @@ +import castPath from './.internal/castPath.js'; +import toKey from './.internal/toKey.js'; + +/** + * This method is like `get` except that if the resolved value is a + * function it's invoked with the `this` binding of its parent object and + * its result is returned. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The object to query. + * @param {Array|string} path The path of the property to resolve. + * @param {*} [defaultValue] The value returned for `undefined` resolved values. + * @returns {*} Returns the resolved value. + * @example + * + * const object = { 'a': [{ 'b': { 'c1': 3, 'c2': () => 4 } }] } + * + * result(object, 'a[0].b.c1') + * // => 3 + * + * result(object, 'a[0].b.c2') + * // => 4 + * + * result(object, 'a[0].b.c3', 'default') + * // => 'default' + * + * result(object, 'a[0].b.c3', () => 'default') + * // => 'default' + */ +function result(object, path, defaultValue) { + path = castPath(path, object); + + let index = -1; + let length = path.length; + + // Ensure the loop is entered when path is empty. + if (!length) { + length = 1; + object = undefined; + } + while (++index < length) { + let value = object == null ? undefined : object[toKey(path[index])]; + if (value === undefined) { + index = length; + value = defaultValue; + } + object = typeof value === 'function' ? value.call(object) : value; + } + return object; +} + +export default result; diff --git a/src/round.ts b/src/round.ts new file mode 100644 index 0000000000..b3e3f33223 --- /dev/null +++ b/src/round.ts @@ -0,0 +1,24 @@ +import createRound from './.internal/createRound.js'; + +/** + * Computes `number` rounded to `precision`. + * + * @since 3.10.0 + * @category Math + * @param {number} number The number to round. + * @param {number} [precision=0] The precision to round to. + * @returns {number} Returns the rounded number. + * @example + * + * round(4.006) + * // => 4 + * + * round(4.006, 2) + * // => 4.01 + * + * round(4060, -2) + * // => 4100 + */ +const round = createRound('round'); + +export default round; diff --git a/src/sample.ts b/src/sample.ts new file mode 100644 index 0000000000..268cd4ebd8 --- /dev/null +++ b/src/sample.ts @@ -0,0 +1,18 @@ +/** + * Gets a random element from `array`. + * + * @since 2.0.0 + * @category Array + * @param {Array} array The array to sample. + * @returns {*} Returns the random element. + * @example + * + * sample([1, 2, 3, 4]) + * // => 2 + */ +function sample(array) { + const length = array == null ? 0 : array.length; + return length ? array[Math.floor(Math.random() * length)] : undefined; +} + +export default sample; diff --git a/src/sampleSize.ts b/src/sampleSize.ts new file mode 100644 index 0000000000..2ea7ef5561 --- /dev/null +++ b/src/sampleSize.ts @@ -0,0 +1,40 @@ +import copyArray from './.internal/copyArray.js'; +import slice from './slice.js'; + +/** + * Gets `n` random elements at unique keys from `array` up to the + * size of `array`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to sample. + * @param {number} [n=1] The number of elements to sample. + * @returns {Array} Returns the random elements. + * @example + * + * sampleSize([1, 2, 3], 2) + * // => [3, 1] + * + * sampleSize([1, 2, 3], 4) + * // => [2, 3, 1] + */ +function sampleSize(array, n) { + n = n == null ? 1 : n; + const length = array == null ? 0 : array.length; + if (!length || n < 1) { + return []; + } + n = n > length ? length : n; + let index = -1; + const lastIndex = length - 1; + const result = copyArray(array); + while (++index < n) { + const rand = index + Math.floor(Math.random() * (lastIndex - index + 1)); + const value = result[rand]; + result[rand] = result[index]; + result[index] = value; + } + return slice(result, 0, n); +} + +export default sampleSize; diff --git a/src/set.ts b/src/set.ts new file mode 100644 index 0000000000..1a3a75a91c --- /dev/null +++ b/src/set.ts @@ -0,0 +1,34 @@ +import baseSet from './.internal/baseSet.js'; + +/** + * Sets the value at `path` of `object`. If a portion of `path` doesn't exist, + * it's created. Arrays are created for missing index properties while objects + * are created for all other missing properties. Use `setWith` to customize + * `path` creation. + * + * **Note:** This method mutates `object`. + * + * @since 3.7.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @returns {Object} Returns `object`. + * @see has, hasIn, get, unset + * @example + * + * const object = { 'a': [{ 'b': { 'c': 3 } }] } + * + * set(object, 'a[0].b.c', 4) + * console.log(object.a[0].b.c) + * // => 4 + * + * set(object, ['x', '0', 'y', 'z'], 5) + * console.log(object.x[0].y.z) + * // => 5 + */ +function set(object, path, value) { + return object == null ? object : baseSet(object, path, value); +} + +export default set; diff --git a/src/setWith.ts b/src/setWith.ts new file mode 100644 index 0000000000..a7e55d714f --- /dev/null +++ b/src/setWith.ts @@ -0,0 +1,30 @@ +import baseSet from './.internal/baseSet.js'; + +/** + * This method is like `set` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {*} value The value to set. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * const object = {} + * + * setWith(object, '[0][1]', 'a', Object) + * // => { '0': { '1': 'a' } } + */ +function setWith(object, path, value, customizer) { + customizer = typeof customizer === 'function' ? customizer : undefined; + return object == null ? object : baseSet(object, path, value, customizer); +} + +export default setWith; diff --git a/src/shuffle.ts b/src/shuffle.ts new file mode 100644 index 0000000000..b378a7da25 --- /dev/null +++ b/src/shuffle.ts @@ -0,0 +1,33 @@ +import copyArray from './.internal/copyArray.js'; + +/** + * Creates an array of shuffled values, using a version of the + * [Fisher-Yates shuffle](https://en.wikipedia.org/wiki/Fisher-Yates_shuffle). + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to shuffle. + * @returns {Array} Returns the new shuffled array. + * @example + * + * shuffle([1, 2, 3, 4]) + * // => [4, 1, 3, 2] + */ +function shuffle(array) { + const length = array == null ? 0 : array.length; + if (!length) { + return []; + } + let index = -1; + const lastIndex = length - 1; + const result = copyArray(array); + while (++index < length) { + const rand = index + Math.floor(Math.random() * (lastIndex - index + 1)); + const value = result[rand]; + result[rand] = result[index]; + result[index] = value; + } + return result; +} + +export default shuffle; diff --git a/src/size.ts b/src/size.ts new file mode 100644 index 0000000000..d9cb0f0f23 --- /dev/null +++ b/src/size.ts @@ -0,0 +1,43 @@ +import getTag from './.internal/getTag.js'; +import isArrayLike from './isArrayLike.js'; +import isString from './isString.js'; +import stringSize from './.internal/stringSize.js'; + +/** `Object#toString` result references. */ +const mapTag = '[object Map]'; +const setTag = '[object Set]'; + +/** + * Gets the size of `collection` by returning its length for array-like + * values or the number of own enumerable string keyed properties for objects. + * + * @since 0.1.0 + * @category Collection + * @param {Array|Object|string} collection The collection to inspect. + * @returns {number} Returns the collection size. + * @example + * + * size([1, 2, 3]) + * // => 3 + * + * size({ 'a': 1, 'b': 2 }) + * // => 2 + * + * size('pebbles') + * // => 7 + */ +function size(collection) { + if (collection == null) { + return 0; + } + if (isArrayLike(collection)) { + return isString(collection) ? stringSize(collection) : collection.length; + } + const tag = getTag(collection); + if (tag === mapTag || tag === setTag) { + return collection.size; + } + return Object.keys(collection).length; +} + +export default size; diff --git a/src/slice.ts b/src/slice.ts new file mode 100644 index 0000000000..fa89ed9003 --- /dev/null +++ b/src/slice.ts @@ -0,0 +1,47 @@ +/** + * Creates a slice of `array` from `start` up to, but not including, `end`. + * + * **Note:** This method is used instead of + * [`Array#slice`](https://mdn.io/Array/slice) to ensure dense arrays are + * returned. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to slice. + * @param {number} [start=0] The start position. A negative index will be treated as an offset from the end. + * @param {number} [end=array.length] The end position. A negative index will be treated as an offset from the end. + * @returns {Array} Returns the slice of `array`. + * @example + * + * var array = [1, 2, 3, 4] + * + * _.slice(array, 2) + * // => [3, 4] + */ +function slice(array, start, end) { + let length = array == null ? 0 : array.length; + if (!length) { + return []; + } + start = start == null ? 0 : start; + end = end === undefined ? length : end; + + if (start < 0) { + start = -start > length ? 0 : length + start; + } + end = end > length ? length : end; + if (end < 0) { + end += length; + } + length = start > end ? 0 : (end - start) >>> 0; + start >>>= 0; + + let index = -1; + const result = new Array(length); + while (++index < length) { + result[index] = array[index + start]; + } + return result; +} + +export default slice; diff --git a/src/snakeCase.ts b/src/snakeCase.ts new file mode 100644 index 0000000000..8fb8988b54 --- /dev/null +++ b/src/snakeCase.ts @@ -0,0 +1,33 @@ +import words from './words.js'; +import toString from './toString.js'; + +/** + * Converts `string` to + * [snake case](https://en.wikipedia.org/wiki/Snake_case). + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the snake cased string. + * @see camelCase, lowerCase, kebabCase, startCase, upperCase, upperFirst + * @example + * + * snakeCase('Foo Bar') + * // => 'foo_bar' + * + * snakeCase('fooBar') + * // => 'foo_bar' + * + * snakeCase('--FOO-BAR--') + * // => 'foo_bar' + * + * snakeCase('foo2bar') + * // => 'foo_2_bar' + */ +const snakeCase = (string) => + words(toString(string).replace(/['\u2019]/g, '')).reduce( + (result, word, index) => result + (index ? '_' : '') + word.toLowerCase(), + '', + ); + +export default snakeCase; diff --git a/src/some.ts b/src/some.ts new file mode 100644 index 0000000000..94f1e632e9 --- /dev/null +++ b/src/some.ts @@ -0,0 +1,29 @@ +/** + * Checks if `predicate` returns truthy for **any** element of `array`. + * Iteration is stopped once `predicate` returns truthy. The predicate is + * invoked with three arguments: (value, index, array). + * + * @since 5.0.0 + * @category Array + * @param {Array} array The array to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * some([null, 0, 'yes', false], Boolean) + * // => true + */ +function some(array, predicate) { + let index = -1; + const length = array == null ? 0 : array.length; + + while (++index < length) { + if (predicate(array[index], index, array)) { + return true; + } + } + return false; +} + +export default some; diff --git a/src/someValue.ts b/src/someValue.ts new file mode 100644 index 0000000000..7dd81b62bd --- /dev/null +++ b/src/someValue.ts @@ -0,0 +1,29 @@ +/** + * Checks if `predicate` returns truthy for **any** element of `object`. + * Iteration is stopped once `predicate` returns truthy. The predicate is + * invoked with three arguments: (value, key, object). + * + * @since 5.0.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} predicate The function invoked per iteration. + * @returns {boolean} Returns `true` if any element passes the predicate check, + * else `false`. + * @example + * + * someValues({ 'a': 0, 'b': 'yes', 'c': false }, Boolean) + * // => true + */ +function someValues(object, predicate) { + object = Object(object); + const props = Object.keys(object); + + for (const key of props) { + if (predicate(object[key], key, object)) { + return true; + } + } + return false; +} + +export default someValues; diff --git a/src/sortedIndex.ts b/src/sortedIndex.ts new file mode 100644 index 0000000000..8b47c4667b --- /dev/null +++ b/src/sortedIndex.ts @@ -0,0 +1,22 @@ +import baseSortedIndex from './.internal/baseSortedIndex.js'; + +/** + * Uses a binary search to determine the lowest index at which `value` + * should be inserted into `array` in order to maintain its sort order. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * sortedIndex([30, 50], 40) + * // => 1 + */ +function sortedIndex(array, value) { + return baseSortedIndex(array, value); +} + +export default sortedIndex; diff --git a/src/sortedIndexBy.ts b/src/sortedIndexBy.ts new file mode 100644 index 0000000000..09ac15d49b --- /dev/null +++ b/src/sortedIndexBy.ts @@ -0,0 +1,26 @@ +import baseSortedIndexBy from './.internal/baseSortedIndexBy.js'; + +/** + * This method is like `sortedIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * const objects = [{ 'n': 4 }, { 'n': 5 }] + * + * sortedIndexBy(objects, { 'n': 4 }, ({ n }) => n) + * // => 0 + */ +function sortedIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, iteratee); +} + +export default sortedIndexBy; diff --git a/src/sortedIndexOf.ts b/src/sortedIndexOf.ts new file mode 100644 index 0000000000..8b96301227 --- /dev/null +++ b/src/sortedIndexOf.ts @@ -0,0 +1,29 @@ +import baseSortedIndex from './.internal/baseSortedIndex.js'; +import eq from './eq.js'; + +/** + * This method is like `indexOf` except that it performs a binary + * search on a sorted `array`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * sortedIndexOf([4, 5, 5, 5, 6], 5) + * // => 1 + */ +function sortedIndexOf(array, value) { + const length = array == null ? 0 : array.length; + if (length) { + const index = baseSortedIndex(array, value); + if (index < length && eq(array[index], value)) { + return index; + } + } + return -1; +} + +export default sortedIndexOf; diff --git a/src/sortedLastIndex.ts b/src/sortedLastIndex.ts new file mode 100644 index 0000000000..addc8e0a26 --- /dev/null +++ b/src/sortedLastIndex.ts @@ -0,0 +1,23 @@ +import baseSortedIndex from './.internal/baseSortedIndex.js'; + +/** + * This method is like `sortedIndex` except that it returns the highest + * index at which `value` should be inserted into `array` in order to + * maintain its sort order. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * sortedLastIndex([4, 5, 5, 5, 6], 5) + * // => 4 + */ +function sortedLastIndex(array, value) { + return baseSortedIndex(array, value, true); +} + +export default sortedLastIndex; diff --git a/src/sortedLastIndexBy.ts b/src/sortedLastIndexBy.ts new file mode 100644 index 0000000000..e2d7260ecf --- /dev/null +++ b/src/sortedLastIndexBy.ts @@ -0,0 +1,26 @@ +import baseSortedIndexBy from './.internal/baseSortedIndexBy.js'; + +/** + * This method is like `sortedLastIndex` except that it accepts `iteratee` + * which is invoked for `value` and each element of `array` to compute their + * sort ranking. The iteratee is invoked with one argument: (value). + * + * @since 4.0.0 + * @category Array + * @param {Array} array The sorted array to inspect. + * @param {*} value The value to evaluate. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {number} Returns the index at which `value` should be inserted + * into `array`. + * @example + * + * const objects = [{ 'n': 4 }, { 'n': 5 }] + * + * sortedLastIndexBy(objects, { 'n': 4 }, ({ n }) => n) + * // => 1 + */ +function sortedLastIndexBy(array, value, iteratee) { + return baseSortedIndexBy(array, value, iteratee, true); +} + +export default sortedLastIndexBy; diff --git a/src/sortedLastIndexOf.ts b/src/sortedLastIndexOf.ts new file mode 100644 index 0000000000..56d3e38b69 --- /dev/null +++ b/src/sortedLastIndexOf.ts @@ -0,0 +1,29 @@ +import baseSortedIndex from './.internal/baseSortedIndex.js'; +import eq from './eq.js'; + +/** + * This method is like `lastIndexOf` except that it performs a binary + * search on a sorted `array`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {*} value The value to search for. + * @returns {number} Returns the index of the matched value, else `-1`. + * @example + * + * sortedLastIndexOf([4, 5, 5, 5, 6], 5) + * // => 3 + */ +function sortedLastIndexOf(array, value) { + const length = array == null ? 0 : array.length; + if (length) { + const index = baseSortedIndex(array, value, true) - 1; + if (eq(array[index], value)) { + return index; + } + } + return -1; +} + +export default sortedLastIndexOf; diff --git a/src/sortedUniq.ts b/src/sortedUniq.ts new file mode 100644 index 0000000000..9b11abe9ef --- /dev/null +++ b/src/sortedUniq.ts @@ -0,0 +1,22 @@ +import baseSortedUniq from './.internal/baseSortedUniq.js'; + +/** + * This method is like `uniq` except that it only works + * for sorted arrays. + * If the input array is known to be sorted `sortedUniq` is + * faster than `uniq`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * sortedUniq([1, 1, 2]) + * // => [1, 2] + */ +function sortedUniq(array) { + return array != null && array.length ? baseSortedUniq(array) : []; +} + +export default sortedUniq; diff --git a/src/sortedUniqBy.ts b/src/sortedUniqBy.ts new file mode 100644 index 0000000000..e8ad625327 --- /dev/null +++ b/src/sortedUniqBy.ts @@ -0,0 +1,21 @@ +import baseSortedUniq from './.internal/baseSortedUniq.js'; + +/** + * This method is like `uniqBy` except that it's designed and optimized + * for sorted arrays. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @example + * + * sortedUniqBy([1.1, 1.2, 2.3, 2.4], Math.floor) + * // => [1.1, 2.3] + */ +function sortedUniqBy(array, iteratee) { + return array != null && array.length ? baseSortedUniq(array, iteratee) : []; +} + +export default sortedUniqBy; diff --git a/src/split.ts b/src/split.ts new file mode 100644 index 0000000000..4f0fad43db --- /dev/null +++ b/src/split.ts @@ -0,0 +1,39 @@ +import castSlice from './.internal/castSlice.js'; +import hasUnicode from './.internal/hasUnicode.js'; +import isRegExp from './isRegExp.js'; +import stringToArray from './.internal/stringToArray.js'; + +/** Used as references for the maximum length and index of an array. */ +const MAX_ARRAY_LENGTH = 4294967295; + +/** + * Splits `string` by `separator`. + * + * **Note:** This method is based on + * [`String#split`](https://mdn.io/String/split). + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to split. + * @param {RegExp|string} separator The separator pattern to split by. + * @param {number} [limit] The length to truncate results to. + * @returns {Array} Returns the string segments. + * @example + * + * split('a-b-c', '-', 2) + * // => ['a', 'b'] + */ +function split(string, separator, limit) { + limit = limit === undefined ? MAX_ARRAY_LENGTH : limit >>> 0; + if (!limit) { + return []; + } + if (string && (typeof separator === 'string' || (separator != null && !isRegExp(separator)))) { + if (!separator && hasUnicode(string)) { + return castSlice(stringToArray(string), 0, limit); + } + } + return string.split(separator, limit); +} + +export default split; diff --git a/src/startCase.ts b/src/startCase.ts new file mode 100644 index 0000000000..a5826ad640 --- /dev/null +++ b/src/startCase.ts @@ -0,0 +1,30 @@ +import upperFirst from './upperFirst.js'; +import words from './words.js'; + +/** + * Converts `string` to + * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). + * + * @since 3.1.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the start cased string. + * @see camelCase, lowerCase, kebabCase, snakeCase, upperCase, upperFirst + * @example + * + * startCase('--foo-bar--') + * // => 'Foo Bar' + * + * startCase('fooBar') + * // => 'Foo Bar' + * + * startCase('__FOO_BAR__') + * // => 'FOO BAR' + */ +const startCase = (string) => + words(`${string}`.replace(/['\u2019]/g, '')).reduce( + (result, word, index) => result + (index ? ' ' : '') + upperFirst(word), + '', + ); + +export default startCase; diff --git a/src/startsWith.ts b/src/startsWith.ts new file mode 100644 index 0000000000..4ac4f36ed5 --- /dev/null +++ b/src/startsWith.ts @@ -0,0 +1,35 @@ +/** + * Checks if `string` starts with the given target string. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {string} [target] The string to search for. + * @param {number} [position=0] The position to search from. + * @returns {boolean} Returns `true` if `string` starts with `target`, + * else `false`. + * @see endsWith, includes + * @example + * + * startsWith('abc', 'a') + * // => true + * + * startsWith('abc', 'b') + * // => false + * + * startsWith('abc', 'b', 1) + * // => true + */ +function startsWith(string, target, position) { + const { length } = string; + position = position == null ? 0 : position; + if (position < 0) { + position = 0; + } else if (position > length) { + position = length; + } + target = `${target}`; + return string.slice(position, position + target.length) === target; +} + +export default startsWith; diff --git a/src/subtract.ts b/src/subtract.ts new file mode 100644 index 0000000000..e203066d0f --- /dev/null +++ b/src/subtract.ts @@ -0,0 +1,18 @@ +import createMathOperation from './.internal/createMathOperation.js'; + +/** + * Subtract two numbers. + * + * @since 4.0.0 + * @category Math + * @param {number} minuend The first number in a subtraction. + * @param {number} subtrahend The second number in a subtraction. + * @returns {number} Returns the difference. + * @example + * + * subtract(6, 4) + * // => 2 + */ +const subtract = createMathOperation((minuend, subtrahend) => minuend - subtrahend, 0); + +export default subtract; diff --git a/src/sum.ts b/src/sum.ts new file mode 100644 index 0000000000..50237ea155 --- /dev/null +++ b/src/sum.ts @@ -0,0 +1,19 @@ +import baseSum from './.internal/baseSum.js'; + +/** + * Computes the sum of the values in `array`. + * + * @since 3.4.0 + * @category Math + * @param {Array} array The array to iterate over. + * @returns {number} Returns the sum. + * @example + * + * sum([4, 2, 8, 6]) + * // => 20 + */ +function sum(array) { + return array != null && array.length ? baseSum(array, (value) => value) : 0; +} + +export default sum; diff --git a/src/sumBy.ts b/src/sumBy.ts new file mode 100644 index 0000000000..723d6fca57 --- /dev/null +++ b/src/sumBy.ts @@ -0,0 +1,24 @@ +import baseSum from './.internal/baseSum.js'; + +/** + * This method is like `sum` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the value to be summed. + * The iteratee is invoked with one argument: (value). + * + * @since 4.0.0 + * @category Math + * @param {Array} array The array to iterate over. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {number} Returns the sum. + * @example + * + * const objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }] + * + * sumBy(objects, ({ n }) => n) + * // => 20 + */ +function sumBy(array, iteratee) { + return array != null && array.length ? baseSum(array, iteratee) : 0; +} + +export default sumBy; diff --git a/src/tail.ts b/src/tail.ts new file mode 100644 index 0000000000..fa8d28c9a3 --- /dev/null +++ b/src/tail.ts @@ -0,0 +1,22 @@ +/** + * Gets all but the first element of `array`. + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to query. + * @returns {Array} Returns the slice of `array`. + * @example + * + * tail([1, 2, 3]) + * // => [2, 3] + */ +function tail(array) { + const length = array == null ? 0 : array.length; + if (!length) { + return []; + } + const [, ...result] = array; + return result; +} + +export default tail; diff --git a/src/take.ts b/src/take.ts new file mode 100644 index 0000000000..e691671eb8 --- /dev/null +++ b/src/take.ts @@ -0,0 +1,32 @@ +import slice from './slice.js'; + +/** + * Creates a slice of `array` with `n` elements taken from the beginning. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @returns {Array} Returns the slice of `array`. + * @example + * + * take([1, 2, 3]) + * // => [1] + * + * take([1, 2, 3], 2) + * // => [1, 2] + * + * take([1, 2, 3], 5) + * // => [1, 2, 3] + * + * take([1, 2, 3], 0) + * // => [] + */ +function take(array, n = 1) { + if (!(array != null && array.length)) { + return []; + } + return slice(array, 0, n < 0 ? 0 : n); +} + +export default take; diff --git a/src/takeRight.ts b/src/takeRight.ts new file mode 100644 index 0000000000..2768e9914d --- /dev/null +++ b/src/takeRight.ts @@ -0,0 +1,34 @@ +import slice from './slice.js'; + +/** + * Creates a slice of `array` with `n` elements taken from the end. + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {number} [n=1] The number of elements to take. + * @returns {Array} Returns the slice of `array`. + * @example + * + * takeRight([1, 2, 3]) + * // => [3] + * + * takeRight([1, 2, 3], 2) + * // => [2, 3] + * + * takeRight([1, 2, 3], 5) + * // => [1, 2, 3] + * + * takeRight([1, 2, 3], 0) + * // => [] + */ +function takeRight(array, n = 1) { + const length = array == null ? 0 : array.length; + if (!length) { + return []; + } + n = length - n; + return slice(array, n < 0 ? 0 : n, length); +} + +export default takeRight; diff --git a/src/takeRightWhile.ts b/src/takeRightWhile.ts new file mode 100644 index 0000000000..a1d9086d68 --- /dev/null +++ b/src/takeRightWhile.ts @@ -0,0 +1,28 @@ +import baseWhile from './.internal/baseWhile.js'; + +/** + * Creates a slice of `array` with elements taken from the end. Elements are + * taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': false }, + * { 'user': 'fred', 'active': true }, + * { 'user': 'pebbles', 'active': true } + * ] + * + * takeRightWhile(users, ({ active }) => active) + * // => objects for ['fred', 'pebbles'] + */ +function takeRightWhile(array, predicate) { + return array != null && array.length ? baseWhile(array, predicate, false, true) : []; +} + +export default takeRightWhile; diff --git a/src/takeWhile.ts b/src/takeWhile.ts new file mode 100644 index 0000000000..3847e0af2e --- /dev/null +++ b/src/takeWhile.ts @@ -0,0 +1,28 @@ +import baseWhile from './.internal/baseWhile.js'; + +/** + * Creates a slice of `array` with elements taken from the beginning. Elements + * are taken until `predicate` returns falsey. The predicate is invoked with + * three arguments: (value, index, array). + * + * @since 3.0.0 + * @category Array + * @param {Array} array The array to query. + * @param {Function} predicate The function invoked per iteration. + * @returns {Array} Returns the slice of `array`. + * @example + * + * const users = [ + * { 'user': 'barney', 'active': true }, + * { 'user': 'fred', 'active': true }, + * { 'user': 'pebbles', 'active': false } + * ] + * + * takeWhile(users, ({ active }) => active) + * // => objects for ['barney', 'fred'] + */ +function takeWhile(array, predicate) { + return array != null && array.length ? baseWhile(array, predicate) : []; +} + +export default takeWhile; diff --git a/src/throttle.ts b/src/throttle.ts new file mode 100644 index 0000000000..c0f721f047 --- /dev/null +++ b/src/throttle.ts @@ -0,0 +1,70 @@ +import debounce from './debounce.js'; +import isObject from './isObject.js'; + +/** + * Creates a throttled function that only invokes `func` at most once per + * every `wait` milliseconds (or once per browser frame). The throttled function + * comes with a `cancel` method to cancel delayed `func` invocations and a + * `flush` method to immediately invoke them. Provide `options` to indicate + * whether `func` should be invoked on the leading and/or trailing edge of the + * `wait` timeout. The `func` is invoked with the last arguments provided to the + * throttled function. Subsequent calls to the throttled function return the + * result of the last `func` invocation. + * + * **Note:** If `leading` and `trailing` options are `true`, `func` is + * invoked on the trailing edge of the timeout only if the throttled function + * is invoked more than once during the `wait` timeout. + * + * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred + * until the next tick, similar to `setTimeout` with a timeout of `0`. + * + * If `wait` is omitted in an environment with `requestAnimationFrame`, `func` + * invocation will be deferred until the next frame is drawn (typically about + * 16ms). + * + * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) + * for details over the differences between `throttle` and `debounce`. + * + * @since 0.1.0 + * @category Function + * @param {Function} func The function to throttle. + * @param {number} [wait=0] + * The number of milliseconds to throttle invocations to; if omitted, + * `requestAnimationFrame` is used (if available). + * @param {Object} [options={}] The options object. + * @param {boolean} [options.leading=true] + * Specify invoking on the leading edge of the timeout. + * @param {boolean} [options.trailing=true] + * Specify invoking on the trailing edge of the timeout. + * @returns {Function} Returns the new throttled function. + * @example + * + * // Avoid excessively updating the position while scrolling. + * jQuery(window).on('scroll', throttle(updatePosition, 100)) + * + * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. + * const throttled = throttle(renewToken, 300000, { 'trailing': false }) + * jQuery(element).on('click', throttled) + * + * // Cancel the trailing throttled invocation. + * jQuery(window).on('popstate', throttled.cancel) + */ +function throttle(func, wait, options) { + let leading = true; + let trailing = true; + + if (typeof func !== 'function') { + throw new TypeError('Expected a function'); + } + if (isObject(options)) { + leading = 'leading' in options ? !!options.leading : leading; + trailing = 'trailing' in options ? !!options.trailing : trailing; + } + return debounce(func, wait, { + leading, + trailing, + maxWait: wait, + }); +} + +export default throttle; diff --git a/src/times.ts b/src/times.ts new file mode 100644 index 0000000000..f779333cdf --- /dev/null +++ b/src/times.ts @@ -0,0 +1,42 @@ +/** Used as references for various `Number` constants. */ +const MAX_SAFE_INTEGER = 9007199254740991; + +/** Used as references for the maximum length and index of an array. */ +const MAX_ARRAY_LENGTH = 4294967295; + +/** + * Invokes the iteratee `n` times, returning an array of the results of + * each invocation. The iteratee is invoked with one argument: (index). + * + * @since 0.1.0 + * @category Util + * @param {number} n The number of times to invoke `iteratee`. + * @param {Function} iteratee The function invoked per iteration. + * @returns {Array} Returns the array of results. + * @example + * + * times(3, String) + * // => ['0', '1', '2'] + * + * times(4, () => 0) + * // => [0, 0, 0, 0] + */ +function times(n, iteratee) { + if (n < 1 || n > MAX_SAFE_INTEGER) { + return []; + } + let index = -1; + const length = Math.min(n, MAX_ARRAY_LENGTH); + const result = new Array(length); + while (++index < length) { + result[index] = iteratee(index); + } + index = MAX_ARRAY_LENGTH; + n -= MAX_ARRAY_LENGTH; + while (++index < n) { + iteratee(index); + } + return result; +} + +export default times; diff --git a/src/toArray.ts b/src/toArray.ts new file mode 100644 index 0000000000..cb121a7948 --- /dev/null +++ b/src/toArray.ts @@ -0,0 +1,55 @@ +import copyArray from './.internal/copyArray.js'; +import getTag from './.internal/getTag.js'; +import isArrayLike from './isArrayLike.js'; +import isString from './isString.js'; +import iteratorToArray from './.internal/iteratorToArray.js'; +import mapToArray from './.internal/mapToArray.js'; +import setToArray from './.internal/setToArray.js'; +import stringToArray from './.internal/stringToArray.js'; +import values from './values.js'; + +/** `Object#toString` result references. */ +const mapTag = '[object Map]'; +const setTag = '[object Set]'; + +/** Built-in value references. */ +const symIterator = Symbol.iterator; + +/** + * Converts `value` to an array. + * + * @since 0.1.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Array} Returns the converted array. + * @example + * + * toArray({ 'a': 1, 'b': 2 }) + * // => [1, 2] + * + * toArray('abc') + * // => ['a', 'b', 'c'] + * + * toArray(1) + * // => [] + * + * toArray(null) + * // => [] + */ +function toArray(value) { + if (!value) { + return []; + } + if (isArrayLike(value)) { + return isString(value) ? stringToArray(value) : copyArray(value); + } + if (symIterator && value[symIterator]) { + return iteratorToArray(value[symIterator]()); + } + const tag = getTag(value); + const func = tag === mapTag ? mapToArray : tag === setTag ? setToArray : values; + + return func(value); +} + +export default toArray; diff --git a/src/toFinite.ts b/src/toFinite.ts new file mode 100644 index 0000000000..7216c87fcf --- /dev/null +++ b/src/toFinite.ts @@ -0,0 +1,40 @@ +import toNumber from './toNumber.js'; + +/** Used as references for various `Number` constants. */ +const INFINITY = 1 / 0; +const MAX_INTEGER = 1.7976931348623157e308; + +/** + * Converts `value` to a finite number. + * + * @since 4.12.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted number. + * @example + * + * toFinite(3.2) + * // => 3.2 + * + * toFinite(Number.MIN_VALUE) + * // => 5e-324 + * + * toFinite(Infinity) + * // => 1.7976931348623157e+308 + * + * toFinite('3.2') + * // => 3.2 + */ +function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + const sign = value < 0 ? -1 : 1; + return sign * MAX_INTEGER; + } + return value === value ? value : 0; +} + +export default toFinite; diff --git a/src/toInteger.ts b/src/toInteger.ts new file mode 100644 index 0000000000..c259ba18b3 --- /dev/null +++ b/src/toInteger.ts @@ -0,0 +1,35 @@ +import toFinite from './toFinite.js'; + +/** + * Converts `value` to an integer. + * + * **Note:** This method is loosely based on + * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @see isInteger, isNumber, toNumber + * @example + * + * toInteger(3.2) + * // => 3 + * + * toInteger(Number.MIN_VALUE) + * // => 0 + * + * toInteger(Infinity) + * // => 1.7976931348623157e+308 + * + * toInteger('3.2') + * // => 3 + */ +function toInteger(value) { + const result = toFinite(value); + const remainder = result % 1; + + return remainder ? result - remainder : result; +} + +export default toInteger; diff --git a/src/toLength.ts b/src/toLength.ts new file mode 100644 index 0000000000..a3b27c9fbf --- /dev/null +++ b/src/toLength.ts @@ -0,0 +1,45 @@ +import toInteger from './toInteger.js'; + +/** Used as references for the maximum length and index of an array. */ +const MAX_ARRAY_LENGTH = 4294967295; + +/** + * Converts `value` to an integer suitable for use as the length of an + * array-like object. + * + * **Note:** This method is based on + * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * toLength(3.2) + * // => 3 + * + * toLength(Number.MIN_VALUE) + * // => 0 + * + * toLength(Infinity) + * // => 4294967295 + * + * toLength('3.2') + * // => 3 + */ +function toLength(value) { + if (!value) { + return 0; + } + value = toInteger(value); + if (value < 0) { + return 0; + } + if (value > MAX_ARRAY_LENGTH) { + return MAX_ARRAY_LENGTH; + } + return value; +} + +export default toLength; diff --git a/src/toNumber.ts b/src/toNumber.ts new file mode 100644 index 0000000000..cc2f2ef33e --- /dev/null +++ b/src/toNumber.ts @@ -0,0 +1,67 @@ +import isObject from './isObject.js'; +import isSymbol from './isSymbol.js'; + +/** Used as references for various `Number` constants. */ +const NAN = 0 / 0; + +/** Used to match leading and trailing whitespace. */ +const reTrim = /^\s+|\s+$/g; + +/** Used to detect bad signed hexadecimal string values. */ +const reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + +/** Used to detect binary string values. */ +const reIsBinary = /^0b[01]+$/i; + +/** Used to detect octal string values. */ +const reIsOctal = /^0o[0-7]+$/i; + +/** Built-in method references without a dependency on `root`. */ +const freeParseInt = parseInt; + +/** + * Converts `value` to a number. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to process. + * @returns {number} Returns the number. + * @see isInteger, toInteger, isNumber + * @example + * + * toNumber(3.2) + * // => 3.2 + * + * toNumber(Number.MIN_VALUE) + * // => 5e-324 + * + * toNumber(Infinity) + * // => Infinity + * + * toNumber('3.2') + * // => 3.2 + */ +function toNumber(value) { + if (typeof value === 'number') { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + const other = typeof value.valueOf === 'function' ? value.valueOf() : value; + value = isObject(other) ? `${other}` : other; + } + if (typeof value !== 'string') { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ''); + const isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) + ? freeParseInt(value.slice(2), isBinary ? 2 : 8) + : reIsBadHex.test(value) + ? NAN + : +value; +} + +export default toNumber; diff --git a/src/toPath.ts b/src/toPath.ts new file mode 100644 index 0000000000..c5a371ffe7 --- /dev/null +++ b/src/toPath.ts @@ -0,0 +1,29 @@ +import map from './map.js'; +import copyArray from './.internal/copyArray.js'; +import isSymbol from './isSymbol.js'; +import stringToPath from './.internal/stringToPath.js'; +import toKey from './.internal/toKey.js'; + +/** + * Converts `value` to a property path array. + * + * @since 4.0.0 + * @category Util + * @param {*} value The value to convert. + * @returns {Array} Returns the new property path array. + * @example + * + * toPath('a.b.c') + * // => ['a', 'b', 'c'] + * + * toPath('a[0].b.c') + * // => ['a', '0', 'b', 'c'] + */ +function toPath(value) { + if (Array.isArray(value)) { + return map(value, toKey); + } + return isSymbol(value) ? [value] : copyArray(stringToPath(value)); +} + +export default toPath; diff --git a/src/toPlainObject.ts b/src/toPlainObject.ts new file mode 100644 index 0000000000..46b903db07 --- /dev/null +++ b/src/toPlainObject.ts @@ -0,0 +1,32 @@ +/** + * Converts `value` to a plain object flattening inherited enumerable string + * keyed properties of `value` to own properties of the plain object. + * + * @since 3.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {Object} Returns the converted plain object. + * @example + * + * function Foo() { + * this.b = 2 + * } + * + * Foo.prototype.c = 3 + * + * assign({ 'a': 1 }, new Foo) + * // => { 'a': 1, 'b': 2 } + * + * assign({ 'a': 1 }, toPlainObject(new Foo)) + * // => { 'a': 1, 'b': 2, 'c': 3 } + */ +function toPlainObject(value) { + value = Object(value); + const result = {}; + for (const key in value) { + result[key] = value[key]; + } + return result; +} + +export default toPlainObject; diff --git a/src/toSafeInteger.ts b/src/toSafeInteger.ts new file mode 100644 index 0000000000..b3580694e1 --- /dev/null +++ b/src/toSafeInteger.ts @@ -0,0 +1,42 @@ +import toInteger from './toInteger.js'; + +/** Used as references for various `Number` constants. */ +const MAX_SAFE_INTEGER = 9007199254740991; + +/** + * Converts `value` to a safe integer. A safe integer can be compared and + * represented correctly. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {number} Returns the converted integer. + * @example + * + * toSafeInteger(3.2) + * // => 3 + * + * toSafeInteger(Number.MIN_VALUE) + * // => 0 + * + * toSafeInteger(Infinity) + * // => 9007199254740991 + * + * toSafeInteger('3.2') + * // => 3 + */ +function toSafeInteger(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toInteger(value); + if (value < -MAX_SAFE_INTEGER) { + return -MAX_SAFE_INTEGER; + } + if (value > MAX_SAFE_INTEGER) { + return MAX_SAFE_INTEGER; + } + return value; +} + +export default toSafeInteger; diff --git a/src/toString.ts b/src/toString.ts new file mode 100644 index 0000000000..7212a38530 --- /dev/null +++ b/src/toString.ts @@ -0,0 +1,44 @@ +import isSymbol from './isSymbol.js'; + +/** Used as references for various `Number` constants. */ +const INFINITY = 1 / 0; + +/** + * Converts `value` to a string. An empty string is returned for `null` + * and `undefined` values. The sign of `-0` is preserved. + * + * @since 4.0.0 + * @category Lang + * @param {*} value The value to convert. + * @returns {string} Returns the converted string. + * @example + * + * toString(null) + * // => '' + * + * toString(-0) + * // => '-0' + * + * toString([1, 2, 3]) + * // => '1,2,3' + */ +function toString(value) { + if (value == null) { + return ''; + } + // Exit early for strings to avoid a performance hit in some environments. + if (typeof value === 'string') { + return value; + } + if (Array.isArray(value)) { + // Recursively convert values (susceptible to call stack limits). + return `${value.map((other) => (other == null ? other : toString(other)))}`; + } + if (isSymbol(value)) { + return value.toString(); + } + const result = `${value}`; + return result === '0' && 1 / value === -INFINITY ? '-0' : result; +} + +export default toString; diff --git a/src/transform.ts b/src/transform.ts new file mode 100644 index 0000000000..19220a7ea3 --- /dev/null +++ b/src/transform.ts @@ -0,0 +1,57 @@ +import arrayEach from './.internal/arrayEach.js'; +import baseForOwn from './.internal/baseForOwn.js'; +import isBuffer from './isBuffer.js'; +import isObject from './isObject.js'; +import isTypedArray from './isTypedArray.js'; + +/** + * An alternative to `reduce` this method transforms `object` to a new + * `accumulator` object which is the result of running each of its own + * enumerable string keyed properties thru `iteratee`, with each invocation + * potentially mutating the `accumulator` object. If `accumulator` is not + * provided, a new object with the same `[[Prototype]]` will be used. The + * iteratee is invoked with four arguments: (accumulator, value, key, object). + * Iteratee functions may exit iteration early by explicitly returning `false`. + * + * @since 1.3.0 + * @category Object + * @param {Object} object The object to iterate over. + * @param {Function} iteratee The function invoked per iteration. + * @param {*} [accumulator] The custom accumulator value. + * @returns {*} Returns the accumulated value. + * @see reduce, reduceRight + * @example + * + * transform([2, 3, 4], (result, n) => { + * result.push(n *= n) + * return n % 2 === 0 + * }, []) + * // => [4, 9] + * + * transform({ 'a': 1, 'b': 2, 'c': 1 }, (result, value, key) => { + * (result[value] || (result[value] = [])).push(key) + * }, {}) + * // => { '1': ['a', 'c'], '2': ['b'] } + */ +function transform(object, iteratee, accumulator) { + const isArr = Array.isArray(object); + const isArrLike = isArr || isBuffer(object) || isTypedArray(object); + + if (accumulator == null) { + const Ctor = object && object.constructor; + if (isArrLike) { + accumulator = isArr ? new Ctor() : []; + } else if (isObject(object)) { + accumulator = + typeof Ctor === 'function' ? Object.create(Object.getPrototypeOf(object)) : {}; + } else { + accumulator = {}; + } + } + (isArrLike ? arrayEach : baseForOwn)(object, (value, index, _object) => + iteratee(accumulator, value, index, _object), + ); + return accumulator; +} + +export default transform; diff --git a/src/trim.ts b/src/trim.ts new file mode 100644 index 0000000000..9e911806ff --- /dev/null +++ b/src/trim.ts @@ -0,0 +1,38 @@ +import castSlice from './.internal/castSlice.js'; +import charsEndIndex from './.internal/charsEndIndex.js'; +import charsStartIndex from './.internal/charsStartIndex.js'; +import stringToArray from './.internal/stringToArray.js'; + +/** + * Removes leading and trailing whitespace or specified characters from `string`. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @see trimEnd, trimStart + * @example + * + * trim(' abc ') + * // => 'abc' + * + * trim('-_-abc-_-', '_-') + * // => 'abc' + */ +function trim(string: string, chars: string) { + if (string && chars === undefined) { + return string.trim(); + } + if (!string || !chars) { + return string || ''; + } + const strSymbols = stringToArray(string); + const chrSymbols = stringToArray(chars); + const start = charsStartIndex(strSymbols, chrSymbols); + const end = charsEndIndex(strSymbols, chrSymbols) + 1; + + return castSlice(strSymbols, start, end).join(''); +} + +export default trim; diff --git a/src/trimEnd.ts b/src/trimEnd.ts new file mode 100644 index 0000000000..cedb58af39 --- /dev/null +++ b/src/trimEnd.ts @@ -0,0 +1,36 @@ +import castSlice from './.internal/castSlice.js'; +import charsEndIndex from './.internal/charsEndIndex.js'; +import stringToArray from './.internal/stringToArray.js'; + +const methodName = ''.trimRight ? 'trimRight' : 'trimEnd'; + +/** + * Removes trailing whitespace or specified characters from `string`. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @see trim, trimStart + * @example + * + * trimEnd(' abc ') + * // => ' abc' + * + * trimEnd('-_-abc-_-', '_-') + * // => '-_-abc' + */ +function trimEnd(string, chars) { + if (string && chars === undefined) { + return string[methodName](); + } + if (!string || !chars) { + return string || ''; + } + const strSymbols = stringToArray(string); + const end = charsEndIndex(strSymbols, stringToArray(chars)) + 1; + return castSlice(strSymbols, 0, end).join(''); +} + +export default trimEnd; diff --git a/src/trimStart.ts b/src/trimStart.ts new file mode 100644 index 0000000000..a96c88c5be --- /dev/null +++ b/src/trimStart.ts @@ -0,0 +1,36 @@ +import castSlice from './.internal/castSlice.js'; +import charsStartIndex from './.internal/charsStartIndex.js'; +import stringToArray from './.internal/stringToArray.js'; + +const methodName = ''.trimLeft ? 'trimLeft' : 'trimStart'; + +/** + * Removes leading whitespace or specified characters from `string`. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to trim. + * @param {string} [chars=whitespace] The characters to trim. + * @returns {string} Returns the trimmed string. + * @see trim, trimEnd + * @example + * + * trimStart(' abc ') + * // => 'abc ' + * + * trimStart('-_-abc-_-', '_-') + * // => 'abc-_-' + */ +function trimStart(string, chars) { + if (string && chars === undefined) { + return string[methodName](); + } + if (!string || !chars) { + return string || ''; + } + const strSymbols = stringToArray(string); + const start = charsStartIndex(strSymbols, stringToArray(chars)); + return castSlice(strSymbols, start).join(''); +} + +export default trimStart; diff --git a/src/truncate.ts b/src/truncate.ts new file mode 100644 index 0000000000..d1b122b3bb --- /dev/null +++ b/src/truncate.ts @@ -0,0 +1,111 @@ +import baseToString from './.internal/baseToString.js'; +import castSlice from './.internal/castSlice.js'; +import hasUnicode from './.internal/hasUnicode.js'; +import isObject from './isObject.js'; +import isRegExp from './isRegExp.js'; +import stringSize from './.internal/stringSize.js'; +import stringToArray from './.internal/stringToArray.js'; +import toString from './toString.js'; + +/** Used as default options for `truncate`. */ +const DEFAULT_TRUNC_LENGTH = 30; +const DEFAULT_TRUNC_OMISSION = '...'; + +/** Used to match `RegExp` flags from their coerced string values. */ +const reFlags = /\w*$/; + +/** + * Truncates `string` if it's longer than the given maximum string length. + * The last characters of the truncated string are replaced with the omission + * string which defaults to "...". + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to truncate. + * @param {Object} [options={}] The options object. + * @param {number} [options.length=30] The maximum string length. + * @param {string} [options.omission='...'] The string to indicate text is omitted. + * @param {RegExp|string} [options.separator] The separator pattern to truncate to. + * @returns {string} Returns the truncated string. + * @see replace + * @example + * + * truncate('hi-diddly-ho there, neighborino') + * // => 'hi-diddly-ho there, neighbo...' + * + * truncate('hi-diddly-ho there, neighborino', { + * 'length': 24, + * 'separator': ' ' + * }) + * // => 'hi-diddly-ho there,...' + * + * truncate('hi-diddly-ho there, neighborino', { + * 'length': 24, + * 'separator': /,? +/ + * }) + * // => 'hi-diddly-ho there...' + * + * truncate('hi-diddly-ho there, neighborino', { + * 'omission': ' [...]' + * }) + * // => 'hi-diddly-ho there, neig [...]' + */ +function truncate(string, options) { + let separator; + let length = DEFAULT_TRUNC_LENGTH; + let omission = DEFAULT_TRUNC_OMISSION; + + if (isObject(options)) { + separator = 'separator' in options ? options.separator : separator; + length = 'length' in options ? options.length : length; + omission = 'omission' in options ? baseToString(options.omission) : omission; + } + + string = toString(string); + + let strSymbols; + let strLength = string.length; + if (hasUnicode(string)) { + strSymbols = stringToArray(string); + strLength = strSymbols.length; + } + if (length >= strLength) { + return string; + } + let end = length - stringSize(omission); + if (end < 1) { + return omission; + } + let result = strSymbols ? castSlice(strSymbols, 0, end).join('') : string.slice(0, end); + + if (separator === undefined) { + return result + omission; + } + if (strSymbols) { + end += result.length - end; + } + if (isRegExp(separator)) { + if (string.slice(end).search(separator)) { + let match; + let newEnd; + const substring = result; + + if (!separator.global) { + separator = RegExp(separator.source, `${reFlags.exec(separator) || ''}g`); + } + separator.lastIndex = 0; + while ((match = separator.exec(substring))) { + newEnd = match.index; + } + result = result.slice(0, newEnd === undefined ? end : newEnd); + } + } else if (string.indexOf(baseToString(separator), end) !== end) { + const index = result.lastIndexOf(separator); + if (index > -1) { + result = result.slice(0, index); + } + } + return result + omission; +} + +export default truncate; diff --git a/src/unescape.ts b/src/unescape.ts new file mode 100644 index 0000000000..c6c63f2edd --- /dev/null +++ b/src/unescape.ts @@ -0,0 +1,38 @@ +/** Used to map HTML entities to characters. */ +const htmlUnescapes = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + ''': "'", +}; + +/** Used to match HTML entities and HTML characters. */ +const reEscapedHtml = /&(?:amp|lt|gt|quot|#(0+)?39);/g; +const reHasEscapedHtml = RegExp(reEscapedHtml.source); + +/** + * The inverse of `escape`this method converts the HTML entities + * `&`, `<`, `>`, `"` and `'` in `string` to + * their corresponding characters. + * + * **Note:** No other HTML entities are unescaped. To unescape additional + * HTML entities use a third-party library like [_he_](https://mths.be/he). + * + * @since 0.6.0 + * @category String + * @param {string} [string=''] The string to unescape. + * @returns {string} Returns the unescaped string. + * @see escape, escapeRegExp + * @example + * + * unescape('fred, barney, & pebbles') + * // => 'fred, barney, & pebbles' + */ +function unescape(string) { + return string && reHasEscapedHtml.test(string) + ? string.replace(reEscapedHtml, (entity) => htmlUnescapes[entity] || "'") + : string || ''; +} + +export default unescape; diff --git a/src/union.ts b/src/union.ts new file mode 100644 index 0000000000..fa4d23ec35 --- /dev/null +++ b/src/union.ts @@ -0,0 +1,24 @@ +import baseFlatten from './.internal/baseFlatten.js'; +import baseUniq from './.internal/baseUniq.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; + +/** + * Creates an array of unique values, in order, from all given arrays using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of combined values. + * @see difference, unionBy, unionWith, without, xor, xorBy + * @example + * + * union([2, 3], [1, 2]) + * // => [2, 3, 1] + */ +function union(...arrays: any[]) { + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)); +} + +export default union; diff --git a/src/unionBy.ts b/src/unionBy.ts new file mode 100644 index 0000000000..dd02ba2d88 --- /dev/null +++ b/src/unionBy.ts @@ -0,0 +1,32 @@ +import baseFlatten from './.internal/baseFlatten.js'; +import baseUniq from './.internal/baseUniq.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `union` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which uniqueness is computed. Result values are chosen from the first + * array in which the value occurs. The iteratee is invoked with one argument: + * (value). + * + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns the new array of combined values. + * @see difference, union, unionWith, without, xor, xorBy + * @example + * + * unionBy([2.1], [1.2, 2.3], Math.floor) + * // => [2.1, 1.2] + */ +function unionBy(...arrays) { + let iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), iteratee); +} + +export default unionBy; diff --git a/src/unionWith.ts b/src/unionWith.ts new file mode 100644 index 0000000000..0237580409 --- /dev/null +++ b/src/unionWith.ts @@ -0,0 +1,32 @@ +import baseFlatten from './.internal/baseFlatten.js'; +import baseUniq from './.internal/baseUniq.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `union` except that it accepts `comparator` which + * is invoked to compare elements of `arrays`. Result values are chosen from + * the first array in which the value occurs. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of combined values. + * @see difference, union, unionBy, without, xor, xorBy + * @example + * + * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + * const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }] + * + * unionWith(objects, others, isEqual) + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ +function unionWith(...arrays) { + let comparator = last(arrays); + comparator = typeof comparator === 'function' ? comparator : undefined; + return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator); +} + +export default unionWith; diff --git a/src/uniq.ts b/src/uniq.ts new file mode 100644 index 0000000000..acb6f351fb --- /dev/null +++ b/src/uniq.ts @@ -0,0 +1,24 @@ +import baseUniq from './.internal/baseUniq.js'; + +/** + * Creates a duplicate-free version of an array, using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons, in which only the first occurrence of each element + * is kept. The order of result values is determined by the order they occur + * in the array. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @returns {Array} Returns the new duplicate free array. + * @see uniqBy, uniqWith + * @example + * + * uniq([2, 1, 2]) + * // => [2, 1] + */ +function uniq(array) { + return array != null && array.length ? baseUniq(array) : []; +} + +export default uniq; diff --git a/src/uniqBy.ts b/src/uniqBy.ts new file mode 100644 index 0000000000..a65d45c59a --- /dev/null +++ b/src/uniqBy.ts @@ -0,0 +1,25 @@ +import baseUniq from './.internal/baseUniq.js'; + +/** + * This method is like `uniq` except that it accepts `iteratee` which is + * invoked for each element in `array` to generate the criterion by which + * uniqueness is computed. The order of result values is determined by the + * order they occur in the array. The iteratee is invoked with one argument: + * (value). + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @see uniq, uniqWith + * @example + * + * uniqBy([2.1, 1.2, 2.3], Math.floor) + * // => [2.1, 1.2] + */ +function uniqBy(array, iteratee) { + return array != null && array.length ? baseUniq(array, iteratee) : []; +} + +export default uniqBy; diff --git a/src/uniqWith.ts b/src/uniqWith.ts new file mode 100644 index 0000000000..9fe627082c --- /dev/null +++ b/src/uniqWith.ts @@ -0,0 +1,27 @@ +import baseUniq from './.internal/baseUniq.js'; + +/** + * This method is like `uniq` except that it accepts `comparator` which + * is invoked to compare elements of `array`. The order of result values is + * determined by the order they occur in the array. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @since 4.0.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new duplicate free array. + * @see uniq, uniqBy + * @example + * + * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }] + * + * uniqWith(objects, isEqual) + * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + */ +function uniqWith(array, comparator) { + comparator = typeof comparator === 'function' ? comparator : undefined; + return array != null && array.length ? baseUniq(array, undefined, comparator) : []; +} + +export default uniqWith; diff --git a/src/uniqueId.ts b/src/uniqueId.ts new file mode 100644 index 0000000000..7e04b38cbd --- /dev/null +++ b/src/uniqueId.ts @@ -0,0 +1,33 @@ +/** Used to generate unique IDs. */ +const idCounter = {}; + +/** + * Generates a unique ID. If `prefix` is given, the ID is appended to it. + * + * @since 0.1.0 + * @category Util + * @param {string} [prefix=''] The value to prefix the ID with. + * @returns {string} Returns the unique ID. + * @see random + * @example + * + * uniqueId('contact_') + * // => 'contact_104' + * + * uniqueId() + * // => '105' + */ +function uniqueId(prefix = '$lodash$') { + if (!idCounter[prefix]) { + idCounter[prefix] = 0; + } + + const id = ++idCounter[prefix]; + if (prefix === '$lodash$') { + return `${id}`; + } + + return `${prefix}${id}`; +} + +export default uniqueId; diff --git a/src/unset.ts b/src/unset.ts new file mode 100644 index 0000000000..3736aef9c1 --- /dev/null +++ b/src/unset.ts @@ -0,0 +1,33 @@ +import baseUnset from './.internal/baseUnset.js'; + +/** + * Removes the property at `path` of `object`. + * + * **Note:** This method mutates `object`. + * + * @since 4.0.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to unset. + * @returns {boolean} Returns `true` if the property is deleted, else `false`. + * @see get, has, set + * @example + * + * const object = { 'a': [{ 'b': { 'c': 7 } }] } + * unset(object, 'a[0].b.c') + * // => true + * + * console.log(object) + * // => { 'a': [{ 'b': {} }] } + * + * unset(object, ['a', '0', 'b', 'c']) + * // => true + * + * console.log(object) + * // => { 'a': [{ 'b': {} }] } + */ +function unset(object, path) { + return object == null ? true : baseUnset(object, path); +} + +export default unset; diff --git a/src/unzip.ts b/src/unzip.ts new file mode 100644 index 0000000000..e137c49d26 --- /dev/null +++ b/src/unzip.ts @@ -0,0 +1,44 @@ +import filter from './filter.js'; +import map from './map.js'; +import baseProperty from './.internal/baseProperty.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; + +/** + * This method is like `zip` except that it accepts an array of grouped + * elements and creates an array regrouping the elements to their pre-zip + * configuration. + * + * @since 1.2.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @returns {Array} Returns the new array of regrouped elements. + * @see unzipWith, zip, zipObject, zipObjectDeep, zipWith + * @example + * + * const zipped = zip(['a', 'b'], [1, 2], [true, false]) + * // => [['a', 1, true], ['b', 2, false]] + * + * unzip(zipped) + * // => [['a', 'b'], [1, 2], [true, false]] + */ +function unzip(array) { + if (!(array != null && array.length)) { + return []; + } + let length = 0; + // eslint-disable-next-line consistent-return + array = filter(array, (group) => { + if (isArrayLikeObject(group)) { + length = Math.max(group.length, length); + return true; + } + }); + let index = -1; + const result = new Array(length); + while (++index < length) { + result[index] = map(array, baseProperty(index)); + } + return result; +} + +export default unzip; diff --git a/src/unzipWith.ts b/src/unzipWith.ts new file mode 100644 index 0000000000..763fe13aa4 --- /dev/null +++ b/src/unzipWith.ts @@ -0,0 +1,31 @@ +import map from './map.js'; +import unzip from './unzip.js'; + +/** + * This method is like `unzip` except that it accepts `iteratee` to specify + * how regrouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @since 3.8.0 + * @category Array + * @param {Array} array The array of grouped elements to process. + * @param {Function} iteratee The function to combine + * regrouped values. + * @returns {Array} Returns the new array of regrouped elements. + * @example + * + * const zipped = zip([1, 2], [10, 20], [100, 200]) + * // => [[1, 10, 100], [2, 20, 200]] + * + * unzipWith(zipped, add) + * // => [3, 30, 300] + */ +function unzipWith(array, iteratee) { + if (!(array != null && array.length)) { + return []; + } + const result = unzip(array); + return map(result, (group) => iteratee.apply(undefined, group)); +} + +export default unzipWith; diff --git a/src/update.ts b/src/update.ts new file mode 100644 index 0000000000..645c2c8cd2 --- /dev/null +++ b/src/update.ts @@ -0,0 +1,32 @@ +import baseUpdate from './.internal/baseUpdate.js'; + +/** + * This method is like `set` except that it accepts `updater` to produce the + * value to set. Use `updateWith` to customize `path` creation. The `updater` + * is invoked with one argument: (value). + * + * **Note:** This method mutates `object`. + * + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @returns {Object} Returns `object`. + * @example + * + * const object = { 'a': [{ 'b': { 'c': 3 } }] } + * + * update(object, 'a[0].b.c', n => n * n) + * console.log(object.a[0].b.c) + * // => 9 + * + * update(object, 'x[0].y.z', n => n ? n + 1 : 0) + * console.log(object.x[0].y.z) + * // => 0 + */ +function update(object, path, updater) { + return object == null ? object : baseUpdate(object, path, updater); +} + +export default update; diff --git a/src/updateWith.ts b/src/updateWith.ts new file mode 100644 index 0000000000..c8abd91fba --- /dev/null +++ b/src/updateWith.ts @@ -0,0 +1,30 @@ +import baseUpdate from './.internal/baseUpdate.js'; + +/** + * This method is like `update` except that it accepts `customizer` which is + * invoked to produce the objects of `path`. If `customizer` returns `undefined` + * path creation is handled by the method instead. The `customizer` is invoked + * with three arguments: (nsValue, key, nsObject). + * + * **Note:** This method mutates `object`. + * + * @since 4.6.0 + * @category Object + * @param {Object} object The object to modify. + * @param {Array|string} path The path of the property to set. + * @param {Function} updater The function to produce the updated value. + * @param {Function} [customizer] The function to customize assigned values. + * @returns {Object} Returns `object`. + * @example + * + * const object = {} + * + * updateWith(object, '[0][1]', () => 'a', Object) + * // => { '0': { '1': 'a' } } + */ +function updateWith(object, path, updater, customizer) { + customizer = typeof customizer === 'function' ? customizer : undefined; + return object == null ? object : baseUpdate(object, path, updater, customizer); +} + +export default updateWith; diff --git a/src/upperCase.ts b/src/upperCase.ts new file mode 100644 index 0000000000..636ad01fbf --- /dev/null +++ b/src/upperCase.ts @@ -0,0 +1,29 @@ +import words from './words.js'; +import toString from './toString.js'; + +/** + * Converts `string`, as space separated words, to upper case. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the upper cased string. + * @see camelCase, kebabCase, lowerCase, snakeCase, startCase, upperFirst + * @example + * + * upperCase('--foo-bar') + * // => 'FOO BAR' + * + * upperCase('fooBar') + * // => 'FOO BAR' + * + * upperCase('__foo_bar__') + * // => 'FOO BAR' + */ +const upperCase = (string) => + words(toString(string).replace(/['\u2019]/g, '')).reduce( + (result, word, index) => result + (index ? ' ' : '') + word.toUpperCase(), + '', + ); + +export default upperCase; diff --git a/src/upperFirst.ts b/src/upperFirst.ts new file mode 100644 index 0000000000..8a41d18e14 --- /dev/null +++ b/src/upperFirst.ts @@ -0,0 +1,21 @@ +import createCaseFirst from './.internal/createCaseFirst.js'; + +/** + * Converts the first character of `string` to upper case. + * + * @since 4.0.0 + * @category String + * @param {string} [string=''] The string to convert. + * @returns {string} Returns the converted string. + * @see camelCase, kebabCase, lowerCase, snakeCase, startCase, upperCase + * @example + * + * upperFirst('fred') + * // => 'Fred' + * + * upperFirst('FRED') + * // => 'FRED' + */ +const upperFirst = createCaseFirst('toUpperCase'); + +export default upperFirst; diff --git a/src/values.ts b/src/values.ts new file mode 100644 index 0000000000..b648d309f3 --- /dev/null +++ b/src/values.ts @@ -0,0 +1,33 @@ +import baseValues from './.internal/baseValues.js'; +import keys from './keys.js'; + +/** + * Creates an array of the own enumerable string keyed property values of `object`. + * + * **Note:** Non-object values are coerced to objects. + * + * @since 0.1.0 + * @category Object + * @param {Object} object The object to query. + * @returns {Array} Returns the array of property values. + * @see keys, valuesIn + * @example + * + * function Foo() { + * this.a = 1 + * this.b = 2 + * } + * + * Foo.prototype.c = 3 + * + * values(new Foo) + * // => [1, 2] (iteration order is not guaranteed) + * + * values('hi') + * // => ['h', 'i'] + */ +function values(object) { + return object == null ? [] : baseValues(object, keys(object)); +} + +export default values; diff --git a/src/without.ts b/src/without.ts new file mode 100644 index 0000000000..02ea17e85d --- /dev/null +++ b/src/without.ts @@ -0,0 +1,26 @@ +import baseDifference from './.internal/baseDifference.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; + +/** + * Creates an array excluding all given values using + * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) + * for equality comparisons. + * + * **Note:** Unlike `pull`, this method returns a new array. + * + * @since 0.1.0 + * @category Array + * @param {Array} array The array to inspect. + * @param {...*} [values] The values to exclude. + * @returns {Array} Returns the new array of filtered values. + * @see difference, union, unionBy, unionWith, xor, xorBy, xorWith + * @example + * + * without([2, 1, 2, 3], 1, 2) + * // => [3] + */ +function without(array, ...values) { + return isArrayLikeObject(array) ? baseDifference(array, values) : []; +} + +export default without; diff --git a/src/words.ts b/src/words.ts new file mode 100644 index 0000000000..39e83809a5 --- /dev/null +++ b/src/words.ts @@ -0,0 +1,39 @@ +import unicodeWords from './.internal/unicodeWords.js'; + +const hasUnicodeWord = RegExp.prototype.test.bind( + /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/, +); + +/** Used to match words composed of alphanumeric characters. */ +// eslint-disable-next-line no-control-regex +const reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g; + +function asciiWords(string) { + return string.match(reAsciiWord); +} + +/** + * Splits `string` into an array of its words. + * + * @since 3.0.0 + * @category String + * @param {string} [string=''] The string to inspect. + * @param {RegExp|string} [pattern] The pattern to match words. + * @returns {Array} Returns the words of `string`. + * @example + * + * words('fred, barney, & pebbles') + * // => ['fred', 'barney', 'pebbles'] + * + * words('fred, barney, & pebbles', /[^, ]+/g) + * // => ['fred', 'barney', '&', 'pebbles'] + */ +function words(string, pattern) { + if (pattern === undefined) { + const result = hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string); + return result || []; + } + return string.match(pattern) || []; +} + +export default words; diff --git a/src/xor.ts b/src/xor.ts new file mode 100644 index 0000000000..19bfce71f5 --- /dev/null +++ b/src/xor.ts @@ -0,0 +1,24 @@ +import baseXor from './.internal/baseXor.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; + +/** + * Creates an array of unique values that is the + * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) + * of the given arrays. The order of result values is determined by the order + * they occur in the arrays. + * + * @since 2.4.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @returns {Array} Returns the new array of filtered values. + * @see difference, union, unionBy, unionWith, without, xorBy, xorWith + * @example + * + * xor([2, 1], [2, 3]) + * // => [1, 3] + */ +function xor(...arrays) { + return baseXor(arrays.filter(isArrayLikeObject)); +} + +export default xor; diff --git a/src/xorBy.ts b/src/xorBy.ts new file mode 100644 index 0000000000..900597287e --- /dev/null +++ b/src/xorBy.ts @@ -0,0 +1,31 @@ +import baseXor from './.internal/baseXor.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `xor` except that it accepts `iteratee` which is + * invoked for each element of each `arrays` to generate the criterion by + * which they're compared. The order of result values is determined + * by the order they occur in the arrays. The iteratee is invoked with one + * argument: (value). + * + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} iteratee The iteratee invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @see difference, union, unionBy, unionWith, without, xor, xorWith + * @example + * + * xorBy([2.1, 1.2], [2.3, 3.4], Math.floor) + * // => [1.2, 3.4] + */ +function xorBy(...arrays) { + let iteratee = last(arrays); + if (isArrayLikeObject(iteratee)) { + iteratee = undefined; + } + return baseXor(arrays.filter(isArrayLikeObject), iteratee); +} + +export default xorBy; diff --git a/src/xorWith.ts b/src/xorWith.ts new file mode 100644 index 0000000000..1d5cb4f26e --- /dev/null +++ b/src/xorWith.ts @@ -0,0 +1,31 @@ +import baseXor from './.internal/baseXor.js'; +import isArrayLikeObject from './isArrayLikeObject.js'; +import last from './last.js'; + +/** + * This method is like `xor` except that it accepts `comparator` which is + * invoked to compare elements of `arrays`. The order of result values is + * determined by the order they occur in the arrays. The comparator is invoked + * with two arguments: (arrVal, othVal). + * + * @since 4.0.0 + * @category Array + * @param {...Array} [arrays] The arrays to inspect. + * @param {Function} [comparator] The comparator invoked per element. + * @returns {Array} Returns the new array of filtered values. + * @see difference, union, unionBy, unionWith, without, xor, xorBy + * @example + * + * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] + * const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }] + * + * xorWith(objects, others, isEqual) + * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] + */ +function xorWith(...arrays) { + let comparator = last(arrays); + comparator = typeof comparator === 'function' ? comparator : undefined; + return baseXor(arrays.filter(isArrayLikeObject), undefined, comparator); +} + +export default xorWith; diff --git a/src/zip.ts b/src/zip.ts new file mode 100644 index 0000000000..7c988ecd71 --- /dev/null +++ b/src/zip.ts @@ -0,0 +1,22 @@ +import unzip from './unzip.js'; + +/** + * Creates an array of grouped elements, the first of which contains the + * first elements of the given arrays, the second of which contains the + * second elements of the given arrays, and so on. + * + * @since 0.1.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @returns {Array} Returns the new array of grouped elements. + * @see unzip, unzipWith, zipObject, zipObjectDeep, zipWith + * @example + * + * zip(['a', 'b'], [1, 2], [true, false]) + * // => [['a', 1, true], ['b', 2, false]] + */ +function zip(...arrays) { + return unzip(arrays); +} + +export default zip; diff --git a/src/zipObject.ts b/src/zipObject.ts new file mode 100644 index 0000000000..cb62d9d05d --- /dev/null +++ b/src/zipObject.ts @@ -0,0 +1,23 @@ +import assignValue from './.internal/assignValue.js'; +import baseZipObject from './.internal/baseZipObject.js'; + +/** + * This method is like `fromPairs` except that it accepts two arrays, + * one of property identifiers and one of corresponding values. + * + * @since 0.4.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @see unzip, unzipWith, zip, zipObjectDeep, zipWith + * @example + * + * zipObject(['a', 'b'], [1, 2]) + * // => { 'a': 1, 'b': 2 } + */ +function zipObject(props, values) { + return baseZipObject(props || [], values || [], assignValue); +} + +export default zipObject; diff --git a/src/zipObjectDeep.ts b/src/zipObjectDeep.ts new file mode 100644 index 0000000000..3d92bd23bb --- /dev/null +++ b/src/zipObjectDeep.ts @@ -0,0 +1,22 @@ +import baseSet from './.internal/baseSet.js'; +import baseZipObject from './.internal/baseZipObject.js'; + +/** + * This method is like `zipObject` except that it supports property paths. + * + * @since 4.1.0 + * @category Array + * @param {Array} [props=[]] The property identifiers. + * @param {Array} [values=[]] The property values. + * @returns {Object} Returns the new object. + * @see unzip, unzipWith, zip, zipObject, zipWith + * @example + * + * zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]) + * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } + */ +function zipObjectDeep(props, values) { + return baseZipObject(props || [], values || [], baseSet); +} + +export default zipObjectDeep; diff --git a/src/zipWith.ts b/src/zipWith.ts new file mode 100644 index 0000000000..d840a9c004 --- /dev/null +++ b/src/zipWith.ts @@ -0,0 +1,27 @@ +import unzipWith from './unzipWith.js'; + +/** + * This method is like `zip` except that it accepts `iteratee` to specify + * how grouped values should be combined. The iteratee is invoked with the + * elements of each group: (...group). + * + * @since 3.8.0 + * @category Array + * @param {...Array} [arrays] The arrays to process. + * @param {Function} iteratee The function to combine + * grouped values. + * @returns {Array} Returns the new array of grouped elements. + * @see unzip, unzipWith, zip, zipObject, zipObjectDeep, zipWith + * @example + * + * zipWith([1, 2], [10, 20], [100, 200], (a, b, c) => a + b + c) + * // => [111, 222] + */ +function zipWith(...arrays) { + const length = arrays.length; + let iteratee = length > 1 ? arrays[length - 1] : undefined; + iteratee = typeof iteratee === 'function' ? (arrays.pop(), iteratee) : undefined; + return unzipWith(arrays, iteratee); +} + +export default zipWith; diff --git a/startCase.js b/startCase.js deleted file mode 100644 index 77190ff948..0000000000 --- a/startCase.js +++ /dev/null @@ -1,30 +0,0 @@ -import upperFirst from './upperFirst.js' -import words from './words.js' - -/** - * Converts `string` to - * [start case](https://en.wikipedia.org/wiki/Letter_case#Stylistic_or_specialised_usage). - * - * @since 3.1.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the start cased string. - * @see camelCase, lowerCase, kebabCase, snakeCase, upperCase, upperFirst - * @example - * - * startCase('--foo-bar--') - * // => 'Foo Bar' - * - * startCase('fooBar') - * // => 'Foo Bar' - * - * startCase('__FOO_BAR__') - * // => 'FOO BAR' - */ -const startCase = (string) => ( - words(`${string}`.replace(/['\u2019]/g, '')).reduce((result, word, index) => ( - result + (index ? ' ' : '') + upperFirst(word) - ), '') -) - -export default startCase diff --git a/startsWith.js b/startsWith.js deleted file mode 100644 index dfdbf551c8..0000000000 --- a/startsWith.js +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Checks if `string` starts with the given target string. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {string} [target] The string to search for. - * @param {number} [position=0] The position to search from. - * @returns {boolean} Returns `true` if `string` starts with `target`, - * else `false`. - * @see endsWith, includes - * @example - * - * startsWith('abc', 'a') - * // => true - * - * startsWith('abc', 'b') - * // => false - * - * startsWith('abc', 'b', 1) - * // => true - */ -function startsWith(string, target, position) { - const { length } = string - position = position == null ? 0 : position - if (position < 0) { - position = 0 - } - else if (position > length) { - position = length - } - target = `${target}` - return string.slice(position, position + target.length) == target -} - -export default startsWith diff --git a/subtract.js b/subtract.js deleted file mode 100644 index bf8ca6dce0..0000000000 --- a/subtract.js +++ /dev/null @@ -1,18 +0,0 @@ -import createMathOperation from './.internal/createMathOperation.js' - -/** - * Subtract two numbers. - * - * @since 4.0.0 - * @category Math - * @param {number} minuend The first number in a subtraction. - * @param {number} subtrahend The second number in a subtraction. - * @returns {number} Returns the difference. - * @example - * - * subtract(6, 4) - * // => 2 - */ -const subtract = createMathOperation((minuend, subtrahend) => minuend - subtrahend, 0) - -export default subtract diff --git a/sum.js b/sum.js deleted file mode 100644 index c9c0c277c3..0000000000 --- a/sum.js +++ /dev/null @@ -1,21 +0,0 @@ -import baseSum from './.internal/baseSum.js' - -/** - * Computes the sum of the values in `array`. - * - * @since 3.4.0 - * @category Math - * @param {Array} array The array to iterate over. - * @returns {number} Returns the sum. - * @example - * - * sum([4, 2, 8, 6]) - * // => 20 - */ -function sum(array) { - return (array != null && array.length) - ? baseSum(array, (value) => value) - : 0 -} - -export default sum diff --git a/sumBy.js b/sumBy.js deleted file mode 100644 index 5775bf5958..0000000000 --- a/sumBy.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseSum from './.internal/baseSum.js' - -/** - * This method is like `sum` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the value to be summed. - * The iteratee is invoked with one argument: (value). - * - * @since 4.0.0 - * @category Math - * @param {Array} array The array to iterate over. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {number} Returns the sum. - * @example - * - * const objects = [{ 'n': 4 }, { 'n': 2 }, { 'n': 8 }, { 'n': 6 }] - * - * sumBy(objects, ({ n }) => n) - * // => 20 - */ -function sumBy(array, iteratee) { - return (array != null && array.length) - ? baseSum(array, iteratee) - : 0 -} - -export default sumBy diff --git a/tail.js b/tail.js deleted file mode 100644 index ef837d4080..0000000000 --- a/tail.js +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Gets all but the first element of `array`. - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to query. - * @returns {Array} Returns the slice of `array`. - * @example - * - * tail([1, 2, 3]) - * // => [2, 3] - */ -function tail(array) { - const length = array == null ? 0 : array.length - if (!length) { - return [] - } - const [, ...result] = array - return result -} - -export default tail diff --git a/take.js b/take.js deleted file mode 100644 index 890118b9bd..0000000000 --- a/take.js +++ /dev/null @@ -1,32 +0,0 @@ -import slice from './slice.js' - -/** - * Creates a slice of `array` with `n` elements taken from the beginning. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @returns {Array} Returns the slice of `array`. - * @example - * - * take([1, 2, 3]) - * // => [1] - * - * take([1, 2, 3], 2) - * // => [1, 2] - * - * take([1, 2, 3], 5) - * // => [1, 2, 3] - * - * take([1, 2, 3], 0) - * // => [] - */ -function take(array, n=1) { - if (!(array != null && array.length)) { - return [] - } - return slice(array, 0, n < 0 ? 0 : n) -} - -export default take diff --git a/takeRight.js b/takeRight.js deleted file mode 100644 index 3ad2a57a70..0000000000 --- a/takeRight.js +++ /dev/null @@ -1,34 +0,0 @@ -import slice from './slice.js' - -/** - * Creates a slice of `array` with `n` elements taken from the end. - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {number} [n=1] The number of elements to take. - * @returns {Array} Returns the slice of `array`. - * @example - * - * takeRight([1, 2, 3]) - * // => [3] - * - * takeRight([1, 2, 3], 2) - * // => [2, 3] - * - * takeRight([1, 2, 3], 5) - * // => [1, 2, 3] - * - * takeRight([1, 2, 3], 0) - * // => [] - */ -function takeRight(array, n=1) { - const length = array == null ? 0 : array.length - if (!length) { - return [] - } - n = length - n - return slice(array, n < 0 ? 0 : n, length) -} - -export default takeRight diff --git a/takeRightWhile.js b/takeRightWhile.js deleted file mode 100644 index 01cccbe13e..0000000000 --- a/takeRightWhile.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseWhile from './.internal/baseWhile.js' - -/** - * Creates a slice of `array` with elements taken from the end. Elements are - * taken until `predicate` returns falsey. The predicate is invoked with - * three arguments: (value, index, array). - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * const users = [ - * { 'user': 'barney', 'active': false }, - * { 'user': 'fred', 'active': true }, - * { 'user': 'pebbles', 'active': true } - * ] - * - * takeRightWhile(users, ({ active }) => active) - * // => objects for ['fred', 'pebbles'] - */ -function takeRightWhile(array, predicate) { - return (array != null && array.length) - ? baseWhile(array, predicate, false, true) - : [] -} - -export default takeRightWhile diff --git a/takeWhile.js b/takeWhile.js deleted file mode 100644 index ec24da5ed5..0000000000 --- a/takeWhile.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseWhile from './.internal/baseWhile.js' - -/** - * Creates a slice of `array` with elements taken from the beginning. Elements - * are taken until `predicate` returns falsey. The predicate is invoked with - * three arguments: (value, index, array). - * - * @since 3.0.0 - * @category Array - * @param {Array} array The array to query. - * @param {Function} predicate The function invoked per iteration. - * @returns {Array} Returns the slice of `array`. - * @example - * - * const users = [ - * { 'user': 'barney', 'active': true }, - * { 'user': 'fred', 'active': true }, - * { 'user': 'pebbles', 'active': false } - * ] - * - * takeWhile(users, ({ active }) => active) - * // => objects for ['barney', 'fred'] - */ -function takeWhile(array, predicate) { - return (array != null && array.length) - ? baseWhile(array, predicate) - : [] -} - -export default takeWhile diff --git a/test/.eslintrc.cjs b/test/.eslintrc.cjs new file mode 100644 index 0000000000..a79516b6ee --- /dev/null +++ b/test/.eslintrc.cjs @@ -0,0 +1,22 @@ +'use strict'; + +const path = require('node:path'); + +module.exports = { + globals: { + describe: 'readonly', + expect: 'readonly', + it: 'readonly', + xdescribe: 'readonly', + xit: 'readonly' + }, + overrides: [ + { + files: ['**/*.{ts}'], + rules: { + 'import/no-unresolved': 'off', + 'import/no-extraneous-dependencies': 'off', + }, + }, + ], +}; diff --git a/test/Arrays-category-methods.js b/test/Arrays-category-methods.js deleted file mode 100644 index 79cd159546..0000000000 --- a/test/Arrays-category-methods.js +++ /dev/null @@ -1,97 +0,0 @@ -import assert from 'assert'; -import { args, toArgs, identity } from './utils.js'; -import difference from '../difference.js'; -import union from '../union.js'; -import compact from '../compact.js'; -import drop from '../drop.js'; -import dropRight from '../dropRight.js'; -import dropRightWhile from '../dropRightWhile.js'; -import dropWhile from '../dropWhile.js'; -import findIndex from '../findIndex.js'; -import findLastIndex from '../findLastIndex.js'; -import flatten from '../flatten.js'; -import head from '../head.js'; -import indexOf from '../indexOf.js'; -import initial from '../initial.js'; -import intersection from '../intersection.js'; -import last from '../last.js'; -import lastIndexOf from '../lastIndexOf.js'; -import sortedIndex from '../sortedIndex.js'; -import sortedIndexOf from '../sortedIndexOf.js'; -import sortedLastIndex from '../sortedLastIndex.js'; -import sortedLastIndexOf from '../sortedLastIndexOf.js'; -import tail from '../tail.js'; -import take from '../take.js'; -import takeRight from '../takeRight.js'; -import takeRightWhile from '../takeRightWhile.js'; -import takeWhile from '../takeWhile.js'; -import uniq from '../uniq.js'; -import without from '../without.js'; -import zip from '../zip.js'; -import xor from '../xor.js'; - -describe('"Arrays" category methods', function() { - var args = toArgs([1, null, [3], null, 5]), - sortedArgs = toArgs([1, [3], 5, null, null]), - array = [1, 2, 3, 4, 5, 6]; - - it('should work with `arguments` objects', function() { - function message(methodName) { - return '`_.' + methodName + '` should work with `arguments` objects'; - } - - assert.deepStrictEqual(difference(args, [null]), [1, [3], 5], message('difference')); - assert.deepStrictEqual(difference(array, args), [2, 3, 4, 6], '_.difference should work with `arguments` objects as secondary arguments'); - - assert.deepStrictEqual(union(args, [null, 6]), [1, null, [3], 5, 6], message('union')); - assert.deepStrictEqual(union(array, args), array.concat([null, [3]]), '_.union should work with `arguments` objects as secondary arguments'); - - assert.deepStrictEqual(compact(args), [1, [3], 5], message('compact')); - assert.deepStrictEqual(drop(args, 3), [null, 5], message('drop')); - assert.deepStrictEqual(dropRight(args, 3), [1, null], message('dropRight')); - assert.deepStrictEqual(dropRightWhile(args,identity), [1, null, [3], null], message('dropRightWhile')); - assert.deepStrictEqual(dropWhile(args,identity), [null, [3], null, 5], message('dropWhile')); - assert.deepStrictEqual(findIndex(args, identity), 0, message('findIndex')); - assert.deepStrictEqual(findLastIndex(args, identity), 4, message('findLastIndex')); - assert.deepStrictEqual(flatten(args), [1, null, 3, null, 5], message('flatten')); - assert.deepStrictEqual(head(args), 1, message('head')); - assert.deepStrictEqual(indexOf(args, 5), 4, message('indexOf')); - assert.deepStrictEqual(initial(args), [1, null, [3], null], message('initial')); - assert.deepStrictEqual(intersection(args, [1]), [1], message('intersection')); - assert.deepStrictEqual(last(args), 5, message('last')); - assert.deepStrictEqual(lastIndexOf(args, 1), 0, message('lastIndexOf')); - assert.deepStrictEqual(sortedIndex(sortedArgs, 6), 3, message('sortedIndex')); - assert.deepStrictEqual(sortedIndexOf(sortedArgs, 5), 2, message('sortedIndexOf')); - assert.deepStrictEqual(sortedLastIndex(sortedArgs, 5), 3, message('sortedLastIndex')); - assert.deepStrictEqual(sortedLastIndexOf(sortedArgs, 1), 0, message('sortedLastIndexOf')); - assert.deepStrictEqual(tail(args, 4), [null, [3], null, 5], message('tail')); - assert.deepStrictEqual(take(args, 2), [1, null], message('take')); - assert.deepStrictEqual(takeRight(args, 1), [5], message('takeRight')); - assert.deepStrictEqual(takeRightWhile(args, identity), [5], message('takeRightWhile')); - assert.deepStrictEqual(takeWhile(args, identity), [1], message('takeWhile')); - assert.deepStrictEqual(uniq(args), [1, null, [3], 5], message('uniq')); - assert.deepStrictEqual(without(args, null), [1, [3], 5], message('without')); - assert.deepStrictEqual(zip(args, args), [[1, 1], [null, null], [[3], [3]], [null, null], [5, 5]], message('zip')); - }); - - it('should accept falsey primary arguments', function() { - function message(methodName) { - return '`_.' + methodName + '` should accept falsey primary arguments'; - } - - assert.deepStrictEqual(difference(null, array), [], message('difference')); - assert.deepStrictEqual(intersection(null, array), [], message('intersection')); - assert.deepStrictEqual(union(null, array), array, message('union')); - assert.deepStrictEqual(xor(null, array), array, message('xor')); - }); - - it('should accept falsey secondary arguments', function() { - function message(methodName) { - return '`_.' + methodName + '` should accept falsey secondary arguments'; - } - - assert.deepStrictEqual(difference(array, null), array, message('difference')); - assert.deepStrictEqual(intersection(array, null), [], message('intersection')); - assert.deepStrictEqual(union(array, null), array, message('union')); - }); -}); diff --git a/test/Arrays-category-methods.spec.js b/test/Arrays-category-methods.spec.js new file mode 100644 index 0000000000..d83c9c87ec --- /dev/null +++ b/test/Arrays-category-methods.spec.js @@ -0,0 +1,122 @@ +import { args, toArgs, identity } from './utils'; +import difference from '../src/difference'; +import union from '../src/union'; +import compact from '../src/compact'; +import drop from '../src/drop'; +import dropRight from '../src/dropRight'; +import dropRightWhile from '../src/dropRightWhile'; +import dropWhile from '../src/dropWhile'; +import findIndex from '../src/findIndex'; +import findLastIndex from '../src/findLastIndex'; +import flatten from '../src/flatten'; +import head from '../src/head'; +import indexOf from '../src/indexOf'; +import initial from '../src/initial'; +import intersection from '../src/intersection'; +import last from '../src/last'; +import lastIndexOf from '../src/lastIndexOf'; +import sortedIndex from '../src/sortedIndex'; +import sortedIndexOf from '../src/sortedIndexOf'; +import sortedLastIndex from '../src/sortedLastIndex'; +import sortedLastIndexOf from '../src/sortedLastIndexOf'; +import tail from '../src/tail'; +import take from '../src/take'; +import takeRight from '../src/takeRight'; +import takeRightWhile from '../src/takeRightWhile'; +import takeWhile from '../src/takeWhile'; +import uniq from '../src/uniq'; +import without from '../src/without'; +import zip from '../src/zip'; +import xor from '../src/xor'; + +describe('"Arrays" category methods', () => { + const args = toArgs([1, null, [3], null, 5]); + const sortedArgs = toArgs([1, [3], 5, null, null]); + const array = [1, 2, 3, 4, 5, 6]; + + it('should work with `arguments` objects', () => { + function message(methodName) { + return `\`_.${methodName}\` should work with \`arguments\` objects`; + } + + expect(difference(args, [null]), [1, [3], 5]).toEqual(message('difference')); + assert.deepStrictEqual( + difference(array, args), + [2, 3, 4, 6], + '_.difference should work with `arguments` objects as secondary arguments', + ); + + expect(union(args, [null, 6]), [1, null, [3], 5, 6]).toEqual(message('union')); + assert.deepStrictEqual( + union(array, args), + array.concat([null, [3]]), + '_.union should work with `arguments` objects as secondary arguments', + ); + + expect(compact(args), [1, [3], 5]).toEqual(message('compact')); + expect(drop(args, 3), [null, 5]).toEqual(message('drop')); + expect(dropRight(args, 3), [1, null]).toEqual(message('dropRight')); + assert.deepStrictEqual( + dropRightWhile(args, identity), + [1, null, [3], null], + message('dropRightWhile'), + ); + assert.deepStrictEqual( + dropWhile(args, identity), + [null, [3], null, 5], + message('dropWhile'), + ); + expect(findIndex(args, identity), 0).toEqual(message('findIndex')); + expect(findLastIndex(args, identity), 4).toEqual(message('findLastIndex')); + expect(flatten(args), [1, null, 3, null, 5]).toEqual(message('flatten')); + expect(head(args), 1).toEqual(message('head')); + expect(indexOf(args, 5), 4).toEqual(message('indexOf')); + expect(initial(args), [1, null, [3], null]).toEqual(message('initial')); + expect(intersection(args, [1]), [1]).toEqual(message('intersection')); + expect(last(args), 5).toEqual(message('last')); + expect(lastIndexOf(args, 1), 0).toEqual(message('lastIndexOf')); + expect(sortedIndex(sortedArgs, 6), 3).toEqual(message('sortedIndex')); + expect(sortedIndexOf(sortedArgs, 5), 2).toEqual(message('sortedIndexOf')); + expect(sortedLastIndex(sortedArgs, 5), 3).toEqual(message('sortedLastIndex')); + expect(sortedLastIndexOf(sortedArgs, 1), 0).toEqual(message('sortedLastIndexOf')); + expect(tail(args, 4), [null, [3], null, 5]).toEqual(message('tail')); + expect(take(args, 2), [1, null]).toEqual(message('take')); + expect(takeRight(args, 1), [5]).toEqual(message('takeRight')); + expect(takeRightWhile(args, identity), [5]).toEqual(message('takeRightWhile')); + expect(takeWhile(args, identity), [1]).toEqual(message('takeWhile')); + expect(uniq(args), [1, null, [3], 5]).toEqual(message('uniq')); + expect(without(args, null), [1, [3], 5]).toEqual(message('without')); + assert.deepStrictEqual( + zip(args, args), + [ + [1, 1], + [null, null], + [[3], [3]], + [null, null], + [5, 5], + ], + message('zip'), + ); + }); + + it('should accept falsey primary arguments', () => { + function message(methodName) { + return `\`_.${methodName}\` should accept falsey primary arguments`; + } + + expect(difference(null, array), []).toEqual(message('difference')); + expect(intersection(null, array), []).toEqual(message('intersection')); + expect(union(null, array), array).toEqual(message('union')); + expect(xor(null, array), array).toEqual(message('xor')); + }); + + it('should accept falsey secondary arguments', () => { + function message(methodName) { + return `\`_.${methodName}\` should accept falsey secondary arguments`; + } + + expect(difference(array, null), array).toEqual(message('difference')); + expect(intersection(array, null), []).toEqual(message('intersection')); + expect(union(array, null), array).toEqual(message('union')); + }); +}); diff --git a/test/Strings-category-methods.spec.js b/test/Strings-category-methods.spec.js new file mode 100644 index 0000000000..7b69665f06 --- /dev/null +++ b/test/Strings-category-methods.spec.js @@ -0,0 +1,80 @@ +import lodashStable from 'lodash'; +import { stubString } from './utils'; + +import camelCase from '../src/camelCase'; +import capitalize from '../src/capitalize'; +import escape from '../src/escape'; +import kebabCase from '../src/kebabCase'; +import lowerCase from '../src/lowerCase'; +import lowerFirst from '../src/lowerFirst'; +import pad from '../src/pad'; +import padEnd from '../src/padEnd'; +import padStart from '../src/padStart'; +import repeat from '../src/repeat'; +import snakeCase from '../src/snakeCase'; +import trim from '../src/trim'; +import trimStart from '../src/trimStart'; +import trimEnd from '../src/trimEnd'; +import truncate from '../src/truncate'; +import unescape from '../src/unescape'; +import upperCase from '../src/upperCase'; +import upperFirst from '../src/upperFirst'; + +const methods = { + camelCase, + capitalize, + escape, + kebabCase, + lowerCase, + lowerFirst, + pad, + padEnd, + padStart, + repeat, + snakeCase, + trim, + trimStart, + trimEnd, + truncate, + unescape, + upperCase, + upperFirst, +}; + +describe('"Strings" category methods', () => { + const stringMethods = [ + 'camelCase', + 'capitalize', + 'escape', + 'kebabCase', + 'lowerCase', + 'lowerFirst', + 'pad', + 'padEnd', + 'padStart', + 'repeat', + 'snakeCase', + 'trim', + 'trimEnd', + 'trimStart', + 'truncate', + 'unescape', + 'upperCase', + 'upperFirst', + ]; + + lodashStable.each(stringMethods, (methodName) => { + const func = methods[methodName]; + + it(`\`_.${methodName}\` should return an empty string for empty values`, () => { + const values = [, null, undefined, '']; + const expected = lodashStable.map(values, stubString); + + const actual = lodashStable.map(values, (value, index) => + index ? func(value) : func(), + ); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/Strings-category-methods.test.js b/test/Strings-category-methods.test.js deleted file mode 100644 index 781a050ef2..0000000000 --- a/test/Strings-category-methods.test.js +++ /dev/null @@ -1,83 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubString } from './utils.js'; - -import camelCase from '../camelCase.js'; -import capitalize from '../capitalize.js'; -import escape from '../escape.js'; -import kebabCase from '../kebabCase.js'; -import lowerCase from '../lowerCase.js'; -import lowerFirst from '../lowerFirst.js'; -import pad from '../pad.js'; -import padEnd from '../padEnd.js'; -import padStart from '../padStart.js'; -import repeat from '../repeat.js'; -import snakeCase from '../snakeCase.js'; -import trim from '../trim.js'; -import trimStart from '../trimStart.js'; -import trimEnd from '../trimEnd.js'; -import truncate from '../truncate.js'; -import unescape from '../unescape.js'; -import upperCase from '../upperCase.js'; -import upperFirst from '../upperFirst'; - - -const methods = { - camelCase, - capitalize, - escape, - kebabCase, - lowerCase, - lowerFirst, - pad, - padEnd, - padStart, - repeat, - snakeCase, - trim, - trimStart, - trimEnd, - truncate, - unescape, - upperCase, - upperFirst -} - - -describe('"Strings" category methods', function() { - var stringMethods = [ - 'camelCase', - 'capitalize', - 'escape', - 'kebabCase', - 'lowerCase', - 'lowerFirst', - 'pad', - 'padEnd', - 'padStart', - 'repeat', - 'snakeCase', - 'trim', - 'trimEnd', - 'trimStart', - 'truncate', - 'unescape', - 'upperCase', - 'upperFirst' - ]; - - lodashStable.each(stringMethods, function(methodName) { - var func = methods[methodName]; - - it('`_.' + methodName + '` should return an empty string for empty values', function() { - var values = [, null, undefined, ''], - expected = lodashStable.map(values, stubString); - - var actual = lodashStable.map(values, function(value, index) { - return index ? func(value) : func(); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/__proto__-property-bugs.js b/test/__proto__-property-bugs.js deleted file mode 100644 index 4814e441de..0000000000 --- a/test/__proto__-property-bugs.js +++ /dev/null @@ -1,87 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, isEven, _, create, stubFalse, objectProto, funcProto } from './utils.js'; -import difference from '../difference.js'; -import intersection from '../intersection.js'; -import uniq from '../uniq.js'; -import without from '../without.js'; -import groupBy from '../groupBy.js'; -import merge from '../merge.js'; - -describe('`__proto__` property bugs', function() { - it('should work with the "__proto__" key in internal data objects', function() { - var stringLiteral = '__proto__', - stringObject = Object(stringLiteral), - expected = [stringLiteral, stringObject]; - - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, function(count) { - return isEven(count) ? stringLiteral : stringObject; - }); - - assert.deepStrictEqual(difference(largeArray, largeArray), []); - assert.deepStrictEqual(intersection(largeArray, largeArray), expected); - assert.deepStrictEqual(uniq(largeArray), expected); - assert.deepStrictEqual(without.apply(_, [largeArray].concat(largeArray)), []); - }); - - it('should treat "__proto__" as a regular key in assignments', function() { - var methods = [ - 'assign', - 'assignIn', - 'defaults', - 'defaultsDeep', - 'merge' - ]; - - var source = create(null); - source.__proto__ = []; - - var expected = lodashStable.map(methods, stubFalse); - - var actual = lodashStable.map(methods, function(methodName) { - var result = _[methodName]({}, source); - return result instanceof Array; - }); - - assert.deepStrictEqual(actual, expected); - - actual = groupBy([{ 'a': '__proto__' }], 'a'); - assert.ok(!(actual instanceof Array)); - }); - - it('should not merge "__proto__" properties', function() { - if (JSON) { - merge({}, JSON.parse('{"__proto__":{"a":1}}')); - - var actual = 'a' in objectProto; - delete objectProto.a; - - assert.ok(!actual); - } - }); - - it('should not indirectly merge builtin prototype properties', function() { - merge({}, { 'toString': { 'constructor': { 'prototype': { 'a': 1 } } } }); - - var actual = 'a' in funcProto; - delete funcProto.a; - - assert.ok(!actual); - - merge({}, { 'constructor': { 'prototype': { 'a': 1 } } }); - - actual = 'a' in objectProto; - delete objectProto.a; - - assert.ok(!actual); - }); - - it('should not indirectly merge `Object` properties', function() { - merge({}, { 'constructor': { 'a': 1 } }); - - var actual = 'a' in Object; - delete Object.a; - - assert.ok(!actual); - }); -}); diff --git a/test/__proto__-property-bugs.spec.js b/test/__proto__-property-bugs.spec.js new file mode 100644 index 0000000000..30e6bdd450 --- /dev/null +++ b/test/__proto__-property-bugs.spec.js @@ -0,0 +1,80 @@ +import lodashStable from 'lodash'; +import { LARGE_ARRAY_SIZE, isEven, _, create, stubFalse, objectProto, funcProto } from './utils'; +import difference from '../src/difference'; +import intersection from '../src/intersection'; +import uniq from '../src/uniq'; +import without from '../src/without'; +import groupBy from '../src/groupBy'; +import merge from '../src/merge'; + +describe('`__proto__` property bugs', () => { + it('should work with the "__proto__" key in internal data objects', () => { + const stringLiteral = '__proto__'; + const stringObject = Object(stringLiteral); + const expected = [stringLiteral, stringObject]; + + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, (count) => + isEven(count) ? stringLiteral : stringObject, + ); + + expect(difference(largeArray, largeArray)).toEqual([]); + expect(intersection(largeArray, largeArray)).toEqual(expected); + expect(uniq(largeArray)).toEqual(expected); + expect(without.apply(_, [largeArray].concat(largeArray))).toEqual([]); + }); + + it('should treat "__proto__" as a regular key in assignments', () => { + const methods = ['assign', 'assignIn', 'defaults', 'defaultsDeep', 'merge']; + + const source = create(null); + source.__proto__ = []; + + const expected = lodashStable.map(methods, stubFalse); + + let actual = lodashStable.map(methods, (methodName) => { + const result = _[methodName]({}, source); + return result instanceof Array; + }); + + expect(actual).toEqual(expected); + + actual = groupBy([{ a: '__proto__' }], 'a'); + expect(actual instanceof Array).toBe(false); + }); + + it('should not merge "__proto__" properties', () => { + if (JSON) { + merge({}, JSON.parse('{"__proto__":{"a":1}}')); + + const actual = 'a' in objectProto; + delete objectProto.a; + + expect(actual).toBe(false); + } + }); + + it('should not indirectly merge builtin prototype properties', () => { + merge({}, { toString: { constructor: { prototype: { a: 1 } } } }); + + let actual = 'a' in funcProto; + delete funcProto.a; + + expect(actual).toBe(false); + + merge({}, { constructor: { prototype: { a: 1 } } }); + + actual = 'a' in objectProto; + delete objectProto.a; + + expect(actual).toBe(false); + }); + + it('should not indirectly merge `Object` properties', () => { + merge({}, { constructor: { a: 1 } }); + + const actual = 'a' in Object; + delete Object.a; + + expect(actual).toBe(false); + }); +}); diff --git a/test/add.spec.js b/test/add.spec.js new file mode 100644 index 0000000000..da21696775 --- /dev/null +++ b/test/add.spec.js @@ -0,0 +1,14 @@ +import add from '../src/add'; + +describe('add', () => { + it('should add two numbers', () => { + expect(add(6, 4)).toBe(10); + expect(add(-6, 4)).toBe(-2); + expect(add(-6, -4)).toBe(-10); + }); + + it('should not coerce arguments to numbers', () => { + expect(add('6', '4')).toBe('64'); + expect(add('x', 'y')).toBe('xy'); + }); +}); diff --git a/test/add.test.js b/test/add.test.js deleted file mode 100644 index cbc12a0bcc..0000000000 --- a/test/add.test.js +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'assert'; -import add from '../add.js'; - -describe('add', function() { - it('should add two numbers', function() { - assert.strictEqual(add(6, 4), 10); - assert.strictEqual(add(-6, 4), -2); - assert.strictEqual(add(-6, -4), -10); - }); - - it('should not coerce arguments to numbers', function() { - assert.strictEqual(add('6', '4'), '64'); - assert.strictEqual(add('x', 'y'), 'xy'); - }); -}); diff --git a/test/after.spec.js b/test/after.spec.js new file mode 100644 index 0000000000..e0babd5366 --- /dev/null +++ b/test/after.spec.js @@ -0,0 +1,41 @@ +import lodashStable from 'lodash'; +import after from '../src/after'; + +describe('after', () => { + function testAfter(n, times) { + let count = 0; + lodashStable.times( + times, + after(n, () => { + count++; + }), + ); + return count; + } + + it('should create a function that invokes `func` after `n` calls', () => { + // 'after(n) should invoke `func` after being called `n` times' + expect(testAfter(5, 5)).toBe(1); + // 'after(n) should not invoke `func` before being called `n` times' + expect(testAfter(5, 4)).toBe(0); + // 'after(0) should not invoke `func` immediately' + expect(testAfter(0, 0)).toBe(0); + // 'after(0) should invoke `func` when called once' + expect(testAfter(0, 1)).toBe(1); + }); + + it('should coerce `n` values of `NaN` to `0`', () => { + expect(testAfter(NaN, 1)).toBe(1); + }); + + it('should use `this` binding of function', () => { + const afterFn = after(1, function () { + return ++this.count; + }); + const object = { after: afterFn, count: 0 }; + + object.after(); + expect(object.after()).toBe(2); + expect(object.count).toBe(2); + }); +}); diff --git a/test/after.test.js b/test/after.test.js deleted file mode 100644 index ab509ecd18..0000000000 --- a/test/after.test.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import after from '../after.js'; - -describe('after', function() { - function testAfter(n, times) { - var count = 0; - lodashStable.times(times, after(n, function() { count++; })); - return count; - } - - it('should create a function that invokes `func` after `n` calls', function() { - assert.strictEqual(testAfter(5, 5), 1, 'after(n) should invoke `func` after being called `n` times'); - assert.strictEqual(testAfter(5, 4), 0, 'after(n) should not invoke `func` before being called `n` times'); - assert.strictEqual(testAfter(0, 0), 0, 'after(0) should not invoke `func` immediately'); - assert.strictEqual(testAfter(0, 1), 1, 'after(0) should invoke `func` when called once'); - }); - - it('should coerce `n` values of `NaN` to `0`', function() { - assert.strictEqual(testAfter(NaN, 1), 1); - }); - - it('should use `this` binding of function', function() { - var afterFn = after(1, function() { return ++this.count; }), - object = { 'after': afterFn, 'count': 0 }; - - object.after(); - assert.strictEqual(object.after(), 2); - assert.strictEqual(object.count, 2); - }); -}); diff --git a/test/ary.js b/test/ary.js deleted file mode 100644 index 80e5fb2863..0000000000 --- a/test/ary.js +++ /dev/null @@ -1,91 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, _ } from './utils.js'; -import ary from '../ary.js'; -import curry from '../curry.js'; -import rearg from '../rearg.js'; - -describe('ary', function() { - function fn(a, b, c) { - return slice.call(arguments); - } - - it('should cap the number of arguments provided to `func`', function() { - var actual = lodashStable.map(['6', '8', '10'], ary(parseInt, 1)); - assert.deepStrictEqual(actual, [6, 8, 10]); - - var capped = ary(fn, 2); - assert.deepStrictEqual(capped('a', 'b', 'c', 'd'), ['a', 'b']); - }); - - it('should use `func.length` if `n` is not given', function() { - var capped = ary(fn); - assert.deepStrictEqual(capped('a', 'b', 'c', 'd'), ['a', 'b', 'c']); - }); - - it('should treat a negative `n` as `0`', function() { - var capped = ary(fn, -1); - - try { - var actual = capped('a'); - } catch (e) {} - - assert.deepStrictEqual(actual, []); - }); - - it('should coerce `n` to an integer', function() { - var values = ['1', 1.6, 'xyz'], - expected = [['a'], ['a'], []]; - - var actual = lodashStable.map(values, function(n) { - var capped = ary(fn, n); - return capped('a', 'b'); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should not force a minimum argument count', function() { - var args = ['a', 'b', 'c'], - capped = ary(fn, 3); - - var expected = lodashStable.map(args, function(arg, index) { - return args.slice(0, index); - }); - - var actual = lodashStable.map(expected, function(array) { - return capped.apply(undefined, array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should use `this` binding of function', function() { - var capped = ary(function(a, b) { return this; }, 1), - object = { 'capped': capped }; - - assert.strictEqual(object.capped(), object); - }); - - it('should use the existing `ary` if smaller', function() { - var capped = ary(ary(fn, 1), 2); - assert.deepStrictEqual(capped('a', 'b', 'c'), ['a']); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var funcs = lodashStable.map([fn], ary), - actual = funcs[0]('a', 'b', 'c'); - - assert.deepStrictEqual(actual, ['a', 'b', 'c']); - }); - - it('should work when combined with other methods that use metadata', function() { - var array = ['a', 'b', 'c'], - includes = curry(rearg(ary(_.includes, 2), 1, 0), 2); - - assert.strictEqual(includes('b')(array, 2), true); - - includes = _(_.includes).ary(2).rearg(1, 0).curry(2).value(); - assert.strictEqual(includes('b')(array, 2), true); - }); -}); diff --git a/test/ary.spec.js b/test/ary.spec.js new file mode 100644 index 0000000000..2e4eaaa3b3 --- /dev/null +++ b/test/ary.spec.js @@ -0,0 +1,76 @@ +import lodashStable from 'lodash'; +import { slice } from './utils'; +import ary from '../src/ary'; + +describe('ary', () => { + function fn(a, b, c) { + return slice.call(arguments); + } + + it('should cap the number of arguments provided to `func`', () => { + const actual = lodashStable.map(['6', '8', '10'], ary(parseInt, 1)); + expect(actual).toEqual([6, 8, 10]); + + const capped = ary(fn, 2); + expect(capped('a', 'b', 'c', 'd')).toEqual(['a', 'b']); + }); + + it('should use `func.length` if `n` is not given', () => { + const capped = ary(fn); + expect(capped('a', 'b', 'c', 'd')).toEqual(['a', 'b', 'c']); + }); + + it('should treat a negative `n` as `0`', () => { + const capped = ary(fn, -1); + + try { + var actual = capped('a'); + } catch (e) {} + + expect(actual).toEqual([]); + }); + + it('should coerce `n` to an integer', () => { + const values = ['1', 1.6, 'xyz']; + const expected = [['a'], ['a'], []]; + + const actual = lodashStable.map(values, (n) => { + const capped = ary(fn, n); + return capped('a', 'b'); + }); + + expect(actual).toEqual(expected); + }); + + it('should not force a minimum argument count', () => { + const args = ['a', 'b', 'c']; + const capped = ary(fn, 3); + + const expected = lodashStable.map(args, (arg, index) => args.slice(0, index)); + + const actual = lodashStable.map(expected, (array) => capped.apply(undefined, array)); + + expect(actual).toEqual(expected); + }); + + it('should use `this` binding of function', () => { + const capped = ary(function (a, b) { + return this; + }, 1); + const object = { capped: capped }; + + expect(object.capped()).toBe(object); + }); + + it('should use the existing `ary` if smaller', () => { + const capped = ary(ary(fn, 1), 2); + expect(capped('a', 'b', 'c')).toEqual(['a']); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const funcs = lodashStable.map([fn], ary); + const actual = funcs[0]('a', 'b', 'c'); + + expect(actual).toEqual(['a', 'b', 'c']); + }); +}); diff --git a/test/assign-and-assignIn.js b/test/assign-and-assignIn.js deleted file mode 100644 index 251d199ce3..0000000000 --- a/test/assign-and-assignIn.js +++ /dev/null @@ -1,88 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, defineProperty, stubOne, noop, stubNaN } from './utils.js'; - -describe('assign and assignIn', function() { - lodashStable.each(['assign', 'assignIn'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should assign source properties to `object`', function() { - assert.deepStrictEqual(func({ 'a': 1 }, { 'b': 2 }), { 'a': 1, 'b': 2 }); - }); - - it('`_.' + methodName + '` should accept multiple sources', function() { - var expected = { 'a': 1, 'b': 2, 'c': 3 }; - assert.deepStrictEqual(func({ 'a': 1 }, { 'b': 2 }, { 'c': 3 }), expected); - assert.deepStrictEqual(func({ 'a': 1 }, { 'b': 2, 'c': 2 }, { 'c': 3 }), expected); - }); - - it('`_.' + methodName + '` should overwrite destination properties', function() { - var expected = { 'a': 3, 'b': 2, 'c': 1 }; - assert.deepStrictEqual(func({ 'a': 1, 'b': 2 }, expected), expected); - }); - - it('`_.' + methodName + '` should assign source properties with nullish values', function() { - var expected = { 'a': null, 'b': undefined, 'c': null }; - assert.deepStrictEqual(func({ 'a': 1, 'b': 2 }, expected), expected); - }); - - it('`_.' + methodName + '` should skip assignments if values are the same', function() { - var object = {}; - - var descriptor = { - 'configurable': true, - 'enumerable': true, - 'set': function() { throw new Error; } - }; - - var source = { - 'a': 1, - 'b': undefined, - 'c': NaN, - 'd': undefined, - 'constructor': Object, - 'toString': lodashStable.constant('source') - }; - - defineProperty(object, 'a', lodashStable.assign({}, descriptor, { - 'get': stubOne - })); - - defineProperty(object, 'b', lodashStable.assign({}, descriptor, { - 'get': noop - })); - - defineProperty(object, 'c', lodashStable.assign({}, descriptor, { - 'get': stubNaN - })); - - defineProperty(object, 'constructor', lodashStable.assign({}, descriptor, { - 'get': lodashStable.constant(Object) - })); - - try { - var actual = func(object, source); - } catch (e) {} - - assert.deepStrictEqual(actual, source); - }); - - it('`_.' + methodName + '` should treat sparse array sources as dense', function() { - var array = [1]; - array[2] = 3; - - assert.deepStrictEqual(func({}, array), { '0': 1, '1': undefined, '2': 3 }); - }); - - it('`_.' + methodName + '` should assign values of prototype objects', function() { - function Foo() {} - Foo.prototype.a = 1; - - assert.deepStrictEqual(func({}, Foo.prototype), { 'a': 1 }); - }); - - it('`_.' + methodName + '` should coerce string sources to objects', function() { - assert.deepStrictEqual(func({}, 'a'), { '0': 'a' }); - }); - }); -}); diff --git a/test/assign-and-assignIn.spec.js b/test/assign-and-assignIn.spec.js new file mode 100644 index 0000000000..370bf61a27 --- /dev/null +++ b/test/assign-and-assignIn.spec.js @@ -0,0 +1,105 @@ +import lodashStable from 'lodash'; +import { _, defineProperty, stubOne, noop, stubNaN } from './utils'; + +describe('assign and assignIn', () => { + lodashStable.each(['assign', 'assignIn'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should assign source properties to \`object\``, () => { + expect(func({ a: 1 }, { b: 2 }), { a: 1).toEqual(b: 2 }); + }); + + it(`\`_.${methodName}\` should accept multiple sources`, () => { + const expected = { a: 1, b: 2, c: 3 }; + expect(func({ a: 1 }, { b: 2 }, { c: 3 })).toEqual(expected); + expect(func({ a: 1 }, { b: 2, c: 2 }, { c: 3 })).toEqual(expected); + }); + + it(`\`_.${methodName}\` should overwrite destination properties`, () => { + const expected = { a: 3, b: 2, c: 1 }; + expect(func({ a: 1, b: 2 }, expected)).toEqual(expected); + }); + + it(`\`_.${methodName}\` should assign source properties with nullish values`, () => { + const expected = { a: null, b: undefined, c: null }; + expect(func({ a: 1, b: 2 }, expected)).toEqual(expected); + }); + + it(`\`_.${methodName}\` should skip assignments if values are the same`, () => { + const object = {}; + + const descriptor = { + configurable: true, + enumerable: true, + set: function () { + throw new Error(); + }, + }; + + const source = { + a: 1, + b: undefined, + c: NaN, + d: undefined, + constructor: Object, + toString: lodashStable.constant('source'), + }; + + defineProperty( + object, + 'a', + lodashStable.assign({}, descriptor, { + get: stubOne, + }), + ); + + defineProperty( + object, + 'b', + lodashStable.assign({}, descriptor, { + get: noop, + }), + ); + + defineProperty( + object, + 'c', + lodashStable.assign({}, descriptor, { + get: stubNaN, + }), + ); + + defineProperty( + object, + 'constructor', + lodashStable.assign({}, descriptor, { + get: lodashStable.constant(Object), + }), + ); + + try { + var actual = func(object, source); + } catch (e) {} + + expect(actual).toEqual(source); + }); + + it(`\`_.${methodName}\` should treat sparse array sources as dense`, () => { + const array = [1]; + array[2] = 3; + + expect(func({}, array), { 0: 1, 1: undefined).toEqual(2: 3 }); + }); + + it(`\`_.${methodName}\` should assign values of prototype objects`, () => { + function Foo() {} + Foo.prototype.a = 1; + + expect(func({}, Foo.prototype)).toEqual({ a: 1 }); + }); + + it(`\`_.${methodName}\` should coerce string sources to objects`, () => { + expect(func({}, 'a')).toEqual({ 0: 'a' }); + }); + }); +}); diff --git a/test/assignIn.js b/test/assignIn.js deleted file mode 100644 index 03a93beabc..0000000000 --- a/test/assignIn.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import extend from '../extend.js'; -import assignIn from '../assignIn.js'; - -describe('assignIn', function() { - it('should be aliased', function() { - assert.strictEqual(extend, assignIn); - }); -}); diff --git a/test/assignIn.spec.js b/test/assignIn.spec.js new file mode 100644 index 0000000000..24828d4f79 --- /dev/null +++ b/test/assignIn.spec.js @@ -0,0 +1,8 @@ +import extend from '../src/extend'; +import assignIn from '../src/assignIn'; + +describe('assignIn', () => { + it('should be aliased', () => { + expect(extend).toBe(assignIn); + }); +}); diff --git a/test/assignInWith.js b/test/assignInWith.js deleted file mode 100644 index e253b3f1e5..0000000000 --- a/test/assignInWith.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import extendWith from '../extendWith.js'; -import assignInWith from '../assignInWith.js'; - -describe('assignInWith', function() { - it('should be aliased', function() { - assert.strictEqual(extendWith, assignInWith); - }); -}); diff --git a/test/assignInWith.spec.js b/test/assignInWith.spec.js new file mode 100644 index 0000000000..99dc97838d --- /dev/null +++ b/test/assignInWith.spec.js @@ -0,0 +1,8 @@ +import extendWith from '../src/extendWith'; +import assignInWith from '../src/assignInWith'; + +describe('assignInWith', () => { + it('should be aliased', () => { + expect(extendWith).toBe(assignInWith); + }); +}); diff --git a/test/assignWith-and-assignInWith.js b/test/assignWith-and-assignInWith.js deleted file mode 100644 index 70ec24a6b8..0000000000 --- a/test/assignWith-and-assignInWith.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, noop } from './utils.js'; - -describe('assignWith and assignInWith', function() { - lodashStable.each(['assignWith', 'assignInWith'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should work with a `customizer` callback', function() { - var actual = func({ 'a': 1, 'b': 2 }, { 'a': 3, 'c': 3 }, function(a, b) { - return a === undefined ? b : a; - }); - - assert.deepStrictEqual(actual, { 'a': 1, 'b': 2, 'c': 3 }); - }); - - it('`_.' + methodName + '` should work with a `customizer` that returns `undefined`', function() { - var expected = { 'a': 1 }; - assert.deepStrictEqual(func({}, expected, noop), expected); - }); - }); -}); diff --git a/test/assignWith-and-assignInWith.spec.js b/test/assignWith-and-assignInWith.spec.js new file mode 100644 index 0000000000..5ce0774a29 --- /dev/null +++ b/test/assignWith-and-assignInWith.spec.js @@ -0,0 +1,21 @@ +import lodashStable from 'lodash'; +import { _, noop } from './utils'; + +describe('assignWith and assignInWith', () => { + lodashStable.each(['assignWith', 'assignInWith'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should work with a \`customizer\` callback`, () => { + const actual = func({ a: 1, b: 2 }, { a: 3, c: 3 }, (a, b) => + a === undefined ? b : a, + ); + + expect(actual).toEqual({ a: 1, b: 2, c: 3 }); + }); + + it(`\`_.${methodName}\` should work with a \`customizer\` that returns \`undefined\``, () => { + const expected = { a: 1 }; + expect(func({}, expected, noop)).toEqual(expected); + }); + }); +}); diff --git a/test/at.spec.js b/test/at.spec.js new file mode 100644 index 0000000000..be33f980c6 --- /dev/null +++ b/test/at.spec.js @@ -0,0 +1,130 @@ +import lodashStable from 'lodash'; +import { empties, stubOne, falsey, args, LARGE_ARRAY_SIZE, square, identity } from './utils'; +import at from '../src/at'; + +describe('at', () => { + const array = ['a', 'b', 'c']; + const object = { a: [{ b: { c: 3 } }, 4] }; + + it('should return the elements corresponding to the specified keys', () => { + const actual = at(array, [0, 2]); + expect(actual, ['a').toEqual('c']); + }); + + it('should return `undefined` for nonexistent keys', () => { + const actual = at(array, [2, 4, 0]); + expect(actual, ['c', undefined).toEqual('a']); + }); + + it('should work with non-index keys on array values', () => { + const values = lodashStable + .reject(empties, (value) => value === 0 || lodashStable.isArray(value)) + .concat(-1, 1.1); + + const array = lodashStable.transform( + values, + (result, value) => { + result[value] = 1; + }, + [], + ); + + const expected = lodashStable.map(values, stubOne); + const actual = at(array, values); + + expect(actual).toEqual(expected); + }); + + it('should return an empty array when no keys are given', () => { + expect(at(array)).toEqual([]); + expect(at(array, [], [])).toEqual([]); + }); + + it('should accept multiple key arguments', () => { + const actual = at(['a', 'b', 'c', 'd'], 3, 0, 2); + expect(actual, ['d', 'a').toEqual('c']); + }); + + it('should work with a falsey `object` when keys are given', () => { + const expected = lodashStable.map(falsey, lodashStable.constant(Array(4).fill(undefined))); + + const actual = lodashStable.map(falsey, (object) => { + try { + return at(object, 0, 1, 'pop', 'push'); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should work with an `arguments` object for `object`', () => { + const actual = at(args, [2, 0]); + expect(actual, [3).toEqual(1]); + }); + + it('should work with `arguments` object as secondary arguments', () => { + const actual = at([1, 2, 3, 4, 5], args); + expect(actual, [2, 3).toEqual(4]); + }); + + it('should work with an object for `object`', () => { + const actual = at(object, ['a[0].b.c', 'a[1]']); + expect(actual, [3).toEqual(4]); + }); + + it('should pluck inherited property values', () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const actual = at(new Foo(), 'b'); + expect(actual).toEqual([2]); + }); + + it('should work in a lazy sequence', () => { + const largeArray = lodashStable.range(LARGE_ARRAY_SIZE); + const smallArray = array; + + lodashStable.each([[2], ['2'], [2, 1]], (paths) => { + lodashStable.times(2, (index) => { + const array = index ? largeArray : smallArray; + const wrapped = _(array).map(identity).at(paths); + + expect(wrapped.value(), at(_.map(array, identity)).toEqual(paths)); + }); + }); + }); + + it('should support shortcut fusion', () => { + const array = lodashStable.range(LARGE_ARRAY_SIZE); + let count = 0; + const iteratee = function (value) { + count++; + return square(value); + }; + const lastIndex = LARGE_ARRAY_SIZE - 1; + + lodashStable.each([lastIndex, `${lastIndex}`, LARGE_ARRAY_SIZE, []], (n, index) => { + count = 0; + const actual = _(array).map(iteratee).at(n).value(); + let expected = index < 2 ? 1 : 0; + + expect(count).toBe(expected); + + expected = index === 3 ? [] : [index === 2 ? undefined : square(lastIndex)]; + expect(actual).toEqual(expected); + }); + }); + + it('work with an object for `object` when chaining', () => { + const paths = ['a[0].b.c', 'a[1]']; + let actual = _(object).map(identity).at(paths).value(); + + expect(actual, at(_.map(object, identity)).toEqual(paths)); + + const indexObject = { 0: 1 }; + actual = _(indexObject).at(0).value(); + expect(actual, at(indexObject).toEqual(0)); + }); +}); diff --git a/test/at.test.js b/test/at.test.js deleted file mode 100644 index c466829f24..0000000000 --- a/test/at.test.js +++ /dev/null @@ -1,124 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, stubOne, falsey, args, LARGE_ARRAY_SIZE, square, identity } from './utils.js'; -import at from '../at.js'; - -describe('at', function() { - var array = ['a', 'b', 'c'], - object = { 'a': [{ 'b': { 'c': 3 } }, 4] }; - - it('should return the elements corresponding to the specified keys', function() { - var actual = at(array, [0, 2]); - assert.deepStrictEqual(actual, ['a', 'c']); - }); - - it('should return `undefined` for nonexistent keys', function() { - var actual = at(array, [2, 4, 0]); - assert.deepStrictEqual(actual, ['c', undefined, 'a']); - }); - - it('should work with non-index keys on array values', function() { - var values = lodashStable.reject(empties, function(value) { - return (value === 0) || lodashStable.isArray(value); - }).concat(-1, 1.1); - - var array = lodashStable.transform(values, function(result, value) { - result[value] = 1; - }, []); - - var expected = lodashStable.map(values, stubOne), - actual = at(array, values); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an empty array when no keys are given', function() { - assert.deepStrictEqual(at(array), []); - assert.deepStrictEqual(at(array, [], []), []); - }); - - it('should accept multiple key arguments', function() { - var actual = at(['a', 'b', 'c', 'd'], 3, 0, 2); - assert.deepStrictEqual(actual, ['d', 'a', 'c']); - }); - - it('should work with a falsey `object` when keys are given', function() { - var expected = lodashStable.map(falsey, lodashStable.constant(Array(4).fill(undefined))); - - var actual = lodashStable.map(falsey, function(object) { - try { - return at(object, 0, 1, 'pop', 'push'); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with an `arguments` object for `object`', function() { - var actual = at(args, [2, 0]); - assert.deepStrictEqual(actual, [3, 1]); - }); - - it('should work with `arguments` object as secondary arguments', function() { - var actual = at([1, 2, 3, 4, 5], args); - assert.deepStrictEqual(actual, [2, 3, 4]); - }); - - it('should work with an object for `object`', function() { - var actual = at(object, ['a[0].b.c', 'a[1]']); - assert.deepStrictEqual(actual, [3, 4]); - }); - - it('should pluck inherited property values', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var actual = at(new Foo, 'b'); - assert.deepStrictEqual(actual, [2]); - }); - - it('should work in a lazy sequence', function() { - var largeArray = lodashStable.range(LARGE_ARRAY_SIZE), - smallArray = array; - - lodashStable.each([[2], ['2'], [2, 1]], function(paths) { - lodashStable.times(2, function(index) { - var array = index ? largeArray : smallArray, - wrapped = _(array).map(identity).at(paths); - - assert.deepEqual(wrapped.value(), at(_.map(array, identity), paths)); - }); - }); - }); - - it('should support shortcut fusion', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - count = 0, - iteratee = function(value) { count++; return square(value); }, - lastIndex = LARGE_ARRAY_SIZE - 1; - - lodashStable.each([lastIndex, lastIndex + '', LARGE_ARRAY_SIZE, []], function(n, index) { - count = 0; - var actual = _(array).map(iteratee).at(n).value(), - expected = index < 2 ? 1 : 0; - - assert.strictEqual(count, expected); - - expected = index == 3 ? [] : [index == 2 ? undefined : square(lastIndex)]; - assert.deepEqual(actual, expected); - }); - }); - - it('work with an object for `object` when chaining', function() { - var paths = ['a[0].b.c', 'a[1]'], - actual = _(object).map(identity).at(paths).value(); - - assert.deepEqual(actual, at(_.map(object, identity), paths)); - - var indexObject = { '0': 1 }; - actual = _(indexObject).at(0).value(); - assert.deepEqual(actual, at(indexObject, 0)); - }); -}); diff --git a/test/attempt.spec.js b/test/attempt.spec.js new file mode 100644 index 0000000000..e687f7158c --- /dev/null +++ b/test/attempt.spec.js @@ -0,0 +1,74 @@ +import lodashStable from 'lodash'; +import { slice, errors, stubTrue, CustomError, realm } from './utils'; +import attempt from '../src/attempt'; + +describe('attempt', () => { + it('should return the result of `func`', () => { + expect(attempt(lodashStable.constant('x'))).toBe('x'); + }); + + it('should provide additional arguments to `func`', () => { + const actual = attempt( + function () { + return slice.call(arguments); + }, + 1, + 2, + ); + expect(actual).toEqual([1, 2]); + }); + + it('should return the caught error', () => { + const expected = lodashStable.map(errors, stubTrue); + + const actual = lodashStable.map( + errors, + (error) => + attempt(() => { + throw error; + }) === error, + ); + + expect(actual).toEqual(expected); + }); + + it('should coerce errors to error objects', () => { + const actual = attempt(() => { + throw 'x'; + }); + expect(lodashStable.isEqual(actual, Error('x'))).toBeTruthy(); + }); + + it('should preserve custom errors', () => { + const actual = attempt(() => { + throw new CustomError('x'); + }); + expect(actual instanceof CustomError); + }); + + it('should work with an error object from another realm', () => { + if (realm.errors) { + const expected = lodashStable.map(realm.errors, stubTrue); + + const actual = lodashStable.map( + realm.errors, + (error) => + attempt(() => { + throw error; + }) === error, + ); + + expect(actual).toEqual(expected); + } + }); + + // FIXME: Work out a solution for _. + // + // it('should return an unwrapped value when implicitly chaining', () => { + // expect(_(lodashStable.constant('x')).attempt()).toBe('x'); + // }); + // + // it('should return a wrapped value when explicitly chaining', () => { + // expect(_(lodashStable.constant('x')).chain().attempt() instanceof _); + // }); +}); diff --git a/test/attempt.test.js b/test/attempt.test.js deleted file mode 100644 index d453ce0ac9..0000000000 --- a/test/attempt.test.js +++ /dev/null @@ -1,55 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, errors, stubTrue, CustomError, realm } from './utils.js'; -import attempt from '../attempt.js'; - -describe('attempt', function() { - it('should return the result of `func`', function() { - assert.strictEqual(attempt(lodashStable.constant('x')), 'x'); - }); - - it('should provide additional arguments to `func`', function() { - var actual = attempt(function() { return slice.call(arguments); }, 1, 2); - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('should return the caught error', function() { - var expected = lodashStable.map(errors, stubTrue); - - var actual = lodashStable.map(errors, function(error) { - return attempt(function() { throw error; }) === error; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should coerce errors to error objects', function() { - var actual = attempt(function() { throw 'x'; }); - assert.ok(lodashStable.isEqual(actual, Error('x'))); - }); - - it('should preserve custom errors', function() { - var actual = attempt(function() { throw new CustomError('x'); }); - assert.ok(actual instanceof CustomError); - }); - - it('should work with an error object from another realm', function() { - if (realm.errors) { - var expected = lodashStable.map(realm.errors, stubTrue); - - var actual = lodashStable.map(realm.errors, function(error) { - return attempt(function() { throw error; }) === error; - }); - - assert.deepStrictEqual(actual, expected); - } - }); - - it('should return an unwrapped value when implicitly chaining', function() { - assert.strictEqual(_(lodashStable.constant('x')).attempt(), 'x'); - }); - - it('should return a wrapped value when explicitly chaining', function() { - assert.ok(_(lodashStable.constant('x')).chain().attempt() instanceof _); - }); -}); diff --git a/test/basename.js b/test/basename.js deleted file mode 100644 index e60f937960..0000000000 --- a/test/basename.js +++ /dev/null @@ -1,151 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - basename, - amd, - ui, - Worker, - QUnit, - lodashBizarro, - LARGE_ARRAY_SIZE, - symbol, - setProperty, -} from './utils.js'; - -import _VERSION from '../.internal/VERSION.js'; -import VERSION from '../VERSION.js'; - -describe(basename, function() { - it('should support loading ' + basename + ' as the "lodash" module', function() { - if (amd) { - assert.strictEqual((lodashModule || {}).moduleName, 'lodash'); - } - }); - - it('should support loading ' + basename + ' with the Require.js "shim" configuration option', function() { - if (amd && lodashStable.includes(ui.loaderPath, 'requirejs')) { - assert.strictEqual((shimmedModule || {}).moduleName, 'shimmed'); - } - }); - - it('should support loading ' + basename + ' as the "underscore" module', function() { - if (amd) { - assert.strictEqual((underscoreModule || {}).moduleName, 'underscore'); - } - }); - - it('should support loading ' + basename + ' in a web worker', function(done) { - if (Worker) { - var limit = 30000 / QUnit.config.asyncRetries, - start = +new Date; - - var attempt = function() { - var actual = _VERSION; - if ((new Date - start) < limit && typeof actual !== 'string') { - setTimeout(attempt, 16); - return; - } - assert.strictEqual(actual, VERSION); - done(); - }; - - attempt(); - } - else { - done(); - } - }); - - it('should not add `Function.prototype` extensions to lodash', function() { - if (lodashBizarro) { - assert.ok(!('_method' in lodashBizarro)); - } - }); - - it('should avoid non-native built-ins', function() { - function message(lodashMethod, nativeMethod) { - return '`' + lodashMethod + '` should avoid overwritten native `' + nativeMethod + '`'; - } - - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var object = { 'a': 1 }, - otherObject = { 'b': 2 }, - largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(object)); - - if (lodashBizarro) { - try { - var actual = lodashBizarro.create(Foo.prototype); - } catch (e) { - actual = null; - } - var label = message('_.create', 'Object.create'); - assert.ok(actual instanceof Foo, label); - - try { - actual = [ - lodashBizarro.difference([object, otherObject], largeArray), - lodashBizarro.intersection(largeArray, [object]), - lodashBizarro.uniq(largeArray) - ]; - } catch (e) { - actual = null; - } - label = message('_.difference`, `_.intersection`, and `_.uniq', 'Map'); - assert.deepStrictEqual(actual, [[otherObject], [object], [object]], label); - - try { - if (Symbol) { - object[symbol] = {}; - } - actual = [ - lodashBizarro.clone(object), - lodashBizarro.cloneDeep(object) - ]; - } catch (e) { - actual = null; - } - label = message('_.clone` and `_.cloneDeep', 'Object.getOwnPropertySymbols'); - assert.deepStrictEqual(actual, [object, object], label); - - try { - // Avoid buggy symbol detection in Babel's `_typeof` helper. - var symObject = setProperty(Object(symbol), 'constructor', Object); - actual = [ - Symbol ? lodashBizarro.clone(symObject) : {}, - Symbol ? lodashBizarro.isEqual(symObject, Object(symbol)) : false, - Symbol ? lodashBizarro.toString(symObject) : '' - ]; - } catch (e) { - actual = null; - } - label = message('_.clone`, `_.isEqual`, and `_.toString', 'Symbol'); - assert.deepStrictEqual(actual, [{}, false, ''], label); - - try { - var map = new lodashBizarro.memoize.Cache; - actual = map.set('a', 1).get('a'); - } catch (e) { - actual = null; - } - label = message('_.memoize.Cache', 'Map'); - assert.deepStrictEqual(actual, 1, label); - - try { - map = new (Map || Object); - if (Symbol && Symbol.iterator) { - map[Symbol.iterator] = null; - } - actual = lodashBizarro.toArray(map); - } catch (e) { - actual = null; - } - label = message('_.toArray', 'Map'); - assert.deepStrictEqual(actual, [], label); - } - }); -}); diff --git a/test/basename.spec.js b/test/basename.spec.js new file mode 100644 index 0000000000..3eff54a09b --- /dev/null +++ b/test/basename.spec.js @@ -0,0 +1,146 @@ +import lodashStable from 'lodash'; + +import { + basename, + amd, + ui, + Worker, + QUnit, + lodashBizarro, + LARGE_ARRAY_SIZE, + symbol, + setProperty, +} from './utils'; + +import _VERSION from '../.internal/VERSION'; +import VERSION from '../src/VERSION'; + +describe(basename, () => { + it(`should support loading ${basename} as the "lodash" module`, () => { + if (amd) { + expect((lodashModule || {}).moduleName).toBe('lodash'); + } + }); + + it(`should support loading ${basename} with the Require.js "shim" configuration option`, () => { + if (amd && lodashStable.includes(ui.loaderPath, 'requirejs')) { + expect((shimmedModule || {}).moduleName).toBe('shimmed'); + } + }); + + it(`should support loading ${basename} as the "underscore" module`, () => { + if (amd) { + expect((underscoreModule || {}).moduleName).toBe('underscore'); + } + }); + + it(`should support loading ${basename} in a web worker`, (done) => { + if (Worker) { + const limit = 30000 / QUnit.config.asyncRetries; + const start = +new Date(); + + const attempt = function () { + const actual = _VERSION; + if (new Date() - start < limit && typeof actual !== 'string') { + setTimeout(attempt, 16); + return; + } + expect(actual).toBe(VERSION); + done(); + }; + + attempt(); + } else { + done(); + } + }); + + it('should not add `Function.prototype` extensions to lodash', () => { + if (lodashBizarro) { + expect('_method' in lodashBizarro).toBe(false); + } + }); + + it('should avoid non-native built-ins', () => { + function message(lodashMethod, nativeMethod) { + return `\`${lodashMethod}\` should avoid overwritten native \`${nativeMethod}\``; + } + + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const object = { a: 1 }; + const otherObject = { b: 2 }; + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(object)); + + if (lodashBizarro) { + try { + var actual = lodashBizarro.create(Foo.prototype); + } catch (e) { + actual = null; + } + let label = message('_.create', 'Object.create'); + expect(actual instanceof Foo, label); + + try { + actual = [ + lodashBizarro.difference([object, otherObject], largeArray), + lodashBizarro.intersection(largeArray, [object]), + lodashBizarro.uniq(largeArray), + ]; + } catch (e) { + actual = null; + } + label = message('_.difference`, `_.intersection`, and `_.uniq', 'Map'); + expect(actual, [[otherObject], [object], [object]]).toEqual(label); + + try { + if (Symbol) { + object[symbol] = {}; + } + actual = [lodashBizarro.clone(object), lodashBizarro.cloneDeep(object)]; + } catch (e) { + actual = null; + } + label = message('_.clone` and `_.cloneDeep', 'Object.getOwnPropertySymbols'); + expect(actual, [object, object]).toEqual(label); + + try { + // Avoid buggy symbol detection in Babel's `_typeof` helper. + const symObject = setProperty(Object(symbol), 'constructor', Object); + actual = [ + Symbol ? lodashBizarro.clone(symObject) : {}, + Symbol ? lodashBizarro.isEqual(symObject, Object(symbol)) : false, + Symbol ? lodashBizarro.toString(symObject) : '', + ]; + } catch (e) { + actual = null; + } + label = message('_.clone`, `_.isEqual`, and `_.toString', 'Symbol'); + expect(actual, [{}, false, '']).toEqual(label); + + try { + var map = new lodashBizarro.memoize.Cache(); + actual = map.set('a', 1).get('a'); + } catch (e) { + actual = null; + } + label = message('_.memoize.Cache', 'Map'); + expect(actual, 1).toEqual(label); + + try { + map = new (Map || Object)(); + if (Symbol && Symbol.iterator) { + map[Symbol.iterator] = null; + } + actual = lodashBizarro.toArray(map); + } catch (e) { + actual = null; + } + label = message('_.toArray', 'Map'); + expect(actual, []).toEqual(label); + } + }); +}); diff --git a/test/before.js b/test/before.js deleted file mode 100644 index d1a678b1e9..0000000000 --- a/test/before.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('before', function() { - function before(n, times) { - var count = 0; - lodashStable.times(times, _.before(n, function() { count++; })); - return count; - } - - it('should create a function that invokes `func` after `n` calls', function() { - assert.strictEqual(before(5, 4), 4, 'before(n) should invoke `func` before being called `n` times'); - assert.strictEqual(before(5, 6), 4, 'before(n) should not invoke `func` after being called `n - 1` times'); - assert.strictEqual(before(0, 0), 0, 'before(0) should not invoke `func` immediately'); - assert.strictEqual(before(0, 1), 0, 'before(0) should not invoke `func` when called'); - }); - - it('should coerce `n` values of `NaN` to `0`', function() { - assert.strictEqual(before(NaN, 1), 0); - }); - - it('should use `this` binding of function', function() { - var before = _.before(2, function() { return ++this.count; }), - object = { 'before': before, 'count': 0 }; - - object.before(); - assert.strictEqual(object.before(), 1); - assert.strictEqual(object.count, 1); - }); -}); diff --git a/test/before.spec.js b/test/before.spec.js new file mode 100644 index 0000000000..c1ccfb3fdb --- /dev/null +++ b/test/before.spec.js @@ -0,0 +1,45 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('before', () => { + function before(n, times) { + let count = 0; + lodashStable.times( + times, + _.before(n, () => { + count++; + }), + ); + return count; + } + + it('should create a function that invokes `func` after `n` calls', () => { + assert.strictEqual( + before(5, 4), + 4, + 'before(n) should invoke `func` before being called `n` times', + ); + assert.strictEqual( + before(5, 6), + 4, + 'before(n) should not invoke `func` after being called `n - 1` times', + ); + expect(before(0, 0), 0).toBe('before(0) should not invoke `func` immediately'); + expect(before(0, 1), 0).toBe('before(0) should not invoke `func` when called'); + }); + + it('should coerce `n` values of `NaN` to `0`', () => { + expect(before(NaN, 1)).toBe(0); + }); + + it('should use `this` binding of function', () => { + const before = _.before(2, function () { + return ++this.count; + }); + const object = { before: before, count: 0 }; + + object.before(); + expect(object.before()).toBe(1); + expect(object.count).toBe(1); + }); +}); diff --git a/test/bind.js b/test/bind.js deleted file mode 100644 index 384ddd9b37..0000000000 --- a/test/bind.js +++ /dev/null @@ -1,231 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { push, falsey, stubTrue } from './utils.js'; -import bind from '../bind.js'; -import placeholder from '../placeholder.js'; - -describe('bind', function() { - function fn() { - var result = [this]; - push.apply(result, arguments); - return result; - } - - it('should bind a function to an object', function() { - var object = {}, - bound = bind(fn, object); - - assert.deepStrictEqual(bound('a'), [object, 'a']); - }); - - it('should accept a falsey `thisArg`', function() { - var values = lodashStable.reject(falsey.slice(1), function(value) { return value == null; }), - expected = lodashStable.map(values, function(value) { return [value]; }); - - var actual = lodashStable.map(values, function(value) { - try { - var bound = bind(fn, value); - return bound(); - } catch (e) {} - }); - - assert.ok(lodashStable.every(actual, function(value, index) { - return lodashStable.isEqual(value, expected[index]); - })); - }); - - it('should bind a function to nullish values', function() { - var bound = bind(fn, null), - actual = bound('a'); - - assert.ok((actual[0] === null) || (actual[0] && actual[0].Array)); - assert.strictEqual(actual[1], 'a'); - - lodashStable.times(2, function(index) { - bound = index ? bind(fn, undefined) : bind(fn); - actual = bound('b'); - - assert.ok((actual[0] === undefined) || (actual[0] && actual[0].Array)); - assert.strictEqual(actual[1], 'b'); - }); - }); - - it('should partially apply arguments ', function() { - var object = {}, - bound = bind(fn, object, 'a'); - - assert.deepStrictEqual(bound(), [object, 'a']); - - bound = bind(fn, object, 'a'); - assert.deepStrictEqual(bound('b'), [object, 'a', 'b']); - - bound = bind(fn, object, 'a', 'b'); - assert.deepStrictEqual(bound(), [object, 'a', 'b']); - assert.deepStrictEqual(bound('c', 'd'), [object, 'a', 'b', 'c', 'd']); - }); - - it('should support placeholders', function() { - var object = {}, - ph = bind.placeholder, - bound = bind(fn, object, ph, 'b', ph); - - assert.deepStrictEqual(bound('a', 'c'), [object, 'a', 'b', 'c']); - assert.deepStrictEqual(bound('a'), [object, 'a', 'b', undefined]); - assert.deepStrictEqual(bound('a', 'c', 'd'), [object, 'a', 'b', 'c', 'd']); - assert.deepStrictEqual(bound(), [object, undefined, 'b', undefined]); - }); - - it('should use `_.placeholder` when set', function() { - var _ph = placeholder = {}, - ph = bind.placeholder, - object = {}, - bound = bind(fn, object, _ph, 'b', ph); - - assert.deepEqual(bound('a', 'c'), [object, 'a', 'b', ph, 'c']); - delete placeholder; - }); - - it('should create a function with a `length` of `0`', function() { - var fn = function(a, b, c) {}, - bound = bind(fn, {}); - - assert.strictEqual(bound.length, 0); - - bound = bind(fn, {}, 1); - assert.strictEqual(bound.length, 0); - }); - - it('should ignore binding when called with the `new` operator', function() { - function Foo() { - return this; - } - - var bound = bind(Foo, { 'a': 1 }), - newBound = new bound; - - assert.strictEqual(bound().a, 1); - assert.strictEqual(newBound.a, undefined); - assert.ok(newBound instanceof Foo); - }); - - it('should handle a number of arguments when called with the `new` operator', function() { - function Foo() { - return this; - } - - function Bar() {} - - var thisArg = { 'a': 1 }, - boundFoo = bind(Foo, thisArg), - boundBar = bind(Bar, thisArg), - count = 9, - expected = lodashStable.times(count, lodashStable.constant([undefined, undefined])); - - var actual = lodashStable.times(count, function(index) { - try { - switch (index) { - case 0: return [new boundFoo().a, new boundBar().a]; - case 1: return [new boundFoo(1).a, new boundBar(1).a]; - case 2: return [new boundFoo(1, 2).a, new boundBar(1, 2).a]; - case 3: return [new boundFoo(1, 2, 3).a, new boundBar(1, 2, 3).a]; - case 4: return [new boundFoo(1, 2, 3, 4).a, new boundBar(1, 2, 3, 4).a]; - case 5: return [new boundFoo(1, 2, 3, 4, 5).a, new boundBar(1, 2, 3, 4, 5).a]; - case 6: return [new boundFoo(1, 2, 3, 4, 5, 6).a, new boundBar(1, 2, 3, 4, 5, 6).a]; - case 7: return [new boundFoo(1, 2, 3, 4, 5, 6, 7).a, new boundBar(1, 2, 3, 4, 5, 6, 7).a]; - case 8: return [new boundFoo(1, 2, 3, 4, 5, 6, 7, 8).a, new boundBar(1, 2, 3, 4, 5, 6, 7, 8).a]; - } - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should ensure `new bound` is an instance of `func`', function() { - function Foo(value) { - return value && object; - } - - var bound = bind(Foo), - object = {}; - - assert.ok(new bound instanceof Foo); - assert.strictEqual(new bound(true), object); - }); - - it('should append array arguments to partially applied arguments', function() { - var object = {}, - bound = bind(fn, object, 'a'); - - assert.deepStrictEqual(bound(['b'], 'c'), [object, 'a', ['b'], 'c']); - }); - - it('should not rebind functions', function() { - var object1 = {}, - object2 = {}, - object3 = {}; - - var bound1 = bind(fn, object1), - bound2 = bind(bound1, object2, 'a'), - bound3 = bind(bound1, object3, 'b'); - - assert.deepStrictEqual(bound1(), [object1]); - assert.deepStrictEqual(bound2(), [object1, 'a']); - assert.deepStrictEqual(bound3(), [object1, 'b']); - }); - - it('should not error when instantiating bound built-ins', function() { - var Ctor = bind(Date, null), - expected = new Date(2012, 4, 23, 0, 0, 0, 0); - - try { - var actual = new Ctor(2012, 4, 23, 0, 0, 0, 0); - } catch (e) {} - - assert.deepStrictEqual(actual, expected); - - Ctor = bind(Date, null, 2012, 4, 23); - - try { - actual = new Ctor(0, 0, 0, 0); - } catch (e) {} - - assert.deepStrictEqual(actual, expected); - }); - - it('should not error when calling bound class constructors with the `new` operator', function() { - var createCtor = lodashStable.attempt(Function, '"use strict";return class A{}'); - - if (typeof createCtor === 'function') { - var bound = bind(createCtor()), - count = 8, - expected = lodashStable.times(count, stubTrue); - - var actual = lodashStable.times(count, function(index) { - try { - switch (index) { - case 0: return !!(new bound); - case 1: return !!(new bound(1)); - case 2: return !!(new bound(1, 2)); - case 3: return !!(new bound(1, 2, 3)); - case 4: return !!(new bound(1, 2, 3, 4)); - case 5: return !!(new bound(1, 2, 3, 4, 5)); - case 6: return !!(new bound(1, 2, 3, 4, 5, 6)); - case 7: return !!(new bound(1, 2, 3, 4, 5, 6, 7)); - } - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - } - }); - - it('should return a wrapped value when chaining', function() { - var object = {}, - bound = _(fn).bind({}, 'a', 'b'); - - assert.ok(bound instanceof _); - - var actual = bound.value()('c'); - assert.deepEqual(actual, [object, 'a', 'b', 'c']); - }); -}); diff --git a/test/bind.spec.js b/test/bind.spec.js new file mode 100644 index 0000000000..d9cd53a7b4 --- /dev/null +++ b/test/bind.spec.js @@ -0,0 +1,255 @@ +import lodashStable from 'lodash'; +import { push, falsey, stubTrue } from './utils'; +import bind from '../src/bind'; +import placeholder from '../src/placeholder'; + +describe('bind', () => { + function fn() { + const result = [this]; + push.apply(result, arguments); + return result; + } + + it('should bind a function to an object', () => { + const object = {}, + bound = bind(fn, object); + + expect(bound('a')).toEqual([object, 'a']); + }); + + it('should accept a falsey `thisArg`', () => { + const values = lodashStable.reject(falsey.slice(1), (value) => value == null), + expected = lodashStable.map(values, (value) => [value]); + + const actual = lodashStable.map(values, (value) => { + try { + const bound = bind(fn, value); + return bound(); + } catch (e) {} + }); + + assert.ok( + lodashStable.every(actual, (value, index) => + lodashStable.isEqual(value, expected[index]), + ), + ); + }); + + it('should bind a function to nullish values', () => { + let bound = bind(fn, null), + actual = bound('a'); + + expect(actual[0] === null || (actual[0] && actual[0].Array)) + expect(actual[1]).toBe('a'); + + lodashStable.times(2, (index) => { + bound = index ? bind(fn, undefined) : bind(fn); + actual = bound('b'); + + expect(actual[0] === undefined || (actual[0] && actual[0].Array)) + expect(actual[1]).toBe('b'); + }); + }); + + it('should partially apply arguments ', () => { + let object = {}, + bound = bind(fn, object, 'a'); + + expect(bound()).toEqual([object, 'a']); + + bound = bind(fn, object, 'a'); + expect(bound('b')).toEqual([object, 'a', 'b']); + + bound = bind(fn, object, 'a', 'b'); + expect(bound()).toEqual([object, 'a', 'b']); + expect(bound('c', 'd')).toEqual([object, 'a', 'b', 'c', 'd']); + }); + + it('should support placeholders', () => { + const object = {}, + ph = bind.placeholder, + bound = bind(fn, object, ph, 'b', ph); + + expect(bound('a', 'c')).toEqual([object, 'a', 'b', 'c']); + expect(bound('a')).toEqual([object, 'a', 'b', undefined]); + expect(bound('a', 'c', 'd')).toEqual([object, 'a', 'b', 'c', 'd']); + expect(bound()).toEqual([object, undefined, 'b', undefined]); + }); + + it('should use `_.placeholder` when set', () => { + const _ph = (placeholder = {}), + ph = bind.placeholder, + object = {}, + bound = bind(fn, object, _ph, 'b', ph); + + expect(bound('a', 'c')).toEqual([object, 'a', 'b', ph, 'c']); + delete placeholder; + }); + + it('should create a function with a `length` of `0`', () => { + let fn = function (a, b, c) {}, + bound = bind(fn, {}); + + expect(bound.length).toBe(0); + + bound = bind(fn, {}, 1); + expect(bound.length).toBe(0); + }); + + it('should ignore binding when called with the `new` operator', () => { + function Foo() { + return this; + } + + const bound = bind(Foo, { a: 1 }), + newBound = new bound(); + + expect(bound().a).toBe(1); + expect(newBound.a).toBe(undefined); + expect(newBound instanceof Foo) + }); + + it('should handle a number of arguments when called with the `new` operator', () => { + function Foo() { + return this; + } + + function Bar() {} + + const thisArg = { a: 1 }, + boundFoo = bind(Foo, thisArg), + boundBar = bind(Bar, thisArg), + count = 9, + expected = lodashStable.times(count, lodashStable.constant([undefined, undefined])); + + const actual = lodashStable.times(count, (index) => { + try { + switch (index) { + case 0: + return [new boundFoo().a, new boundBar().a]; + case 1: + return [new boundFoo(1).a, new boundBar(1).a]; + case 2: + return [new boundFoo(1, 2).a, new boundBar(1, 2).a]; + case 3: + return [new boundFoo(1, 2, 3).a, new boundBar(1, 2, 3).a]; + case 4: + return [new boundFoo(1, 2, 3, 4).a, new boundBar(1, 2, 3, 4).a]; + case 5: + return [new boundFoo(1, 2, 3, 4, 5).a, new boundBar(1, 2, 3, 4, 5).a]; + case 6: + return [new boundFoo(1, 2, 3, 4, 5, 6).a, new boundBar(1, 2, 3, 4, 5, 6).a]; + case 7: + return [ + new boundFoo(1, 2, 3, 4, 5, 6, 7).a, + new boundBar(1, 2, 3, 4, 5, 6, 7).a, + ]; + case 8: + return [ + new boundFoo(1, 2, 3, 4, 5, 6, 7, 8).a, + new boundBar(1, 2, 3, 4, 5, 6, 7, 8).a, + ]; + } + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should ensure `new bound` is an instance of `func`', () => { + function Foo(value) { + return value && object; + } + + var bound = bind(Foo), + object = {}; + + expect(new bound() instanceof Foo) + expect(new bound(true)).toBe(object); + }); + + it('should append array arguments to partially applied arguments', () => { + const object = {}, + bound = bind(fn, object, 'a'); + + expect(bound(['b'], 'c')).toEqual([object, 'a', ['b'], 'c']); + }); + + it('should not rebind functions', () => { + const object1 = {}, + object2 = {}, + object3 = {}; + + const bound1 = bind(fn, object1), + bound2 = bind(bound1, object2, 'a'), + bound3 = bind(bound1, object3, 'b'); + + expect(bound1()).toEqual([object1]); + expect(bound2()).toEqual([object1, 'a']); + expect(bound3()).toEqual([object1, 'b']); + }); + + it('should not error when instantiating bound built-ins', () => { + let Ctor = bind(Date, null), + expected = new Date(2012, 4, 23, 0, 0, 0, 0); + + try { + var actual = new Ctor(2012, 4, 23, 0, 0, 0, 0); + } catch (e) {} + + expect(actual).toEqual(expected); + + Ctor = bind(Date, null, 2012, 4, 23); + + try { + actual = new Ctor(0, 0, 0, 0); + } catch (e) {} + + expect(actual).toEqual(expected); + }); + + it('should not error when calling bound class constructors with the `new` operator', () => { + const createCtor = lodashStable.attempt(Function, '"use strict";return class A{}'); + + if (typeof createCtor === 'function') { + const bound = bind(createCtor()), + count = 8, + expected = lodashStable.times(count, stubTrue); + + const actual = lodashStable.times(count, (index) => { + try { + switch (index) { + case 0: + return !!new bound(); + case 1: + return !!new bound(1); + case 2: + return !!new bound(1, 2); + case 3: + return !!new bound(1, 2, 3); + case 4: + return !!new bound(1, 2, 3, 4); + case 5: + return !!new bound(1, 2, 3, 4, 5); + case 6: + return !!new bound(1, 2, 3, 4, 5, 6); + case 7: + return !!new bound(1, 2, 3, 4, 5, 6, 7); + } + } catch (e) {} + }); + + expect(actual).toEqual(expected); + } + }); + + it('should return a wrapped value when chaining', () => { + const object = {}, + bound = _(fn).bind({}, 'a', 'b'); + + expect(bound instanceof _).toBeTruthy() + + const actual = bound.value()('c'); + expect(actual).toEqual([object, 'a', 'b', 'c']); + }); +}); diff --git a/test/bindAll.js b/test/bindAll.js deleted file mode 100644 index a085bd97b3..0000000000 --- a/test/bindAll.js +++ /dev/null @@ -1,74 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, toArgs, arrayProto } from './utils.js'; -import bindAll from '../bindAll.js'; - -describe('bindAll', function() { - var args = toArgs(['a']); - - var source = { - '_n0': -2, - '_p0': -1, - '_a': 1, - '_b': 2, - '_c': 3, - '_d': 4, - '-0': function() { return this._n0; }, - '0': function() { return this._p0; }, - 'a': function() { return this._a; }, - 'b': function() { return this._b; }, - 'c': function() { return this._c; }, - 'd': function() { return this._d; } - }; - - it('should accept individual method names', function() { - var object = lodashStable.cloneDeep(source); - bindAll(object, 'a', 'b'); - - var actual = lodashStable.map(['a', 'b', 'c'], function(key) { - return object[key].call({}); - }); - - assert.deepStrictEqual(actual, [1, 2, undefined]); - }); - - it('should accept arrays of method names', function() { - var object = lodashStable.cloneDeep(source); - bindAll(object, ['a', 'b'], ['c']); - - var actual = lodashStable.map(['a', 'b', 'c', 'd'], function(key) { - return object[key].call({}); - }); - - assert.deepStrictEqual(actual, [1, 2, 3, undefined]); - }); - - it('should preserve the sign of `0`', function() { - var props = [-0, Object(-0), 0, Object(0)]; - - var actual = lodashStable.map(props, function(key) { - var object = lodashStable.cloneDeep(source); - bindAll(object, key); - return object[lodashStable.toString(key)].call({}); - }); - - assert.deepStrictEqual(actual, [-2, -2, -1, -1]); - }); - - it('should work with an array `object`', function() { - var array = ['push', 'pop']; - bindAll(array); - assert.strictEqual(array.pop, arrayProto.pop); - }); - - it('should work with `arguments` objects as secondary arguments', function() { - var object = lodashStable.cloneDeep(source); - bindAll(object, args); - - var actual = lodashStable.map(args, function(key) { - return object[key].call({}); - }); - - assert.deepStrictEqual(actual, [1]); - }); -}); diff --git a/test/bindAll.spec.js b/test/bindAll.spec.js new file mode 100644 index 0000000000..ef03ddf76d --- /dev/null +++ b/test/bindAll.spec.js @@ -0,0 +1,79 @@ +import lodashStable from 'lodash'; +import { toArgs, arrayProto } from './utils'; +import bindAll from '../src/bindAll'; + +describe('bindAll', () => { + const args = toArgs(['a']); + + const source = { + _n0: -2, + _p0: -1, + _a: 1, + _b: 2, + _c: 3, + _d: 4, + '-0': function () { + return this._n0; + }, + 0: function () { + return this._p0; + }, + a: function () { + return this._a; + }, + b: function () { + return this._b; + }, + c: function () { + return this._c; + }, + d: function () { + return this._d; + }, + }; + + it('should accept individual method names', () => { + const object = lodashStable.cloneDeep(source); + bindAll(object, 'a', 'b'); + + const actual = lodashStable.map(['a', 'b', 'c'], (key) => object[key].call({})); + + expect(actual).toEqual([1, 2, undefined]); + }); + + it('should accept arrays of method names', () => { + const object = lodashStable.cloneDeep(source); + bindAll(object, ['a', 'b'], ['c']); + + const actual = lodashStable.map(['a', 'b', 'c', 'd'], (key) => object[key].call({})); + + expect(actual).toEqual([1, 2, 3, undefined]); + }); + + it('should preserve the sign of `0`', () => { + const props = [-0, Object(-0), 0, Object(0)]; + + const actual = lodashStable.map(props, (key) => { + const object = lodashStable.cloneDeep(source); + bindAll(object, key); + return object[lodashStable.toString(key)].call({}); + }); + + expect(actual).toEqual([-2, -2, -1, -1]); + }); + + it('should work with an array `object`', () => { + const array = ['push', 'pop']; + bindAll(array); + expect(array.pop).toBe(arrayProto.pop); + }); + + it('should work with `arguments` objects as secondary arguments', () => { + const object = lodashStable.cloneDeep(source); + bindAll(object, args); + + const actual = lodashStable.map(args, (key) => object[key].call({})); + + expect(actual).toEqual([1]); + }); +}); diff --git a/test/bindKey.js b/test/bindKey.js deleted file mode 100644 index 1489f30d3d..0000000000 --- a/test/bindKey.js +++ /dev/null @@ -1,66 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import bindKey from '../bindKey.js'; - -describe('bindKey', function() { - it('should work when the target function is overwritten', function() { - var object = { - 'user': 'fred', - 'greet': function(greeting) { - return this.user + ' says: ' + greeting; - } - }; - - var bound = bindKey(object, 'greet', 'hi'); - assert.strictEqual(bound(), 'fred says: hi'); - - object.greet = function(greeting) { - return this.user + ' says: ' + greeting + '!'; - }; - - assert.strictEqual(bound(), 'fred says: hi!'); - }); - - it('should support placeholders', function() { - var object = { - 'fn': function() { - return slice.call(arguments); - } - }; - - var ph = bindKey.placeholder, - bound = bindKey(object, 'fn', ph, 'b', ph); - - assert.deepStrictEqual(bound('a', 'c'), ['a', 'b', 'c']); - assert.deepStrictEqual(bound('a'), ['a', 'b', undefined]); - assert.deepStrictEqual(bound('a', 'c', 'd'), ['a', 'b', 'c', 'd']); - assert.deepStrictEqual(bound(), [undefined, 'b', undefined]); - }); - - it('should use `_.placeholder` when set', function() { - var object = { - 'fn': function() { - return slice.call(arguments); - } - }; - - var _ph = _.placeholder = {}, - ph = bindKey.placeholder, - bound = bindKey(object, 'fn', _ph, 'b', ph); - - assert.deepEqual(bound('a', 'c'), ['a', 'b', ph, 'c']); - delete _.placeholder; - }); - - it('should ensure `new bound` is an instance of `object[key]`', function() { - function Foo(value) { - return value && object; - } - - var object = { 'Foo': Foo }, - bound = bindKey(object, 'Foo'); - - assert.ok(new bound instanceof Foo); - assert.strictEqual(new bound(true), object); - }); -}); diff --git a/test/bindKey.spec.js b/test/bindKey.spec.js new file mode 100644 index 0000000000..41602e4d1c --- /dev/null +++ b/test/bindKey.spec.js @@ -0,0 +1,65 @@ +import { slice } from './utils'; +import bindKey from '../src/bindKey'; + +describe('bindKey', () => { + it('should work when the target function is overwritten', () => { + const object = { + user: 'fred', + greet: function (greeting) { + return `${this.user} says: ${greeting}`; + }, + }; + + const bound = bindKey(object, 'greet', 'hi'); + expect(bound()).toBe('fred says: hi'); + + object.greet = function (greeting) { + return `${this.user} says: ${greeting}!`; + }; + + expect(bound()).toBe('fred says: hi!'); + }); + + it('should support placeholders', () => { + const object = { + fn: function () { + return slice.call(arguments); + }, + }; + + const ph = bindKey.placeholder; + const bound = bindKey(object, 'fn', ph, 'b', ph); + + expect(bound('a', 'c'), ['a', 'b').toEqual('c']); + expect(bound('a'), ['a', 'b').toEqual(undefined]); + expect(bound('a', 'c', 'd'), ['a', 'b', 'c').toEqual('d']); + expect(bound(), [undefined, 'b').toEqual(undefined]); + }); + + it('should use `_.placeholder` when set', () => { + const object = { + fn: function () { + return slice.call(arguments); + }, + }; + + const _ph = (_.placeholder = {}); + const ph = bindKey.placeholder; + const bound = bindKey(object, 'fn', _ph, 'b', ph); + + expect(bound('a', 'c'), ['a', 'b', ph).toEqual('c']); + delete _.placeholder; + }); + + it('should ensure `new bound` is an instance of `object[key]`', () => { + function Foo(value) { + return value && object; + } + + var object = { Foo: Foo }; + const bound = bindKey(object, 'Foo'); + + expect(new bound() instanceof Foo) + expect(new bound(true)).toBe(object); + }); +}); diff --git a/test/camelCase.spec.js b/test/camelCase.spec.js new file mode 100644 index 0000000000..0e781a4794 --- /dev/null +++ b/test/camelCase.spec.js @@ -0,0 +1,35 @@ +import lodashStable from 'lodash'; +import camelCase from '../src/camelCase'; + +describe('camelCase', () => { + it('should work with numbers', () => { + expect(camelCase('12 feet')).toBe('12Feet'); + expect(camelCase('enable 6h format')).toBe('enable6HFormat'); + expect(camelCase('enable 24H format')).toBe('enable24HFormat'); + expect(camelCase('too legit 2 quit')).toBe('tooLegit2Quit'); + expect(camelCase('walk 500 miles')).toBe('walk500Miles'); + expect(camelCase('xhr2 request')).toBe('xhr2Request'); + }); + + it('should handle acronyms', () => { + lodashStable.each(['safe HTML', 'safeHTML'], (string) => { + expect(camelCase(string)).toBe('safeHtml'); + }); + + lodashStable.each(['escape HTML entities', 'escapeHTMLEntities'], (string) => { + expect(camelCase(string)).toBe('escapeHtmlEntities'); + }); + + lodashStable.each(['XMLHttpRequest', 'XmlHTTPRequest'], (string) => { + expect(camelCase(string)).toBe('xmlHttpRequest'); + }); + + lodashStable.each(['IDs'], (string) => { + expect(camelCase(string)).toBe('ids'); + }); + + lodashStable.each(['Product XMLs'], (string) => { + expect(camelCase(string)).toBe('productXmls'); + }); + }); +}); diff --git a/test/camelCase.test.js b/test/camelCase.test.js deleted file mode 100644 index 89a36b02cd..0000000000 --- a/test/camelCase.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import camelCase from '../camelCase.js'; - -describe('camelCase', function() { - it('should work with numbers', function() { - assert.strictEqual(camelCase('12 feet'), '12Feet'); - assert.strictEqual(camelCase('enable 6h format'), 'enable6HFormat'); - assert.strictEqual(camelCase('enable 24H format'), 'enable24HFormat'); - assert.strictEqual(camelCase('too legit 2 quit'), 'tooLegit2Quit'); - assert.strictEqual(camelCase('walk 500 miles'), 'walk500Miles'); - assert.strictEqual(camelCase('xhr2 request'), 'xhr2Request'); - }); - - it('should handle acronyms', function() { - lodashStable.each(['safe HTML', 'safeHTML'], function(string) { - assert.strictEqual(camelCase(string), 'safeHtml'); - }); - - lodashStable.each(['escape HTML entities', 'escapeHTMLEntities'], function(string) { - assert.strictEqual(camelCase(string), 'escapeHtmlEntities'); - }); - - lodashStable.each(['XMLHttpRequest', 'XmlHTTPRequest'], function(string) { - assert.strictEqual(camelCase(string), 'xmlHttpRequest'); - }); - }); -}); diff --git a/test/capitalize.spec.js b/test/capitalize.spec.js new file mode 100644 index 0000000000..9d4cf8789d --- /dev/null +++ b/test/capitalize.spec.js @@ -0,0 +1,9 @@ +import capitalize from '../src/capitalize'; + +describe('capitalize', () => { + it('should capitalize the first character of a string', () => { + expect(capitalize('fred')).toBe('Fred'); + expect(capitalize('Fred')).toBe('Fred'); + expect(capitalize(' fred')).toBe(' fred'); + }); +}); diff --git a/test/capitalize.test.js b/test/capitalize.test.js deleted file mode 100644 index 0aeb2be926..0000000000 --- a/test/capitalize.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import capitalize from '../capitalize.js'; - -describe('capitalize', function() { - it('should capitalize the first character of a string', function() { - assert.strictEqual(capitalize('fred'), 'Fred'); - assert.strictEqual(capitalize('Fred'), 'Fred'); - assert.strictEqual(capitalize(' fred'), ' fred'); - }); -}); diff --git a/test/case-methods.spec.js b/test/case-methods.spec.js new file mode 100644 index 0000000000..26cd9d0727 --- /dev/null +++ b/test/case-methods.spec.js @@ -0,0 +1,132 @@ +import lodashStable from 'lodash'; +import { stubTrue, burredLetters, deburredLetters } from './utils'; +import camelCase from '../src/camelCase'; +import kebabCase from '../src/kebabCase'; +import lowerCase from '../src/lowerCase'; +import snakeCase from '../src/snakeCase'; +import startCase from '../src/startCase'; +import upperCase from '../src/upperCase'; + +const caseMethods = { + camelCase, + kebabCase, + lowerCase, + snakeCase, + startCase, + upperCase, +}; + +describe('case methods', () => { + lodashStable.each(['camel', 'kebab', 'lower', 'snake', 'start', 'upper'], (caseName) => { + const methodName = `${caseName}Case`; + const func = caseMethods[methodName]; + + const strings = [ + 'foo bar', + 'Foo bar', + 'foo Bar', + 'Foo Bar', + 'FOO BAR', + 'fooBar', + '--foo-bar--', + '__foo_bar__', + ]; + + const converted = (function () { + switch (caseName) { + case 'camel': + return 'fooBar'; + case 'kebab': + return 'foo-bar'; + case 'lower': + return 'foo bar'; + case 'snake': + return 'foo_bar'; + case 'start': + return 'Foo Bar'; + case 'upper': + return 'FOO BAR'; + } + })(); + + it(`\`_.${methodName}\` should convert \`string\` to ${caseName} case`, () => { + const actual = lodashStable.map(strings, (string) => { + const expected = caseName === 'start' && string === 'FOO BAR' ? string : converted; + return func(string) === expected; + }); + + expect(actual).toEqual(lodashStable.map(strings, stubTrue)); + }); + + it(`\`_.${methodName}\` should handle double-converting strings`, () => { + const actual = lodashStable.map(strings, (string) => { + const expected = caseName === 'start' && string === 'FOO BAR' ? string : converted; + return func(func(string)) === expected; + }); + + expect(actual).toEqual(lodashStable.map(strings, stubTrue)); + }); + + it(`\`_.${methodName}\` should remove contraction apostrophes`, () => { + const postfixes = ['d', 'll', 'm', 're', 's', 't', 've']; + + lodashStable.each(["'", '\u2019'], (apos) => { + const actual = lodashStable.map(postfixes, (postfix) => + func(`a b${apos}${postfix} c`), + ); + + const expected = lodashStable.map(postfixes, (postfix) => { + switch (caseName) { + case 'camel': + return `aB${postfix}C`; + case 'kebab': + return `a-b${postfix}-c`; + case 'lower': + return `a b${postfix} c`; + case 'snake': + return `a_b${postfix}_c`; + case 'start': + return `A B${postfix} C`; + case 'upper': + return `A B${postfix.toUpperCase()} C`; + } + }); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should remove Latin mathematical operators`, () => { + const actual = lodashStable.map(['\xd7', '\xf7'], func); + expect(actual).toEqual(['', '']); + }); + + it(`\`_.${methodName}\` should coerce \`string\` to a string`, () => { + const string = 'foo bar'; + expect(func(Object(string))).toBe(converted); + expect(func({ toString: lodashStable.constant(string) })).toBe(converted); + }); + }); + + (function () { + it('should get the original value after cycling through all case methods', () => { + const funcs = [ + camelCase, + kebabCase, + lowerCase, + snakeCase, + startCase, + lowerCase, + camelCase, + ]; + + const actual = lodashStable.reduce( + funcs, + (result, func) => func(result), + 'enable 6h format', + ); + + expect(actual).toBe('enable6HFormat'); + }); + })(); +}); diff --git a/test/case-methods.test.js b/test/case-methods.test.js deleted file mode 100644 index 0fb6a5d874..0000000000 --- a/test/case-methods.test.js +++ /dev/null @@ -1,105 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubTrue, burredLetters, deburredLetters } from './utils.js'; -import camelCase from '../camelCase.js'; -import kebabCase from '../kebabCase.js'; -import lowerCase from '../lowerCase.js'; -import snakeCase from '../snakeCase.js'; -import startCase from '../startCase.js'; -import upperCase from '../upperCase.js'; - -const caseMethods = { - camelCase, - kebabCase, - lowerCase, - snakeCase, - startCase, - upperCase -}; - -describe('case methods', function() { - lodashStable.each(['camel', 'kebab', 'lower', 'snake', 'start', 'upper'], function(caseName) { - var methodName = caseName + 'Case', - func = caseMethods[methodName]; - - var strings = [ - 'foo bar', 'Foo bar', 'foo Bar', 'Foo Bar', - 'FOO BAR', 'fooBar', '--foo-bar--', '__foo_bar__' - ]; - - var converted = (function() { - switch (caseName) { - case 'camel': return 'fooBar'; - case 'kebab': return 'foo-bar'; - case 'lower': return 'foo bar'; - case 'snake': return 'foo_bar'; - case 'start': return 'Foo Bar'; - case 'upper': return 'FOO BAR'; - } - }()); - - it('`_.' + methodName + '` should convert `string` to ' + caseName + ' case', function() { - var actual = lodashStable.map(strings, function(string) { - var expected = (caseName == 'start' && string == 'FOO BAR') ? string : converted; - return func(string) === expected; - }); - - assert.deepStrictEqual(actual, lodashStable.map(strings, stubTrue)); - }); - - it('`_.' + methodName + '` should handle double-converting strings', function() { - var actual = lodashStable.map(strings, function(string) { - var expected = (caseName == 'start' && string == 'FOO BAR') ? string : converted; - return func(func(string)) === expected; - }); - - assert.deepStrictEqual(actual, lodashStable.map(strings, stubTrue)); - }); - - it('`_.' + methodName + '` should remove contraction apostrophes', function() { - var postfixes = ['d', 'll', 'm', 're', 's', 't', 've']; - - lodashStable.each(["'", '\u2019'], function(apos) { - var actual = lodashStable.map(postfixes, function(postfix) { - return func('a b' + apos + postfix + ' c'); - }); - - var expected = lodashStable.map(postfixes, function(postfix) { - switch (caseName) { - case 'camel': return 'aB' + postfix + 'C'; - case 'kebab': return 'a-b' + postfix + '-c'; - case 'lower': return 'a b' + postfix + ' c'; - case 'snake': return 'a_b' + postfix + '_c'; - case 'start': return 'A B' + postfix + ' C'; - case 'upper': return 'A B' + postfix.toUpperCase() + ' C'; - } - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should remove Latin mathematical operators', function() { - var actual = lodashStable.map(['\xd7', '\xf7'], func); - assert.deepStrictEqual(actual, ['', '']); - }); - - it('`_.' + methodName + '` should coerce `string` to a string', function() { - var string = 'foo bar'; - assert.strictEqual(func(Object(string)), converted); - assert.strictEqual(func({ 'toString': lodashStable.constant(string) }), converted); - }); - }); - - (function() { - it('should get the original value after cycling through all case methods', function() { - var funcs = [camelCase, kebabCase, lowerCase, snakeCase, startCase, lowerCase, camelCase]; - - var actual = lodashStable.reduce(funcs, function(result, func) { - return func(result); - }, 'enable 6h format'); - - assert.strictEqual(actual, 'enable6HFormat'); - }); - })(); -}); diff --git a/test/castArray.spec.js b/test/castArray.spec.js new file mode 100644 index 0000000000..9899a351ed --- /dev/null +++ b/test/castArray.spec.js @@ -0,0 +1,22 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import castArray from '../src/castArray'; + +describe('castArray', () => { + it('should wrap non-array items in an array', () => { + const values = falsey.concat(true, 1, 'a', { a: 1 }); + const expected = lodashStable.map(values, (value) => [value]); + const actual = lodashStable.map(values, castArray); + + expect(actual).toEqual(expected); + }); + + it('should return array values by reference', () => { + const array = [1]; + expect(castArray(array)).toBe(array); + }); + + it('should return an empty array when no arguments are given', () => { + expect(castArray()).toEqual([]); + }); +}); diff --git a/test/castArray.test.js b/test/castArray.test.js deleted file mode 100644 index be874b5e04..0000000000 --- a/test/castArray.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey } from './utils.js'; -import castArray from '../castArray.js'; - -describe('castArray', function() { - it('should wrap non-array items in an array', function() { - var values = falsey.concat(true, 1, 'a', { 'a': 1 }), - expected = lodashStable.map(values, function(value) { return [value]; }), - actual = lodashStable.map(values, castArray); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return array values by reference', function() { - var array = [1]; - assert.strictEqual(castArray(array), array); - }); - - it('should return an empty array when no arguments are given', function() { - assert.deepStrictEqual(castArray(), []); - }); -}); diff --git a/test/chain.js b/test/chain.js deleted file mode 100644 index d02142071a..0000000000 --- a/test/chain.js +++ /dev/null @@ -1,74 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { square } from './utils.js'; -import chain from '../chain.js'; - -describe('chain', function() { - it('should return a wrapped value', function() { - var actual = chain({ 'a': 0 }); - assert.ok(actual instanceof _); - }); - - it('should return existing wrapped values', function() { - var wrapped = _({ 'a': 0 }); - assert.strictEqual(chain(wrapped), wrapped); - assert.strictEqual(wrapped.chain(), wrapped); - }); - - it('should enable chaining for methods that return unwrapped values', function() { - var array = ['c', 'b', 'a']; - - assert.ok(chain(array).head() instanceof _); - assert.ok(_(array).chain().head() instanceof _); - - assert.ok(chain(array).isArray() instanceof _); - assert.ok(_(array).chain().isArray() instanceof _); - - assert.ok(chain(array).sortBy().head() instanceof _); - assert.ok(_(array).chain().sortBy().head() instanceof _); - }); - - it('should chain multiple methods', function() { - lodashStable.times(2, function(index) { - var array = ['one two three four', 'five six seven eight', 'nine ten eleven twelve'], - expected = { ' ': 9, 'e': 14, 'f': 2, 'g': 1, 'h': 2, 'i': 4, 'l': 2, 'n': 6, 'o': 3, 'r': 2, 's': 2, 't': 5, 'u': 1, 'v': 4, 'w': 2, 'x': 1 }, - wrapped = index ? _(array).chain() : chain(array); - - var actual = wrapped - .chain() - .map(function(value) { return value.split(''); }) - .flatten() - .reduce(function(object, chr) { - object[chr] || (object[chr] = 0); - object[chr]++; - return object; - }, {}) - .value(); - - assert.deepStrictEqual(actual, expected); - - array = [1, 2, 3, 4, 5, 6]; - wrapped = index ? _(array).chain() : chain(array); - actual = wrapped - .chain() - .filter(function(n) { return n % 2 != 0; }) - .reject(function(n) { return n % 3 == 0; }) - .sortBy(function(n) { return -n; }) - .value(); - - assert.deepStrictEqual(actual, [5, 1]); - - array = [3, 4]; - wrapped = index ? _(array).chain() : chain(array); - actual = wrapped - .reverse() - .concat([2, 1]) - .unshift(5) - .tap(function(value) { value.pop(); }) - .map(square) - .value(); - - assert.deepStrictEqual(actual, [25, 16, 9, 4]); - }); - }); -}); diff --git a/test/chunk.spec.js b/test/chunk.spec.js new file mode 100644 index 0000000000..1a01a3917f --- /dev/null +++ b/test/chunk.spec.js @@ -0,0 +1,48 @@ +import lodashStable from 'lodash'; +import { falsey, stubArray } from './utils'; +import chunk from '../src/chunk'; + +describe('chunk', () => { + const array = [0, 1, 2, 3, 4, 5]; + + it('should return chunked arrays', () => { + const actual = chunk(array, 3); + expect(actual).toEqual([ + [0, 1, 2], + [3, 4, 5], + ]); + }); + + it('should return the last chunk as remaining elements', () => { + const actual = chunk(array, 4); + expect(actual).toEqual([ + [0, 1, 2, 3], + [4, 5], + ]); + }); + + it('should treat falsey `size` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => + value === undefined ? [[0], [1], [2], [3], [4], [5]] : [], + ); + + const actual = lodashStable.map(falsey, (size, index) => + index ? chunk(array, size) : chunk(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should ensure the minimum `size` is `0`', () => { + const values = lodashStable.reject(falsey, lodashStable.isUndefined).concat(-1, -Infinity); + const expected = lodashStable.map(values, stubArray); + + const actual = lodashStable.map(values, (n) => chunk(array, n)); + + expect(actual).toEqual(expected); + }); + + it('should coerce `size` to an integer', () => { + expect(chunk(array, array.length / 4)).toEqual([[0], [1], [2], [3], [4], [5]]); + }); +}); diff --git a/test/chunk.test.js b/test/chunk.test.js deleted file mode 100644 index f8bdc69d2b..0000000000 --- a/test/chunk.test.js +++ /dev/null @@ -1,45 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubArray } from './utils.js'; -import chunk from '../chunk.js'; - -describe('chunk', function() { - var array = [0, 1, 2, 3, 4, 5]; - - it('should return chunked arrays', function() { - var actual = chunk(array, 3); - assert.deepStrictEqual(actual, [[0, 1, 2], [3, 4, 5]]); - }); - - it('should return the last chunk as remaining elements', function() { - var actual = chunk(array, 4); - assert.deepStrictEqual(actual, [[0, 1, 2, 3], [4, 5]]); - }); - - it('should treat falsey `size` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? [[0], [1], [2], [3], [4], [5]] : []; - }); - - var actual = lodashStable.map(falsey, function(size, index) { - return index ? chunk(array, size) : chunk(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should ensure the minimum `size` is `0`', function() { - var values = lodashStable.reject(falsey, lodashStable.isUndefined).concat(-1, -Infinity), - expected = lodashStable.map(values, stubArray); - - var actual = lodashStable.map(values, function(n) { - return chunk(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should coerce `size` to an integer', function() { - assert.deepStrictEqual(chunk(array, array.length / 4), [[0], [1], [2], [3], [4], [5]]); - }); -}); diff --git a/test/clamp.js b/test/clamp.js deleted file mode 100644 index 911f57e9db..0000000000 --- a/test/clamp.js +++ /dev/null @@ -1,58 +0,0 @@ -import assert from 'assert'; -import clamp from '../clamp.js'; - -describe('clamp', function() { - it('should work with a `max`', function() { - assert.strictEqual(clamp(5, 3), 3); - assert.strictEqual(clamp(1, 3), 1); - }); - - it('should clamp negative numbers', function() { - assert.strictEqual(clamp(-10, -5, 5), -5); - assert.strictEqual(clamp(-10.2, -5.5, 5.5), -5.5); - assert.strictEqual(clamp(-Infinity, -5, 5), -5); - }); - - it('should clamp positive numbers', function() { - assert.strictEqual(clamp(10, -5, 5), 5); - assert.strictEqual(clamp(10.6, -5.6, 5.4), 5.4); - assert.strictEqual(clamp(Infinity, -5, 5), 5); - }); - - it('should not alter negative numbers in range', function() { - assert.strictEqual(clamp(-4, -5, 5), -4); - assert.strictEqual(clamp(-5, -5, 5), -5); - assert.strictEqual(clamp(-5.5, -5.6, 5.6), -5.5); - }); - - it('should not alter positive numbers in range', function() { - assert.strictEqual(clamp(4, -5, 5), 4); - assert.strictEqual(clamp(5, -5, 5), 5); - assert.strictEqual(clamp(4.5, -5.1, 5.2), 4.5); - }); - - it('should not alter `0` in range', function() { - assert.strictEqual(1 / clamp(0, -5, 5), Infinity); - }); - - it('should clamp to `0`', function() { - assert.strictEqual(1 / clamp(-10, 0, 5), Infinity); - }); - - it('should not alter `-0` in range', function() { - assert.strictEqual(1 / clamp(-0, -5, 5), -Infinity); - }); - - it('should clamp to `-0`', function() { - assert.strictEqual(1 / clamp(-10, -0, 5), -Infinity); - }); - - it('should return `NaN` when `number` is `NaN`', function() { - assert.deepStrictEqual(clamp(NaN, -5, 5), NaN); - }); - - it('should coerce `min` and `max` of `NaN` to `0`', function() { - assert.deepStrictEqual(clamp(1, -5, NaN), 0); - assert.deepStrictEqual(clamp(-1, NaN, 5), 0); - }); -}); diff --git a/test/clamp.spec.js b/test/clamp.spec.js new file mode 100644 index 0000000000..811d882602 --- /dev/null +++ b/test/clamp.spec.js @@ -0,0 +1,57 @@ +import clamp from '../src/clamp'; + +describe('clamp', () => { + it('should work with a `max`', () => { + expect(clamp(5, 3)).toBe(3); + expect(clamp(1, 3)).toBe(1); + }); + + it('should clamp negative numbers', () => { + expect(clamp(-10, -5, 5)).toBe(-5); + expect(clamp(-10.2, -5.5, 5.5)).toBe(-5.5); + expect(clamp(-Infinity, -5, 5)).toBe(-5); + }); + + it('should clamp positive numbers', () => { + expect(clamp(10, -5, 5)).toBe(5); + expect(clamp(10.6, -5.6, 5.4)).toBe(5.4); + expect(clamp(Infinity, -5, 5)).toBe(5); + }); + + it('should not alter negative numbers in range', () => { + expect(clamp(-4, -5, 5)).toBe(-4); + expect(clamp(-5, -5, 5)).toBe(-5); + expect(clamp(-5.5, -5.6, 5.6)).toBe(-5.5); + }); + + it('should not alter positive numbers in range', () => { + expect(clamp(4, -5, 5)).toBe(4); + expect(clamp(5, -5, 5)).toBe(5); + expect(clamp(4.5, -5.1, 5.2)).toBe(4.5); + }); + + it('should not alter `0` in range', () => { + expect(1 / clamp(0, -5, 5)).toBe(Infinity); + }); + + it('should clamp to `0`', () => { + expect(1 / clamp(-10, 0, 5)).toBe(Infinity); + }); + + it('should not alter `-0` in range', () => { + expect(1 / clamp(-0, -5, 5)).toBe(-Infinity); + }); + + it('should clamp to `-0`', () => { + expect(1 / clamp(-10, -0, 5)).toBe(-Infinity); + }); + + it('should return `NaN` when `number` is `NaN`', () => { + expect(clamp(NaN, -5, 5)).toEqual(NaN); + }); + + it('should coerce `min` and `max` of `NaN` to `0`', () => { + expect(clamp(1, -5, NaN)).toEqual(0); + expect(clamp(-1, NaN, 5)).toEqual(0); + }); +}); diff --git a/test/clone-methods.js b/test/clone-methods.js deleted file mode 100644 index b3f086d4d0..0000000000 --- a/test/clone-methods.js +++ /dev/null @@ -1,429 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - map, - set, - realm, - body, - asyncFunc, - genFunc, - errors, - _, - LARGE_ARRAY_SIZE, - isNpm, - mapCaches, - arrayBuffer, - stubTrue, - objectProto, - symbol, - defineProperty, - getSymbols, - document, - arrayViews, - slice, - noop, -} from './utils.js'; - -import cloneDeep from '../cloneDeep.js'; -import cloneDeepWith from '../cloneDeepWith.js'; -import last from '../last.js'; - -describe('clone methods', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 1; - Foo.c = function() {}; - - if (Map) { - var map = new Map; - map.set('a', 1); - map.set('b', 2); - } - if (Set) { - var set = new Set; - set.add(1); - set.add(2); - } - var objects = { - '`arguments` objects': arguments, - 'arrays': ['a', ''], - 'array-like objects': { '0': 'a', 'length': 1 }, - 'booleans': false, - 'boolean objects': Object(false), - 'date objects': new Date, - 'Foo instances': new Foo, - 'objects': { 'a': 0, 'b': 1, 'c': 2 }, - 'objects with object values': { 'a': /a/, 'b': ['B'], 'c': { 'C': 1 } }, - 'objects from another document': realm.object || {}, - 'maps': map, - 'null values': null, - 'numbers': 0, - 'number objects': Object(0), - 'regexes': /a/gim, - 'sets': set, - 'strings': 'a', - 'string objects': Object('a'), - 'undefined values': undefined - }; - - objects.arrays.length = 3; - - var uncloneable = { - 'DOM elements': body, - 'functions': Foo, - 'async functions': asyncFunc, - 'generator functions': genFunc, - 'the `Proxy` constructor': Proxy - }; - - lodashStable.each(errors, function(error) { - uncloneable[error.name + 's'] = error; - }); - - it('`_.clone` should perform a shallow clone', function() { - var array = [{ 'a': 0 }, { 'b': 1 }], - actual = _.clone(array); - - assert.deepStrictEqual(actual, array); - assert.ok(actual !== array && actual[0] === array[0]); - }); - - it('`_.cloneDeep` should deep clone objects with circular references', function() { - var object = { - 'foo': { 'b': { 'c': { 'd': {} } } }, - 'bar': {} - }; - - object.foo.b.c.d = object; - object.bar.b = object.foo.b; - - var actual = cloneDeep(object); - assert.ok(actual.bar.b === actual.foo.b && actual === actual.foo.b.c.d && actual !== object); - }); - - it('`_.cloneDeep` should deep clone objects with lots of circular references', function() { - var cyclical = {}; - lodashStable.times(LARGE_ARRAY_SIZE + 1, function(index) { - cyclical['v' + index] = [index ? cyclical['v' + (index - 1)] : cyclical]; - }); - - var clone = cloneDeep(cyclical), - actual = clone['v' + LARGE_ARRAY_SIZE][0]; - - assert.strictEqual(actual, clone['v' + (LARGE_ARRAY_SIZE - 1)]); - assert.notStrictEqual(actual, cyclical['v' + (LARGE_ARRAY_SIZE - 1)]); - }); - - it('`_.cloneDeepWith` should provide `stack` to `customizer`', function() { - var actual; - - cloneDeepWith({ 'a': 1 }, function() { - actual = last(arguments); - }); - - assert.ok(isNpm - ? actual.constructor.name == 'Stack' - : actual instanceof mapCaches.Stack - ); - }); - - lodashStable.each(['clone', 'cloneDeep'], function(methodName) { - var func = _[methodName], - isDeep = methodName == 'cloneDeep'; - - lodashStable.forOwn(objects, function(object, kind) { - it('`_.' + methodName + '` should clone ' + kind, function() { - var actual = func(object); - assert.ok(lodashStable.isEqual(actual, object)); - - if (lodashStable.isObject(object)) { - assert.notStrictEqual(actual, object); - } else { - assert.strictEqual(actual, object); - } - }); - }); - - it('`_.' + methodName + '` should clone array buffers', function() { - if (ArrayBuffer) { - var actual = func(arrayBuffer); - assert.strictEqual(actual.byteLength, arrayBuffer.byteLength); - assert.notStrictEqual(actual, arrayBuffer); - } - }); - - it('`_.' + methodName + '` should clone buffers', function() { - if (Buffer) { - var buffer = new Buffer([1, 2]), - actual = func(buffer); - - assert.strictEqual(actual.byteLength, buffer.byteLength); - assert.strictEqual(actual.inspect(), buffer.inspect()); - assert.notStrictEqual(actual, buffer); - - buffer[0] = 2; - assert.strictEqual(actual[0], isDeep ? 2 : 1); - } - }); - - it('`_.' + methodName + '` should clone `index` and `input` array properties', function() { - var array = /c/.exec('abcde'), - actual = func(array); - - assert.strictEqual(actual.index, 2); - assert.strictEqual(actual.input, 'abcde'); - }); - - it('`_.' + methodName + '` should clone `lastIndex` regexp property', function() { - var regexp = /c/g; - regexp.exec('abcde'); - - assert.strictEqual(func(regexp).lastIndex, 3); - }); - - it('`_.' + methodName + '` should clone expando properties', function() { - var values = lodashStable.map([false, true, 1, 'a'], function(value) { - var object = Object(value); - object.a = 1; - return object; - }); - - var expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return func(value).a === 1; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should clone prototype objects', function() { - var actual = func(Foo.prototype); - - assert.ok(!(actual instanceof Foo)); - assert.deepStrictEqual(actual, { 'b': 1 }); - }); - - it('`_.' + methodName + '` should set the `[[Prototype]]` of a clone', function() { - assert.ok(func(new Foo) instanceof Foo); - }); - - it('`_.' + methodName + '` should set the `[[Prototype]]` of a clone even when the `constructor` is incorrect', function() { - Foo.prototype.constructor = Object; - assert.ok(func(new Foo) instanceof Foo); - Foo.prototype.constructor = Foo; - }); - - it('`_.' + methodName + '` should ensure `value` constructor is a function before using its `[[Prototype]]`', function() { - Foo.prototype.constructor = null; - assert.ok(!(func(new Foo) instanceof Foo)); - Foo.prototype.constructor = Foo; - }); - - it('`_.' + methodName + '` should clone properties that shadow those on `Object.prototype`', function() { - var object = { - 'constructor': objectProto.constructor, - 'hasOwnProperty': objectProto.hasOwnProperty, - 'isPrototypeOf': objectProto.isPrototypeOf, - 'propertyIsEnumerable': objectProto.propertyIsEnumerable, - 'toLocaleString': objectProto.toLocaleString, - 'toString': objectProto.toString, - 'valueOf': objectProto.valueOf - }; - - var actual = func(object); - - assert.deepStrictEqual(actual, object); - assert.notStrictEqual(actual, object); - }); - - it('`_.' + methodName + '` should clone symbol properties', function() { - function Foo() { - this[symbol] = { 'c': 1 }; - } - - if (Symbol) { - var symbol2 = Symbol('b'); - Foo.prototype[symbol2] = 2; - - var symbol3 = Symbol('c'); - defineProperty(Foo.prototype, symbol3, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': 3 - }); - - var object = { 'a': { 'b': new Foo } }; - object[symbol] = { 'b': 1 }; - - var actual = func(object); - if (isDeep) { - assert.notStrictEqual(actual[symbol], object[symbol]); - assert.notStrictEqual(actual.a, object.a); - } else { - assert.strictEqual(actual[symbol], object[symbol]); - assert.strictEqual(actual.a, object.a); - } - assert.deepStrictEqual(actual[symbol], object[symbol]); - assert.deepStrictEqual(getSymbols(actual.a.b), [symbol]); - assert.deepStrictEqual(actual.a.b[symbol], object.a.b[symbol]); - assert.deepStrictEqual(actual.a.b[symbol2], object.a.b[symbol2]); - assert.deepStrictEqual(actual.a.b[symbol3], object.a.b[symbol3]); - } - }); - - it('`_.' + methodName + '` should clone symbol objects', function() { - if (Symbol) { - assert.strictEqual(func(symbol), symbol); - - var object = Object(symbol), - actual = func(object); - - assert.strictEqual(typeof actual, 'object'); - assert.strictEqual(typeof actual.valueOf(), 'symbol'); - assert.notStrictEqual(actual, object); - } - }); - - it('`_.' + methodName + '` should not clone symbol primitives', function() { - if (Symbol) { - assert.strictEqual(func(symbol), symbol); - } - }); - - it('`_.' + methodName + '` should not error on DOM elements', function() { - if (document) { - var element = document.createElement('div'); - - try { - assert.deepStrictEqual(func(element), {}); - } catch (e) { - assert.ok(false, e.message); - } - } - }); - - it('`_.' + methodName + '` should create an object from the same realm as `value`', function() { - var props = []; - - var objects = lodashStable.transform(_, function(result, value, key) { - if (lodashStable.startsWith(key, '_') && lodashStable.isObject(value) && - !lodashStable.isArguments(value) && !lodashStable.isElement(value) && - !lodashStable.isFunction(value)) { - props.push(lodashStable.capitalize(lodashStable.camelCase(key))); - result.push(value); - } - }, []); - - var expected = lodashStable.map(objects, stubTrue); - - var actual = lodashStable.map(objects, function(object) { - var Ctor = object.constructor, - result = func(object); - - return result !== object && ((result instanceof Ctor) || !(new Ctor instanceof Ctor)); - }); - - assert.deepStrictEqual(actual, expected, props.join(', ')); - }); - - it('`_.' + methodName + '` should perform a ' + (isDeep ? 'deep' : 'shallow') + ' clone when used as an iteratee for methods like `_.map`', function() { - var expected = [{ 'a': [0] }, { 'b': [1] }], - actual = lodashStable.map(expected, func); - - assert.deepStrictEqual(actual, expected); - - if (isDeep) { - assert.ok(actual[0] !== expected[0] && actual[0].a !== expected[0].a && actual[1].b !== expected[1].b); - } else { - assert.ok(actual[0] !== expected[0] && actual[0].a === expected[0].a && actual[1].b === expected[1].b); - } - }); - - it('`_.' + methodName + '` should return a unwrapped value when chaining', function() { - var object = objects.objects, - actual = _(object)[methodName](); - - assert.deepEqual(actual, object); - assert.notStrictEqual(actual, object); - }); - - lodashStable.each(arrayViews, function(type) { - it('`_.' + methodName + '` should clone ' + type + ' values', function() { - var Ctor = root[type]; - - lodashStable.times(2, function(index) { - if (Ctor) { - var buffer = new ArrayBuffer(24), - view = index ? new Ctor(buffer, 8, 1) : new Ctor(buffer), - actual = func(view); - - assert.deepStrictEqual(actual, view); - assert.notStrictEqual(actual, view); - assert.strictEqual(actual.buffer === view.buffer, !isDeep); - assert.strictEqual(actual.byteOffset, view.byteOffset); - assert.strictEqual(actual.length, view.length); - } - }); - }); - }); - - lodashStable.forOwn(uncloneable, function(value, key) { - it('`_.' + methodName + '` should not clone ' + key, function() { - if (value) { - var object = { 'a': value, 'b': { 'c': value } }, - actual = func(object), - expected = value === Foo ? { 'c': Foo.c } : {}; - - assert.deepStrictEqual(actual, object); - assert.notStrictEqual(actual, object); - assert.deepStrictEqual(func(value), expected); - } - }); - }); - }); - - lodashStable.each(['cloneWith', 'cloneDeepWith'], function(methodName) { - var func = _[methodName], - isDeep = methodName == 'cloneDeepWith'; - - it('`_.' + methodName + '` should provide correct `customizer` arguments', function() { - var argsList = [], - object = new Foo; - - func(object, function() { - var length = arguments.length, - args = slice.call(arguments, 0, length - (length > 1 ? 1 : 0)); - - argsList.push(args); - }); - - assert.deepStrictEqual(argsList, isDeep ? [[object], [1, 'a', object]] : [[object]]); - }); - - it('`_.' + methodName + '` should handle cloning when `customizer` returns `undefined`', function() { - var actual = func({ 'a': { 'b': 'c' } }, noop); - assert.deepStrictEqual(actual, { 'a': { 'b': 'c' } }); - }); - - lodashStable.forOwn(uncloneable, function(value, key) { - it('`_.' + methodName + '` should work with a `customizer` callback and ' + key, function() { - var customizer = function(value) { - return lodashStable.isPlainObject(value) ? undefined : value; - }; - - var actual = func(value, customizer); - assert.strictEqual(actual, value); - - var object = { 'a': value, 'b': { 'c': value } }; - actual = func(object, customizer); - - assert.deepStrictEqual(actual, object); - assert.notStrictEqual(actual, object); - }); - }); - }); -}); diff --git a/test/clone-methods.spec.js b/test/clone-methods.spec.js new file mode 100644 index 0000000000..c0ebd8fbda --- /dev/null +++ b/test/clone-methods.spec.js @@ -0,0 +1,445 @@ +import lodashStable from 'lodash'; + +import { + map, + set, + realm, + body, + asyncFunc, + genFunc, + errors, + _, + LARGE_ARRAY_SIZE, + isNpm, + mapCaches, + arrayBuffer, + stubTrue, + objectProto, + symbol, + defineProperty, + getSymbols, + document, + arrayViews, + slice, + noop, +} from './utils'; + +import cloneDeep from '../src/cloneDeep'; +import cloneDeepWith from '../src/cloneDeepWith'; +import last from '../src/last'; + +xdescribe('clone methods', function () { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 1; + Foo.c = function () {}; + + if (Map) { + var map = new Map(); + map.set('a', 1); + map.set('b', 2); + } + if (Set) { + var set = new Set(); + set.add(1); + set.add(2); + } + const objects = { + '`arguments` objects': arguments, + arrays: ['a', ''], + 'array-like objects': { 0: 'a', length: 1 }, + booleans: false, + 'boolean objects': Object(false), + 'date objects': new Date(), + 'Foo instances': new Foo(), + objects: { a: 0, b: 1, c: 2 }, + 'objects with object values': { a: /a/, b: ['B'], c: { C: 1 } }, + 'objects from another document': realm.object || {}, + maps: map, + 'null values': null, + numbers: 0, + 'number objects': Object(0), + regexes: /a/gim, + sets: set, + strings: 'a', + 'string objects': Object('a'), + 'undefined values': undefined, + }; + + objects.arrays.length = 3; + + const uncloneable = { + 'DOM elements': body, + functions: Foo, + 'async functions': asyncFunc, + 'generator functions': genFunc, + 'the `Proxy` constructor': Proxy, + }; + + lodashStable.each(errors, (error) => { + uncloneable[`${error.name}s`] = error; + }); + + it('`_.clone` should perform a shallow clone', () => { + const array = [{ a: 0 }, { b: 1 }]; + const actual = _.clone(array); + + expect(actual).toEqual(array); + expect(actual !== array && actual[0] === array[0]) + }); + + it('`_.cloneDeep` should deep clone objects with circular references', () => { + const object = { + foo: { b: { c: { d: {} } } }, + bar: {}, + }; + + object.foo.b.c.d = object; + object.bar.b = object.foo.b; + + const actual = cloneDeep(object); + assert.ok( + actual.bar.b === actual.foo.b && actual === actual.foo.b.c.d && actual !== object, + ); + }); + + it('`_.cloneDeep` should deep clone objects with lots of circular references', () => { + const cyclical = {}; + lodashStable.times(LARGE_ARRAY_SIZE + 1, (index) => { + cyclical[`v${index}`] = [index ? cyclical[`v${index - 1}`] : cyclical]; + }); + + const clone = cloneDeep(cyclical); + const actual = clone[`v${LARGE_ARRAY_SIZE}`][0]; + + expect(actual).toBe(clone[`v${LARGE_ARRAY_SIZE - 1}`]); + assert.notStrictEqual(actual, cyclical[`v${LARGE_ARRAY_SIZE - 1}`]); + }); + + it('`_.cloneDeepWith` should provide `stack` to `customizer`', () => { + let actual; + + cloneDeepWith({ a: 1 }, function () { + actual = last(arguments); + }); + + expect(isNpm ? actual.constructor.name === 'Stack' : actual instanceof mapCaches.Stack) + }); + + lodashStable.each(['clone', 'cloneDeep'], (methodName) => { + const func = _[methodName]; + const isDeep = methodName === 'cloneDeep'; + + lodashStable.forOwn(objects, (object, kind) => { + it(`\`_.${methodName}\` should clone ${kind}`, () => { + const actual = func(object); + expect(lodashStable.isEqual(actual, object)) + + if (lodashStable.isObject(object)) { + assert.notStrictEqual(actual, object); + } else { + expect(actual).toBe(object); + } + }); + }); + + it(`\`_.${methodName}\` should clone array buffers`, () => { + if (ArrayBuffer) { + const actual = func(arrayBuffer); + expect(actual.byteLength).toBe(arrayBuffer.byteLength); + assert.notStrictEqual(actual, arrayBuffer); + } + }); + + it(`\`_.${methodName}\` should clone buffers`, () => { + if (Buffer) { + const buffer = Buffer.from([1, 2]); + const actual = func(buffer); + + expect(actual.byteLength).toBe(buffer.byteLength); + expect(actual.inspect()).toBe(buffer.inspect()); + assert.notStrictEqual(actual, buffer); + + buffer[0] = 2; + expect(actual[0]).toBe(isDeep ? 2 : 1); + } + }); + + it(`\`_.${methodName}\` should clone \`index\` and \`input\` array properties`, () => { + const array = /c/.exec('abcde'); + const actual = func(array); + + expect(actual.index).toBe(2); + expect(actual.input).toBe('abcde'); + }); + + it(`\`_.${methodName}\` should clone \`lastIndex\` regexp property`, () => { + const regexp = /c/g; + regexp.exec('abcde'); + + expect(func(regexp).lastIndex).toBe(3); + }); + + it(`\`_.${methodName}\` should clone expando properties`, () => { + const values = lodashStable.map([false, true, 1, 'a'], (value) => { + const object = Object(value); + object.a = 1; + return object; + }); + + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => func(value).a === 1); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should clone prototype objects`, () => { + const actual = func(Foo.prototype); + + expect((actual instanceof Foo)).toBe(false) + expect(actual).toEqual({ b: 1 }); + }); + + it(`\`_.${methodName}\` should set the \`[[Prototype]]\` of a clone`, () => { + expect(func(new Foo()) instanceof Foo) + }); + + it(`\`_.${methodName}\` should set the \`[[Prototype]]\` of a clone even when the \`constructor\` is incorrect`, () => { + Foo.prototype.constructor = Object; + expect(func(new Foo()) instanceof Foo) + Foo.prototype.constructor = Foo; + }); + + it(`\`_.${methodName}\` should ensure \`value\` constructor is a function before using its \`[[Prototype]]\``, () => { + Foo.prototype.constructor = null; + expect((func(new Foo()) instanceof Foo)).toBe(false) + Foo.prototype.constructor = Foo; + }); + + it(`\`_.${methodName}\` should clone properties that shadow those on \`Object.prototype\``, () => { + const object = { + constructor: objectProto.constructor, + hasOwnProperty: objectProto.hasOwnProperty, + isPrototypeOf: objectProto.isPrototypeOf, + propertyIsEnumerable: objectProto.propertyIsEnumerable, + toLocaleString: objectProto.toLocaleString, + toString: objectProto.toString, + valueOf: objectProto.valueOf, + }; + + const actual = func(object); + + expect(actual).toEqual(object); + assert.notStrictEqual(actual, object); + }); + + it(`\`_.${methodName}\` should clone symbol properties`, () => { + function Foo() { + this[symbol] = { c: 1 }; + } + + if (Symbol) { + const symbol2 = Symbol('b'); + Foo.prototype[symbol2] = 2; + + const symbol3 = Symbol('c'); + defineProperty(Foo.prototype, symbol3, { + configurable: true, + enumerable: false, + writable: true, + value: 3, + }); + + const object = { a: { b: new Foo() } }; + object[symbol] = { b: 1 }; + + const actual = func(object); + if (isDeep) { + assert.notStrictEqual(actual[symbol], object[symbol]); + assert.notStrictEqual(actual.a, object.a); + } else { + expect(actual[symbol]).toBe(object[symbol]); + expect(actual.a).toBe(object.a); + } + expect(actual[symbol]).toEqual(object[symbol]); + expect(getSymbols(actual.a.b)).toEqual([symbol]); + expect(actual.a.b[symbol]).toEqual(object.a.b[symbol]); + expect(actual.a.b[symbol2]).toEqual(object.a.b[symbol2]); + expect(actual.a.b[symbol3]).toEqual(object.a.b[symbol3]); + } + }); + + it(`\`_.${methodName}\` should clone symbol objects`, () => { + if (Symbol) { + expect(func(symbol)).toBe(symbol); + + const object = Object(symbol); + const actual = func(object); + + expect(typeof actual).toBe('object'); + expect(typeof actual.valueOf()).toBe('symbol'); + assert.notStrictEqual(actual, object); + } + }); + + it(`\`_.${methodName}\` should not clone symbol primitives`, () => { + if (Symbol) { + expect(func(symbol)).toBe(symbol); + } + }); + + it(`\`_.${methodName}\` should not error on DOM elements`, () => { + if (document) { + const element = document.createElement('div'); + + try { + expect(func(element)).toEqual({}); + } catch (e) { + expect(false, e.message) + } + } + }); + + it(`\`_.${methodName}\` should create an object from the same realm as \`value\``, () => { + const props = []; + + const objects = lodashStable.transform( + _, + (result, value, key) => { + if ( + lodashStable.startsWith(key, '_') && + lodashStable.isObject(value) && + !lodashStable.isArguments(value) && + !lodashStable.isElement(value) && + !lodashStable.isFunction(value) + ) { + props.push(lodashStable.capitalize(lodashStable.camelCase(key))); + result.push(value); + } + }, + [], + ); + + const expected = lodashStable.map(objects, stubTrue); + + const actual = lodashStable.map(objects, (object) => { + const Ctor = object.constructor; + const result = func(object); + + return ( + result !== object && (result instanceof Ctor || !(new Ctor() instanceof Ctor)) + ); + }); + + expect(actual, expected, props.join(').toEqual(')); + }); + + it(`\`_.${methodName}\` should perform a ${ + isDeep ? 'deep' : 'shallow' + } clone when used as an iteratee for methods like \`_.map\``, () => { + const expected = [{ a: [0] }, { b: [1] }]; + const actual = lodashStable.map(expected, func); + + expect(actual).toEqual(expected); + + if (isDeep) { + assert.ok( + actual[0] !== expected[0] && + actual[0].a !== expected[0].a && + actual[1].b !== expected[1].b, + ); + } else { + assert.ok( + actual[0] !== expected[0] && + actual[0].a === expected[0].a && + actual[1].b === expected[1].b, + ); + } + }); + + it(`\`_.${methodName}\` should return a unwrapped value when chaining`, () => { + const object = objects.objects; + const actual = _(object)[methodName](); + + expect(actual).toEqual(object); + assert.notStrictEqual(actual, object); + }); + + lodashStable.each(arrayViews, (type) => { + it(`\`_.${methodName}\` should clone ${type} values`, () => { + const Ctor = root[type]; + + lodashStable.times(2, (index) => { + if (Ctor) { + const buffer = new ArrayBuffer(24); + const view = index ? new Ctor(buffer, 8, 1) : new Ctor(buffer); + const actual = func(view); + + expect(actual).toEqual(view); + assert.notStrictEqual(actual, view); + expect(actual.buffer === view.buffer).toBe(!isDeep); + expect(actual.byteOffset).toBe(view.byteOffset); + expect(actual.length).toBe(view.length); + } + }); + }); + }); + + lodashStable.forOwn(uncloneable, (value, key) => { + it(`\`_.${methodName}\` should not clone ${key}`, () => { + if (value) { + const object = { a: value, b: { c: value } }; + const actual = func(object); + const expected = value === Foo ? { c: Foo.c } : {}; + + expect(actual).toEqual(object); + assert.notStrictEqual(actual, object); + expect(func(value)).toEqual(expected); + } + }); + }); + }); + + lodashStable.each(['cloneWith', 'cloneDeepWith'], (methodName) => { + const func = _[methodName]; + const isDeep = methodName === 'cloneDeepWith'; + + it(`\`_.${methodName}\` should provide correct \`customizer\` arguments`, () => { + const argsList = []; + const object = new Foo(); + + func(object, function () { + const length = arguments.length; + const args = slice.call(arguments, 0, length - (length > 1 ? 1 : 0)); + + argsList.push(args); + }); + + expect(argsList, isDeep ? [[object], [1, 'a').toEqual(object]] : [[object]]); + }); + + it(`\`_.${methodName}\` should handle cloning when \`customizer\` returns \`undefined\``, () => { + const actual = func({ a: { b: 'c' } }, noop); + expect(actual).toEqual({ a: { b: 'c' } }); + }); + + lodashStable.forOwn(uncloneable, (value, key) => { + it(`\`_.${methodName}\` should work with a \`customizer\` callback and ${key}`, () => { + const customizer = function (value) { + return lodashStable.isPlainObject(value) ? undefined : value; + }; + + let actual = func(value, customizer); + expect(actual).toBe(value); + + const object = { a: value, b: { c: value } }; + actual = func(object, customizer); + + expect(actual).toEqual(object); + assert.notStrictEqual(actual, object); + }); + }); + }); +}); diff --git a/test/compact.js b/test/compact.js deleted file mode 100644 index 2c39358639..0000000000 --- a/test/compact.js +++ /dev/null @@ -1,42 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, _, falsey } from './utils.js'; -import compact from '../compact.js'; -import slice from '../slice.js'; - -describe('compact', function() { - var largeArray = lodashStable.range(LARGE_ARRAY_SIZE).concat(null); - - it('should filter falsey values', function() { - var array = ['0', '1', '2']; - assert.deepStrictEqual(compact(falsey.concat(array)), array); - }); - - it('should work when in-between lazy operators', function() { - var actual = _(falsey).thru(slice).compact().thru(slice).value(); - assert.deepEqual(actual, []); - - actual = _(falsey).thru(slice).push(true, 1).compact().push('a').value(); - assert.deepEqual(actual, [true, 1, 'a']); - }); - - it('should work in a lazy sequence', function() { - var actual = _(largeArray).slice(1).compact().reverse().take().value(); - assert.deepEqual(actual, _.take(compact(slice(largeArray, 1)).reverse())); - }); - - it('should work in a lazy sequence with a custom `_.iteratee`', function() { - var iteratee = _.iteratee, - pass = false; - - _.iteratee = identity; - - try { - var actual = _(largeArray).slice(1).compact().value(); - pass = lodashStable.isEqual(actual, compact(slice(largeArray, 1))); - } catch (e) {console.log(e);} - - assert.ok(pass); - _.iteratee = iteratee; - }); -}); diff --git a/test/compact.spec.js b/test/compact.spec.js new file mode 100644 index 0000000000..47250de692 --- /dev/null +++ b/test/compact.spec.js @@ -0,0 +1,9 @@ +import { falsey } from './utils'; +import compact from '../src/compact'; + +describe('compact', () => { + it('should filter falsey values', () => { + const array = ['0', '1', '2']; + expect(compact(falsey.concat(array))).toEqual(array); + }); +}); diff --git a/test/concat.js b/test/concat.js deleted file mode 100644 index d665619821..0000000000 --- a/test/concat.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import concat from '../concat.js'; - -describe('concat', function() { - it('should shallow clone `array`', function() { - var array = [1, 2, 3], - actual = concat(array); - - assert.deepStrictEqual(actual, array); - assert.notStrictEqual(actual, array); - }); - - it('should concat arrays and values', function() { - var array = [1], - actual = concat(array, 2, [3], [[4]]); - - assert.deepStrictEqual(actual, [1, 2, 3, [4]]); - assert.deepStrictEqual(array, [1]); - }); - - it('should cast non-array `array` values to arrays', function() { - var values = [, null, undefined, false, true, 1, NaN, 'a']; - - var expected = lodashStable.map(values, function(value, index) { - return index ? [value] : []; - }); - - var actual = lodashStable.map(values, function(value, index) { - return index ? concat(value) : concat(); - }); - - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(values, function(value) { - return [value, 2, [3]]; - }); - - actual = lodashStable.map(values, function(value) { - return concat(value, [2], [[3]]); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should treat sparse arrays as dense', function() { - var expected = [], - actual = concat(Array(1), Array(1)); - - expected.push(undefined, undefined); - - assert.ok('0'in actual); - assert.ok('1' in actual); - assert.deepStrictEqual(actual, expected); - }); - - it('should return a new wrapped array', function() { - var array = [1], - wrapped = _(array).concat([2, 3]), - actual = wrapped.value(); - - assert.deepEqual(array, [1]); - assert.deepEqual(actual, [1, 2, 3]); - }); -}); diff --git a/test/concat.spec.js b/test/concat.spec.js new file mode 100644 index 0000000000..8682b8fb76 --- /dev/null +++ b/test/concat.spec.js @@ -0,0 +1,56 @@ +import lodashStable from 'lodash'; +import concat from '../src/concat'; + +describe('concat', () => { + it('should shallow clone `array`', () => { + const array = [1, 2, 3]; + const actual = concat(array); + + expect(actual).toEqual(array); + assert.notStrictEqual(actual, array); + }); + + it('should concat arrays and values', () => { + const array = [1]; + const actual = concat(array, 2, [3], [[4]]); + + expect(actual).toEqual([1, 2, 3, [4]]); + expect(array).toEqual([1]); + }); + + it('should cast non-array `array` values to arrays', () => { + const values = [, null, undefined, false, true, 1, NaN, 'a']; + + let expected = lodashStable.map(values, (value, index) => (index ? [value] : [])); + + let actual = lodashStable.map(values, (value, index) => (index ? concat(value) : concat())); + + expect(actual).toEqual(expected); + + expected = lodashStable.map(values, (value) => [value, 2, [3]]); + + actual = lodashStable.map(values, (value) => concat(value, [2], [[3]])); + + expect(actual).toEqual(expected); + }); + + it('should treat sparse arrays as dense', () => { + const expected = []; + const actual = concat(Array(1), Array(1)); + + expected.push(undefined, undefined); + + expect('0' in actual).toBeTruthy(); + expect('1' in actual).toBeTruthy(); + expect(actual).toEqual(expected); + }); + + it('should return a new wrapped array', () => { + const array = [1]; + const wrapped = _(array).concat([2, 3]); + const actual = wrapped.value(); + + expect(array).toEqual([1]); + expect(actual).toEqual([1, 2, 3]); + }); +}); diff --git a/test/cond.js b/test/cond.js deleted file mode 100644 index e3594ab46d..0000000000 --- a/test/cond.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, stubA, stubB, stubC, slice, stubFalse, stubTrue } from './utils.js'; - -describe('cond', function() { - it('should create a conditional function', function() { - var cond = _.cond([ - [lodashStable.matches({ 'a': 1 }), stubA], - [lodashStable.matchesProperty('b', 1), stubB], - [lodashStable.property('c'), stubC] - ]); - - assert.strictEqual(cond({ 'a': 1, 'b': 2, 'c': 3 }), 'a'); - assert.strictEqual(cond({ 'a': 0, 'b': 1, 'c': 2 }), 'b'); - assert.strictEqual(cond({ 'a': -1, 'b': 0, 'c': 1 }), 'c'); - }); - - it('should provide arguments to functions', function() { - var args1, - args2, - expected = ['a', 'b', 'c']; - - var cond = _.cond([[ - function() { args1 || (args1 = slice.call(arguments)); return true; }, - function() { args2 || (args2 = slice.call(arguments)); } - ]]); - - cond('a', 'b', 'c'); - - assert.deepStrictEqual(args1, expected); - assert.deepStrictEqual(args2, expected); - }); - - it('should work with predicate shorthands', function() { - var cond = _.cond([ - [{ 'a': 1 }, stubA], - [['b', 1], stubB], - ['c', stubC] - ]); - - assert.strictEqual(cond({ 'a': 1, 'b': 2, 'c': 3 }), 'a'); - assert.strictEqual(cond({ 'a': 0, 'b': 1, 'c': 2 }), 'b'); - assert.strictEqual(cond({ 'a': -1, 'b': 0, 'c': 1 }), 'c'); - }); - - it('should return `undefined` when no condition is met', function() { - var cond = _.cond([[stubFalse, stubA]]); - assert.strictEqual(cond({ 'a': 1 }), undefined); - }); - - it('should throw a TypeError if `pairs` is not composed of functions', function() { - lodashStable.each([false, true], function(value) { - assert.throws(function() { _.cond([[stubTrue, value]])(); }, TypeError); - }); - }); - - it('should use `this` binding of function for `pairs`', function() { - var cond = _.cond([ - [function(a) { return this[a]; }, function(a, b) { return this[b]; }] - ]); - - var object = { 'cond': cond, 'a': 1, 'b': 2 }; - assert.strictEqual(object.cond('a', 'b'), 2); - }); -}); diff --git a/test/cond.spec.js b/test/cond.spec.js new file mode 100644 index 0000000000..6d97da9a48 --- /dev/null +++ b/test/cond.spec.js @@ -0,0 +1,81 @@ +import lodashStable from 'lodash'; +import cond from '../src/cond'; +import { stubA, stubB, stubC, slice, stubFalse, stubTrue } from './utils'; + +describe('cond', () => { + it('should create a conditional function', () => { + const resultFunc = cond([ + [lodashStable.matches({ a: 1 }), stubA], + [lodashStable.matchesProperty('b', 1), stubB], + [lodashStable.property('c'), stubC], + ]); + + expect(resultFunc({ a: 1, b: 2, c: 3 })).toBe('a'); + expect(resultFunc({ a: 0, b: 1, c: 2 })).toBe('b'); + expect(resultFunc({ a: -1, b: 0, c: 1 })).toBe('c'); + }); + + it('should provide arguments to functions', () => { + let args1; + let args2; + const expected = ['a', 'b', 'c']; + + const resultFunc = cond([ + [ + function () { + args1 || (args1 = slice.call(arguments)); + return true; + }, + function () { + args2 || (args2 = slice.call(arguments)); + }, + ], + ]); + + resultFunc('a', 'b', 'c'); + + expect(args1).toEqual(expected); + expect(args2).toEqual(expected); + }); + + it('should work with predicate shorthands', () => { + const resultFunc = cond([ + [{ a: 1 }, stubA], + [['b', 1], stubB], + ['c', stubC], + ]); + + expect(resultFunc({ a: 1, b: 2, c: 3 })).toBe('a'); + expect(resultFunc({ a: 0, b: 1, c: 2 })).toBe('b'); + expect(resultFunc({ a: -1, b: 0, c: 1 })).toBe('c'); + }); + + it('should return `undefined` when no condition is met', () => { + const resultFunc = cond([[stubFalse, stubA]]); + expect(resultFunc({ a: 1 })).toBe(undefined); + }); + + it('should throw a TypeError if `pairs` is not resultFunc of functions', () => { + lodashStable.each([false, true], (value) => { + expect(() => { + cond([[stubTrue, value]])(); + }).toThrowError(TypeError); + }); + }); + + it('should use `this` binding of function for `pairs`', () => { + const resultFunc = cond([ + [ + function (a) { + return this[a]; + }, + function (a, b) { + return this[b]; + }, + ], + ]); + + const object = { resultFunc, a: 1, b: 2 }; + expect(object.resultFunc('a', 'b')).toBe(2); + }); +}); diff --git a/test/conforms-methods.js b/test/conforms-methods.js deleted file mode 100644 index c28e8f8b54..0000000000 --- a/test/conforms-methods.js +++ /dev/null @@ -1,153 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, stubFalse, stubTrue, empties } from './utils.js'; -import conformsTo from '../conformsTo.js'; - -describe('conforms methods', function() { - lodashStable.each(['conforms', 'conformsTo'], function(methodName) { - var isConforms = methodName == 'conforms'; - - function conforms(source) { - return isConforms ? _.conforms(source) : function(object) { - return conformsTo(object, source); - }; - } - - it('`_.' + methodName + '` should check if `object` conforms to `source`', function() { - var objects = [ - { 'a': 1, 'b': 8 }, - { 'a': 2, 'b': 4 }, - { 'a': 3, 'b': 16 } - ]; - - var par = conforms({ - 'b': function(value) { return value > 4; } - }); - - var actual = lodashStable.filter(objects, par); - assert.deepStrictEqual(actual, [objects[0], objects[2]]); - - par = conforms({ - 'b': function(value) { return value > 8; }, - 'a': function(value) { return value > 1; } - }); - - actual = lodashStable.filter(objects, par); - assert.deepStrictEqual(actual, [objects[2]]); - }); - - it('`_.' + methodName + '` should not match by inherited `source` properties', function() { - function Foo() { - this.a = function(value) { - return value > 1; - }; - } - Foo.prototype.b = function(value) { - return value > 8; - }; - - var objects = [ - { 'a': 1, 'b': 8 }, - { 'a': 2, 'b': 4 }, - { 'a': 3, 'b': 16 } - ]; - - var par = conforms(new Foo), - actual = lodashStable.filter(objects, par); - - assert.deepStrictEqual(actual, [objects[1], objects[2]]); - }); - - it('`_.' + methodName + '` should not invoke `source` predicates for missing `object` properties', function() { - var count = 0; - - var par = conforms({ - 'a': function() { count++; return true; } - }); - - assert.strictEqual(par({}), false); - assert.strictEqual(count, 0); - }); - - it('`_.' + methodName + '` should work with a function for `object`', function() { - function Foo() {} - Foo.a = 1; - - function Bar() {} - Bar.a = 2; - - var par = conforms({ - 'a': function(value) { return value > 1; } - }); - - assert.strictEqual(par(Foo), false); - assert.strictEqual(par(Bar), true); - }); - - it('`_.' + methodName + '` should work with a function for `source`', function() { - function Foo() {} - Foo.a = function(value) { return value > 1; }; - - var objects = [{ 'a': 1 }, { 'a': 2 }], - actual = lodashStable.filter(objects, conforms(Foo)); - - assert.deepStrictEqual(actual, [objects[1]]); - }); - - it('`_.' + methodName + '` should work with a non-plain `object`', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var par = conforms({ - 'b': function(value) { return value > 1; } - }); - - assert.strictEqual(par(new Foo), true); - }); - - it('`_.' + methodName + '` should return `false` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse); - - var par = conforms({ - 'a': function(value) { return value > 1; } - }); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? par(value) : par(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `true` when comparing an empty `source` to a nullish `object`', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubTrue), - par = conforms({}); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? par(value) : par(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `true` when comparing an empty `source`', function() { - var object = { 'a': 1 }, - expected = lodashStable.map(empties, stubTrue); - - var actual = lodashStable.map(empties, function(value) { - var par = conforms(value); - return par(object); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/conforms-methods.spec.js b/test/conforms-methods.spec.js new file mode 100644 index 0000000000..4826bd2c7c --- /dev/null +++ b/test/conforms-methods.spec.js @@ -0,0 +1,171 @@ +import lodashStable from 'lodash'; +import { _, stubFalse, stubTrue, empties } from './utils'; +import conformsTo from '../src/conformsTo'; + +describe('conforms methods', () => { + lodashStable.each(['conforms', 'conformsTo'], (methodName) => { + const isConforms = methodName === 'conforms'; + + function conforms(source) { + return isConforms + ? _.conforms(source) + : function (object) { + return conformsTo(object, source); + }; + } + + it(`\`_.${methodName}\` should check if \`object\` conforms to \`source\``, () => { + const objects = [ + { a: 1, b: 8 }, + { a: 2, b: 4 }, + { a: 3, b: 16 }, + ]; + + let par = conforms({ + b: function (value) { + return value > 4; + }, + }); + + let actual = lodashStable.filter(objects, par); + expect(actual).toEqual([objects[0], objects[2]]); + + par = conforms({ + b: function (value) { + return value > 8; + }, + a: function (value) { + return value > 1; + }, + }); + + actual = lodashStable.filter(objects, par); + expect(actual).toEqual([objects[2]]); + }); + + it(`\`_.${methodName}\` should not match by inherited \`source\` properties`, () => { + function Foo() { + this.a = function (value) { + return value > 1; + }; + } + Foo.prototype.b = function (value) { + return value > 8; + }; + + const objects = [ + { a: 1, b: 8 }, + { a: 2, b: 4 }, + { a: 3, b: 16 }, + ]; + + const par = conforms(new Foo()); + const actual = lodashStable.filter(objects, par); + + expect(actual).toEqual([objects[1], objects[2]]); + }); + + it(`\`_.${methodName}\` should not invoke \`source\` predicates for missing \`object\` properties`, () => { + let count = 0; + + const par = conforms({ + a: function () { + count++; + return true; + }, + }); + + expect(par({})).toBe(false); + expect(count).toBe(0); + }); + + it(`\`_.${methodName}\` should work with a function for \`object\``, () => { + function Foo() {} + Foo.a = 1; + + function Bar() {} + Bar.a = 2; + + const par = conforms({ + a: function (value) { + return value > 1; + }, + }); + + expect(par(Foo)).toBe(false); + expect(par(Bar)).toBe(true); + }); + + it(`\`_.${methodName}\` should work with a function for \`source\``, () => { + function Foo() {} + Foo.a = function (value) { + return value > 1; + }; + + const objects = [{ a: 1 }, { a: 2 }]; + const actual = lodashStable.filter(objects, conforms(Foo)); + + expect(actual).toEqual([objects[1]]); + }); + + it(`\`_.${methodName}\` should work with a non-plain \`object\``, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const par = conforms({ + b: function (value) { + return value > 1; + }, + }); + + expect(par(new Foo())).toBe(true); + }); + + it(`\`_.${methodName}\` should return \`false\` when \`object\` is nullish`, () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubFalse); + + const par = conforms({ + a: function (value) { + return value > 1; + }, + }); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? par(value) : par(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`true\` when comparing an empty \`source\` to a nullish \`object\``, () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubTrue); + const par = conforms({}); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? par(value) : par(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`true\` when comparing an empty \`source\``, () => { + const object = { a: 1 }; + const expected = lodashStable.map(empties, stubTrue); + + const actual = lodashStable.map(empties, (value) => { + const par = conforms(value); + return par(object); + }); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/conforms.js b/test/conforms.js deleted file mode 100644 index 204694a375..0000000000 --- a/test/conforms.js +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'assert'; -import conforms from '../conforms.js'; - -describe('conforms', function() { - it('should not change behavior if `source` is modified', function() { - var object = { 'a': 2 }, - source = { 'a': function(value) { return value > 1; } }, - par = conforms(source); - - assert.strictEqual(par(object), true); - - source.a = function(value) { return value < 2; }; - assert.strictEqual(par(object), true); - }); -}); diff --git a/test/conforms.spec.js b/test/conforms.spec.js new file mode 100644 index 0000000000..0d1c880463 --- /dev/null +++ b/test/conforms.spec.js @@ -0,0 +1,20 @@ +import conforms from '../src/conforms'; + +describe('conforms', () => { + it('should not change behavior if `source` is modified', () => { + const object = { a: 2 }; + const source = { + a: function (value) { + return value > 1; + }, + }; + const par = conforms(source); + + expect(par(object)).toBe(true); + + source.a = function (value) { + return value < 2; + }; + expect(par(object)).toBe(true); + }); +}); diff --git a/test/constant.js b/test/constant.js deleted file mode 100644 index 00151ad67e..0000000000 --- a/test/constant.js +++ /dev/null @@ -1,40 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, _, falsey, stubTrue } from './utils.js'; - -describe('constant', function() { - it('should create a function that returns `value`', function() { - var object = { 'a': 1 }, - values = Array(2).concat(empties, true, 1, 'a'), - constant = _.constant(object); - - var results = lodashStable.map(values, function(value, index) { - if (index < 2) { - return index ? constant.call({}) : constant(); - } - return constant(value); - }); - - assert.ok(lodashStable.every(results, function(result) { - return result === object; - })); - }); - - it('should work with falsey values', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - var constant = index ? _.constant(value) : _.constant(), - result = constant(); - - return (result === value) || (result !== result && value !== value); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return a wrapped value when chaining', function() { - var wrapped = _(true).constant(); - assert.ok(wrapped instanceof _); - }); -}); diff --git a/test/constant.spec.js b/test/constant.spec.js new file mode 100644 index 0000000000..0b81fda927 --- /dev/null +++ b/test/constant.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { empties, _, falsey, stubTrue } from './utils'; + +describe('constant', () => { + it('should create a function that returns `value`', () => { + const object = { a: 1 }; + const values = Array(2).concat(empties, true, 1, 'a'); + const constant = _.constant(object); + + const results = lodashStable.map(values, (value, index) => { + if (index < 2) { + return index ? constant.call({}) : constant(); + } + return constant(value); + }); + + expect(lodashStable.every(results, (result) => result === object)); + }); + + it('should work with falsey values', () => { + const expected = lodashStable.map(falsey, stubTrue); + + const actual = lodashStable.map(falsey, (value, index) => { + const constant = index ? _.constant(value) : _.constant(); + const result = constant(); + + return result === value || (result !== result && value !== value); + }); + + expect(actual).toEqual(expected); + }); + + it('should return a wrapped value when chaining', () => { + const wrapped = _(true).constant(); + expect(wrapped instanceof _); + }); +}); diff --git a/test/countBy.js b/test/countBy.js deleted file mode 100644 index b3e7a27795..0000000000 --- a/test/countBy.js +++ /dev/null @@ -1,66 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE } from './utils.js'; -import countBy from '../countBy.js'; - -describe('countBy', function() { - var array = [6.1, 4.2, 6.3]; - - it('should transform keys by `iteratee`', function() { - var actual = countBy(array, Math.floor); - assert.deepStrictEqual(actual, { '4': 1, '6': 2 }); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var array = [4, 6, 6], - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant({ '4': 1, '6': 2 })); - - var actual = lodashStable.map(values, function(value, index) { - return index ? countBy(array, value) : countBy(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var actual = countBy(['one', 'two', 'three'], 'length'); - assert.deepStrictEqual(actual, { '3': 2, '5': 1 }); - }); - - it('should only add values to own, not inherited, properties', function() { - var actual = countBy(array, function(n) { - return Math.floor(n) > 4 ? 'hasOwnProperty' : 'constructor'; - }); - - assert.deepStrictEqual(actual.constructor, 1); - assert.deepStrictEqual(actual.hasOwnProperty, 2); - }); - - it('should work with a number for `iteratee`', function() { - var array = [ - [1, 'a'], - [2, 'a'], - [2, 'b'] - ]; - - assert.deepStrictEqual(countBy(array, 0), { '1': 1, '2': 2 }); - assert.deepStrictEqual(countBy(array, 1), { 'a': 2, 'b': 1 }); - }); - - it('should work with an object for `collection`', function() { - var actual = countBy({ 'a': 6.1, 'b': 4.2, 'c': 6.3 }, Math.floor); - assert.deepStrictEqual(actual, { '4': 1, '6': 2 }); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE).concat( - lodashStable.range(Math.floor(LARGE_ARRAY_SIZE / 2), LARGE_ARRAY_SIZE), - lodashStable.range(Math.floor(LARGE_ARRAY_SIZE / 1.5), LARGE_ARRAY_SIZE) - ); - - var actual = _(array).countBy().map(square).filter(isEven).take().value(); - - assert.deepEqual(actual, _.take(_.filter(_.map(countBy(array), square), isEven))); - }); -}); diff --git a/test/countBy.spec.js b/test/countBy.spec.js new file mode 100644 index 0000000000..1d2d9b0e49 --- /dev/null +++ b/test/countBy.spec.js @@ -0,0 +1,53 @@ +import lodashStable from 'lodash'; +import countBy from '../src/countBy'; + +describe('countBy', () => { + const array = [6.1, 4.2, 6.3]; + + it('should transform keys by `iteratee`', () => { + const actual = countBy(array, Math.floor); + expect(actual).toEqual({ 4: 1, 6: 2 }); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const array = [4, 6, 6]; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant({ 4: 1, 6: 2 })); + + const actual = lodashStable.map(values, (value, index) => + index ? countBy(array, value) : countBy(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const actual = countBy(['one', 'two', 'three'], 'length'); + expect(actual).toEqual({ 3: 2, 5: 1 }); + }); + + it('should only add values to own, not inherited, properties', () => { + const actual = countBy(array, (n) => + Math.floor(n) > 4 ? 'hasOwnProperty' : 'constructor', + ); + + expect(actual.constructor).toEqual(1); + expect(actual.hasOwnProperty).toEqual(2); + }); + + it('should work with a number for `iteratee`', () => { + const array = [ + [1, 'a'], + [2, 'a'], + [2, 'b'], + ]; + + expect(countBy(array, 0)).toEqual({ 1: 1, 2: 2 }); + expect(countBy(array, 1)).toEqual({ a: 2, b: 1 }); + }); + + it('should work with an object for `collection`', () => { + const actual = countBy({ a: 6.1, b: 4.2, c: 6.3 }, Math.floor); + expect(actual).toEqual({ 4: 1, 6: 2 }); + }); +}); diff --git a/test/create.spec.js b/test/create.spec.js new file mode 100644 index 0000000000..157907e114 --- /dev/null +++ b/test/create.spec.js @@ -0,0 +1,99 @@ +import lodashStable from 'lodash'; +import { falsey, primitives, stubTrue } from './utils'; +import create from '../src/create'; +import keys from '../src/keys'; + +describe('create', () => { + function Shape() { + this.x = 0; + this.y = 0; + } + + function Circle() { + Shape.call(this); + } + + it('should create an object that inherits from the given `prototype` object', () => { + Circle.prototype = create(Shape.prototype); + Circle.prototype.constructor = Circle; + + const actual = new Circle(); + + expect(actual instanceof Circle); + expect(actual instanceof Shape); + assert.notStrictEqual(Circle.prototype, Shape.prototype); + }); + + it('should assign `properties` to the created object', () => { + const expected = { constructor: Circle, radius: 0 }; + const properties = Object.keys(expected); + Circle.prototype = create(Shape.prototype, expected); + + const actual = new Circle(); + + expect(actual instanceof Circle); + expect(actual instanceof Shape); + expect(Object.keys(Circle.prototype)).toEqual(properties); + properties.forEach((property) => { + expect(Circle.prototype[property]).toBe(expected[property]); + }); + }); + + it('should assign own properties', () => { + function Foo() { + this.a = 1; + this.c = 3; + } + Foo.prototype.b = 2; + + const actual = create({}, new Foo()); + const expected = { a: 1, c: 3 }; + const properties = Object.keys(expected); + + expect(Object.keys(actual)).toEqual(properties); + properties.forEach((property) => { + expect(actual[property]).toBe(expected[property]); + }); + }); + + it('should assign properties that shadow those of `prototype`', () => { + function Foo() { + this.a = 1; + } + const object = create(new Foo(), { a: 1 }); + expect(lodashStable.keys(object)).toEqual(['a']); + }); + + it('should accept a falsey `prototype`', () => { + const actual = lodashStable.map(falsey, (prototype, index) => + index ? create(prototype) : create(), + ); + + actual.forEach((value) => { + expect(lodashStable.isObject(value)); + }); + }); + + it('should accept a primitive `prototype`', () => { + const actual = lodashStable.map(primitives, (value, index) => + index ? create(value) : create(), + ); + + actual.forEach((value) => { + expect(lodashStable.isObject(value)); + }); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [{ a: 1 }, { a: 1 }, { a: 1 }]; + const expected = lodashStable.map(array, stubTrue); + const objects = lodashStable.map(array, create); + + const actual = lodashStable.map( + objects, + (object) => object.a === 1 && !keys(object).length, + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/create.test.js b/test/create.test.js deleted file mode 100644 index fef0be6de7..0000000000 --- a/test/create.test.js +++ /dev/null @@ -1,99 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, primitives, stubTrue } from './utils.js'; -import create from '../create.js'; -import keys from '../keys.js'; - -describe('create', function() { - function Shape() { - this.x = 0; - this.y = 0; - } - - function Circle() { - Shape.call(this); - } - - it('should create an object that inherits from the given `prototype` object', function() { - Circle.prototype = create(Shape.prototype); - Circle.prototype.constructor = Circle; - - var actual = new Circle; - - assert.ok(actual instanceof Circle); - assert.ok(actual instanceof Shape); - assert.notStrictEqual(Circle.prototype, Shape.prototype); - }); - - it('should assign `properties` to the created object', function() { - var expected = { 'constructor': Circle, 'radius': 0 }; - var properties = Object.keys(expected); - Circle.prototype = create(Shape.prototype, expected); - - var actual = new Circle; - - assert.ok(actual instanceof Circle); - assert.ok(actual instanceof Shape); - assert.deepStrictEqual(Object.keys(Circle.prototype), properties); - properties.forEach((property) => { - assert.strictEqual(Circle.prototype[property], expected[property]); - }); - }); - - it('should assign own properties', function() { - function Foo() { - this.a = 1; - this.c = 3; - } - Foo.prototype.b = 2; - - var actual = create({}, new Foo); - var expected = { 'a': 1, 'c': 3 }; - var properties = Object.keys(expected); - - assert.deepStrictEqual(Object.keys(actual), properties); - properties.forEach((property) => { - assert.strictEqual(actual[property], expected[property]); - }); - }); - - it('should assign properties that shadow those of `prototype`', function() { - function Foo() { - this.a = 1; - } - var object = create(new Foo, { 'a': 1 }); - assert.deepStrictEqual(lodashStable.keys(object), ['a']); - }); - - it('should accept a falsey `prototype`', function() { - var actual = lodashStable.map(falsey, function(prototype, index) { - return index ? create(prototype) : create(); - }); - - actual.forEach((value) => { - assert.ok(lodashStable.isObject(value)); - }); - }); - - it('should accept a primitive `prototype`', function() { - var actual = lodashStable.map(primitives, function(value, index) { - return index ? create(value) : create(); - }); - - actual.forEach((value) => { - assert.ok(lodashStable.isObject(value)); - }); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [{ 'a': 1 }, { 'a': 1 }, { 'a': 1 }], - expected = lodashStable.map(array, stubTrue), - objects = lodashStable.map(array, create); - - var actual = lodashStable.map(objects, function(object) { - return object.a === 1 && !keys(object).length; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/curry-methods.js b/test/curry-methods.js deleted file mode 100644 index e742e03154..0000000000 --- a/test/curry-methods.js +++ /dev/null @@ -1,52 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, slice } from './utils.js'; -import curry from '../curry.js'; - -describe('curry methods', function() { - lodashStable.each(['curry', 'curryRight'], function(methodName) { - var func = _[methodName], - fn = function(a, b) { return slice.call(arguments); }, - isCurry = methodName == 'curry'; - - it('`_.' + methodName + '` should not error on functions with the same name as lodash methods', function() { - function run(a, b) { - return a + b; - } - - var curried = func(run); - - try { - var actual = curried(1)(2); - } catch (e) {} - - assert.strictEqual(actual, 3); - }); - - it('`_.' + methodName + '` should work for function names that shadow those on `Object.prototype`', function() { - var curried = curry(function hasOwnProperty(a, b, c) { - return [a, b, c]; - }); - - var expected = [1, 2, 3]; - - assert.deepStrictEqual(curried(1)(2)(3), expected); - }); - - it('`_.' + methodName + '` should work as an iteratee for methods like `_.map`', function() { - var array = [fn, fn, fn], - object = { 'a': fn, 'b': fn, 'c': fn }; - - lodashStable.each([array, object], function(collection) { - var curries = lodashStable.map(collection, func), - expected = lodashStable.map(collection, lodashStable.constant(isCurry ? ['a', 'b'] : ['b', 'a'])); - - var actual = lodashStable.map(curries, function(curried) { - return curried('a')('b'); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - }); -}); diff --git a/test/curry-methods.spec.js b/test/curry-methods.spec.js new file mode 100644 index 0000000000..757f6bfb4d --- /dev/null +++ b/test/curry-methods.spec.js @@ -0,0 +1,52 @@ +import lodashStable from 'lodash'; +import { _, slice } from './utils'; +import curry from '../src/curry'; + +describe('curry methods', () => { + lodashStable.each(['curry', 'curryRight'], (methodName) => { + const func = _[methodName]; + const fn = function (a, b) { + return slice.call(arguments); + }; + const isCurry = methodName === 'curry'; + + it(`\`_.${methodName}\` should not error on functions with the same name as lodash methods`, () => { + function run(a, b) { + return a + b; + } + + const curried = func(run); + + try { + var actual = curried(1)(2); + } catch (e) {} + + expect(actual).toBe(3); + }); + + it(`\`_.${methodName}\` should work for function names that shadow those on \`Object.prototype\``, () => { + const curried = curry((a, b, c) => [a, b, c]); + + const expected = [1, 2, 3]; + + expect(curried(1)(2)(3)).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work as an iteratee for methods like \`_.map\``, () => { + const array = [fn, fn, fn]; + const object = { a: fn, b: fn, c: fn }; + + lodashStable.each([array, object], (collection) => { + const curries = lodashStable.map(collection, func); + const expected = lodashStable.map( + collection, + lodashStable.constant(isCurry ? ['a', 'b'] : ['b', 'a']), + ); + + const actual = lodashStable.map(curries, (curried) => curried('a')('b')); + + expect(actual).toEqual(expected); + }); + }); + }); +}); diff --git a/test/curry.js b/test/curry.js deleted file mode 100644 index 5b8ab73644..0000000000 --- a/test/curry.js +++ /dev/null @@ -1,135 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, stubArray } from './utils.js'; -import curry from '../curry.js'; -import placeholder from '../placeholder.js'; -import bind from '../bind.js'; -import partial from '../partial.js'; -import partialRight from '../partialRight.js'; - -describe('curry', function() { - function fn(a, b, c, d) { - return slice.call(arguments); - } - - it('should curry based on the number of arguments given', function() { - var curried = curry(fn), - expected = [1, 2, 3, 4]; - - assert.deepStrictEqual(curried(1)(2)(3)(4), expected); - assert.deepStrictEqual(curried(1, 2)(3, 4), expected); - assert.deepStrictEqual(curried(1, 2, 3, 4), expected); - }); - - it('should allow specifying `arity`', function() { - var curried = curry(fn, 3), - expected = [1, 2, 3]; - - assert.deepStrictEqual(curried(1)(2, 3), expected); - assert.deepStrictEqual(curried(1, 2)(3), expected); - assert.deepStrictEqual(curried(1, 2, 3), expected); - }); - - it('should coerce `arity` to an integer', function() { - var values = ['0', 0.6, 'xyz'], - expected = lodashStable.map(values, stubArray); - - var actual = lodashStable.map(values, function(arity) { - return curry(fn, arity)(); - }); - - assert.deepStrictEqual(actual, expected); - assert.deepStrictEqual(curry(fn, '2')(1)(2), [1, 2]); - }); - - it('should support placeholders', function() { - var curried = curry(fn), - ph = curried.placeholder; - - assert.deepStrictEqual(curried(1)(ph, 3)(ph, 4)(2), [1, 2, 3, 4]); - assert.deepStrictEqual(curried(ph, 2)(1)(ph, 4)(3), [1, 2, 3, 4]); - assert.deepStrictEqual(curried(ph, ph, 3)(ph, 2)(ph, 4)(1), [1, 2, 3, 4]); - assert.deepStrictEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), [1, 2, 3, 4]); - }); - - it('should persist placeholders', function() { - var curried = curry(fn), - ph = curried.placeholder, - actual = curried(ph, ph, ph, 'd')('a')(ph)('b')('c'); - - assert.deepStrictEqual(actual, ['a', 'b', 'c', 'd']); - }); - - it('should use `_.placeholder` when set', function() { - var curried = curry(fn), - _ph = placeholder = {}, - ph = curried.placeholder; - - assert.deepEqual(curried(1)(_ph, 3)(ph, 4), [1, ph, 3, 4]); - delete placeholder; - }); - - it('should provide additional arguments after reaching the target arity', function() { - var curried = curry(fn, 3); - assert.deepStrictEqual(curried(1)(2, 3, 4), [1, 2, 3, 4]); - assert.deepStrictEqual(curried(1, 2)(3, 4, 5), [1, 2, 3, 4, 5]); - assert.deepStrictEqual(curried(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5, 6]); - }); - - it('should create a function with a `length` of `0`', function() { - lodashStable.times(2, function(index) { - var curried = index ? curry(fn, 4) : curry(fn); - assert.strictEqual(curried.length, 0); - assert.strictEqual(curried(1).length, 0); - assert.strictEqual(curried(1, 2).length, 0); - }); - }); - - it('should ensure `new curried` is an instance of `func`', function() { - function Foo(value) { - return value && object; - } - - var curried = curry(Foo), - object = {}; - - assert.ok(new curried(false) instanceof Foo); - assert.strictEqual(new curried(true), object); - }); - - it('should use `this` binding of function', function() { - var fn = function(a, b, c) { - var value = this || {}; - return [value[a], value[b], value[c]]; - }; - - var object = { 'a': 1, 'b': 2, 'c': 3 }, - expected = [1, 2, 3]; - - assert.deepStrictEqual(curry(bind(fn, object), 3)('a')('b')('c'), expected); - assert.deepStrictEqual(curry(bind(fn, object), 3)('a', 'b')('c'), expected); - assert.deepStrictEqual(curry(bind(fn, object), 3)('a', 'b', 'c'), expected); - - assert.deepStrictEqual(bind(curry(fn), object)('a')('b')('c'), Array(3)); - assert.deepStrictEqual(bind(curry(fn), object)('a', 'b')('c'), Array(3)); - assert.deepStrictEqual(bind(curry(fn), object)('a', 'b', 'c'), expected); - - object.curried = curry(fn); - assert.deepStrictEqual(object.curried('a')('b')('c'), Array(3)); - assert.deepStrictEqual(object.curried('a', 'b')('c'), Array(3)); - assert.deepStrictEqual(object.curried('a', 'b', 'c'), expected); - }); - - it('should work with partialed methods', function() { - var curried = curry(fn), - expected = [1, 2, 3, 4]; - - var a = partial(curried, 1), - b = bind(a, null, 2), - c = partialRight(b, 4), - d = partialRight(b(3), 4); - - assert.deepStrictEqual(c(3), expected); - assert.deepStrictEqual(d(), expected); - }); -}); diff --git a/test/curry.spec.js b/test/curry.spec.js new file mode 100644 index 0000000000..2901039812 --- /dev/null +++ b/test/curry.spec.js @@ -0,0 +1,132 @@ +import lodashStable from 'lodash'; +import { slice, stubArray } from './utils'; +import curry from '../src/curry'; +import placeholder from '../src/placeholder'; +import bind from '../src/bind'; +import partial from '../src/partial'; +import partialRight from '../src/partialRight'; + +describe('curry', () => { + function fn(a, b, c, d) { + return slice.call(arguments); + } + + it('should curry based on the number of arguments given', () => { + const curried = curry(fn), + expected = [1, 2, 3, 4]; + + expect(curried(1)(2)(3)(4)).toEqual(expected); + expect(curried(1, 2)(3, 4)).toEqual(expected); + expect(curried(1, 2, 3, 4)).toEqual(expected); + }); + + it('should allow specifying `arity`', () => { + const curried = curry(fn, 3), + expected = [1, 2, 3]; + + expect(curried(1)(2, 3)).toEqual(expected); + expect(curried(1, 2)(3)).toEqual(expected); + expect(curried(1, 2, 3)).toEqual(expected); + }); + + it('should coerce `arity` to an integer', () => { + const values = ['0', 0.6, 'xyz'], + expected = lodashStable.map(values, stubArray); + + const actual = lodashStable.map(values, (arity) => curry(fn, arity)()); + + expect(actual).toEqual(expected); + expect(curry(fn, '2')(1)(2), [1).toEqual(2]); + }); + + it('should support placeholders', () => { + const curried = curry(fn), + ph = curried.placeholder; + + expect(curried(1)(ph, 3)(ph, 4)(2), [1, 2, 3).toEqual(4]); + expect(curried(ph, 2)(1)(ph, 4)(3), [1, 2, 3).toEqual(4]); + expect(curried(ph, ph, 3)(ph, 2)(ph, 4)(1), [1, 2, 3).toEqual(4]); + expect(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), [1, 2, 3).toEqual(4]); + }); + + it('should persist placeholders', () => { + const curried = curry(fn), + ph = curried.placeholder, + actual = curried(ph, ph, ph, 'd')('a')(ph)('b')('c'); + + expect(actual, ['a', 'b', 'c').toEqual('d']); + }); + + it('should use `_.placeholder` when set', () => { + const curried = curry(fn), + _ph = (placeholder = {}), + ph = curried.placeholder; + + expect(curried(1)(_ph, 3)(ph, 4), [1, ph, 3).toEqual(4]); + delete placeholder; + }); + + it('should provide additional arguments after reaching the target arity', () => { + const curried = curry(fn, 3); + expect(curried(1)(2, 3, 4), [1, 2, 3).toEqual(4]); + expect(curried(1, 2)(3, 4, 5), [1, 2, 3, 4).toEqual(5]); + expect(curried(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5).toEqual(6]); + }); + + it('should create a function with a `length` of `0`', () => { + lodashStable.times(2, (index) => { + const curried = index ? curry(fn, 4) : curry(fn); + expect(curried.length).toBe(0); + expect(curried(1).length).toBe(0); + expect(curried(1, 2).length).toBe(0); + }); + }); + + it('should ensure `new curried` is an instance of `func`', () => { + function Foo(value) { + return value && object; + } + + var curried = curry(Foo), + object = {}; + + expect(new curried(false) instanceof Foo) + expect(new curried(true)).toBe(object); + }); + + it('should use `this` binding of function', () => { + const fn = function (a, b, c) { + const value = this || {}; + return [value[a], value[b], value[c]]; + }; + + const object = { a: 1, b: 2, c: 3 }, + expected = [1, 2, 3]; + + expect(curry(bind(fn, object), 3)('a')('b')('c')).toEqual(expected); + expect(curry(bind(fn, object), 3)('a', 'b')('c')).toEqual(expected); + expect(curry(bind(fn, object), 3)('a', 'b', 'c')).toEqual(expected); + + expect(bind(curry(fn), object)('a')('b')('c')).toEqual(Array(3)); + expect(bind(curry(fn), object)('a', 'b')('c')).toEqual(Array(3)); + expect(bind(curry(fn), object)('a', 'b', 'c')).toEqual(expected); + + object.curried = curry(fn); + expect(object.curried('a')('b')('c')).toEqual(Array(3)); + expect(object.curried('a', 'b')('c')).toEqual(Array(3)); + expect(object.curried('a', 'b', 'c')).toEqual(expected); + }); + + it('should work with partialed methods', () => { + const curried = curry(fn), + expected = [1, 2, 3, 4]; + + const a = partial(curried, 1), + b = bind(a, null, 2), + c = partialRight(b, 4), + d = partialRight(b(3), 4); + + expect(c(3)).toEqual(expected); + expect(d()).toEqual(expected); + }); +}); diff --git a/test/curryRight.js b/test/curryRight.js deleted file mode 100644 index 21f6acdb7f..0000000000 --- a/test/curryRight.js +++ /dev/null @@ -1,136 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, stubArray } from './utils.js'; -import curryRight from '../curryRight.js'; -import placeholder from '../placeholder.js'; -import bind from '../bind.js'; -import partialRight from '../partialRight.js'; -import partial from '../partial.js'; - -describe('curryRight', function() { - function fn(a, b, c, d) { - return slice.call(arguments); - } - - it('should curry based on the number of arguments given', function() { - var curried = curryRight(fn), - expected = [1, 2, 3, 4]; - - assert.deepStrictEqual(curried(4)(3)(2)(1), expected); - assert.deepStrictEqual(curried(3, 4)(1, 2), expected); - assert.deepStrictEqual(curried(1, 2, 3, 4), expected); - }); - - it('should allow specifying `arity`', function() { - var curried = curryRight(fn, 3), - expected = [1, 2, 3]; - - assert.deepStrictEqual(curried(3)(1, 2), expected); - assert.deepStrictEqual(curried(2, 3)(1), expected); - assert.deepStrictEqual(curried(1, 2, 3), expected); - }); - - it('should coerce `arity` to an integer', function() { - var values = ['0', 0.6, 'xyz'], - expected = lodashStable.map(values, stubArray); - - var actual = lodashStable.map(values, function(arity) { - return curryRight(fn, arity)(); - }); - - assert.deepStrictEqual(actual, expected); - assert.deepStrictEqual(curryRight(fn, '2')(1)(2), [2, 1]); - }); - - it('should support placeholders', function() { - var curried = curryRight(fn), - expected = [1, 2, 3, 4], - ph = curried.placeholder; - - assert.deepStrictEqual(curried(4)(2, ph)(1, ph)(3), expected); - assert.deepStrictEqual(curried(3, ph)(4)(1, ph)(2), expected); - assert.deepStrictEqual(curried(ph, ph, 4)(ph, 3)(ph, 2)(1), expected); - assert.deepStrictEqual(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1), expected); - }); - - it('should persist placeholders', function() { - var curried = curryRight(fn), - ph = curried.placeholder, - actual = curried('a', ph, ph, ph)('b')(ph)('c')('d'); - - assert.deepStrictEqual(actual, ['a', 'b', 'c', 'd']); - }); - - it('should use `_.placeholder` when set', function() { - var curried = curryRight(fn), - _ph = placeholder = {}, - ph = curried.placeholder; - - assert.deepEqual(curried(4)(2, _ph)(1, ph), [1, 2, ph, 4]); - delete placeholder; - }); - - it('should provide additional arguments after reaching the target arity', function() { - var curried = curryRight(fn, 3); - assert.deepStrictEqual(curried(4)(1, 2, 3), [1, 2, 3, 4]); - assert.deepStrictEqual(curried(4, 5)(1, 2, 3), [1, 2, 3, 4, 5]); - assert.deepStrictEqual(curried(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5, 6]); - }); - - it('should create a function with a `length` of `0`', function() { - lodashStable.times(2, function(index) { - var curried = index ? curryRight(fn, 4) : curryRight(fn); - assert.strictEqual(curried.length, 0); - assert.strictEqual(curried(4).length, 0); - assert.strictEqual(curried(3, 4).length, 0); - }); - }); - - it('should ensure `new curried` is an instance of `func`', function() { - function Foo(value) { - return value && object; - } - - var curried = curryRight(Foo), - object = {}; - - assert.ok(new curried(false) instanceof Foo); - assert.strictEqual(new curried(true), object); - }); - - it('should use `this` binding of function', function() { - var fn = function(a, b, c) { - var value = this || {}; - return [value[a], value[b], value[c]]; - }; - - var object = { 'a': 1, 'b': 2, 'c': 3 }, - expected = [1, 2, 3]; - - assert.deepStrictEqual(curryRight(bind(fn, object), 3)('c')('b')('a'), expected); - assert.deepStrictEqual(curryRight(bind(fn, object), 3)('b', 'c')('a'), expected); - assert.deepStrictEqual(curryRight(bind(fn, object), 3)('a', 'b', 'c'), expected); - - assert.deepStrictEqual(bind(curryRight(fn), object)('c')('b')('a'), Array(3)); - assert.deepStrictEqual(bind(curryRight(fn), object)('b', 'c')('a'), Array(3)); - assert.deepStrictEqual(bind(curryRight(fn), object)('a', 'b', 'c'), expected); - - object.curried = curryRight(fn); - assert.deepStrictEqual(object.curried('c')('b')('a'), Array(3)); - assert.deepStrictEqual(object.curried('b', 'c')('a'), Array(3)); - assert.deepStrictEqual(object.curried('a', 'b', 'c'), expected); - }); - - it('should work with partialed methods', function() { - var curried = curryRight(fn), - expected = [1, 2, 3, 4]; - - var a = partialRight(curried, 4), - b = partialRight(a, 3), - c = bind(b, null, 1), - d = partial(b(2), 1); - - assert.deepStrictEqual(c(2), expected); - assert.deepStrictEqual(d(), expected); - }); -}); diff --git a/test/curryRight.spec.js b/test/curryRight.spec.js new file mode 100644 index 0000000000..977c97fcce --- /dev/null +++ b/test/curryRight.spec.js @@ -0,0 +1,133 @@ +import lodashStable from 'lodash'; +import { slice, stubArray } from './utils'; +import curryRight from '../src/curryRight'; +import placeholder from '../src/placeholder'; +import bind from '../src/bind'; +import partialRight from '../src/partialRight'; +import partial from '../src/partial'; + +describe('curryRight', () => { + function fn(a, b, c, d) { + return slice.call(arguments); + } + + it('should curry based on the number of arguments given', () => { + const curried = curryRight(fn), + expected = [1, 2, 3, 4]; + + expect(curried(4)(3)(2)(1)).toEqual(expected); + expect(curried(3, 4)(1, 2)).toEqual(expected); + expect(curried(1, 2, 3, 4)).toEqual(expected); + }); + + it('should allow specifying `arity`', () => { + const curried = curryRight(fn, 3), + expected = [1, 2, 3]; + + expect(curried(3)(1, 2)).toEqual(expected); + expect(curried(2, 3)(1)).toEqual(expected); + expect(curried(1, 2, 3)).toEqual(expected); + }); + + it('should coerce `arity` to an integer', () => { + const values = ['0', 0.6, 'xyz'], + expected = lodashStable.map(values, stubArray); + + const actual = lodashStable.map(values, (arity) => curryRight(fn, arity)()); + + expect(actual).toEqual(expected); + expect(curryRight(fn, '2')(1)(2), [2).toEqual(1]); + }); + + it('should support placeholders', () => { + const curried = curryRight(fn), + expected = [1, 2, 3, 4], + ph = curried.placeholder; + + expect(curried(4)(2, ph)(1, ph)(3)).toEqual(expected); + expect(curried(3, ph)(4)(1, ph)(2)).toEqual(expected); + expect(curried(ph, ph, 4)(ph, 3)(ph, 2)(1)).toEqual(expected); + expect(curried(ph, ph, ph, 4)(ph, ph, 3)(ph, 2)(1)).toEqual(expected); + }); + + it('should persist placeholders', () => { + const curried = curryRight(fn), + ph = curried.placeholder, + actual = curried('a', ph, ph, ph)('b')(ph)('c')('d'); + + expect(actual, ['a', 'b', 'c').toEqual('d']); + }); + + it('should use `_.placeholder` when set', () => { + const curried = curryRight(fn), + _ph = (placeholder = {}), + ph = curried.placeholder; + + expect(curried(4)(2, _ph)(1, ph), [1, 2, ph).toEqual(4]); + delete placeholder; + }); + + it('should provide additional arguments after reaching the target arity', () => { + const curried = curryRight(fn, 3); + expect(curried(4)(1, 2, 3), [1, 2, 3).toEqual(4]); + expect(curried(4, 5)(1, 2, 3), [1, 2, 3, 4).toEqual(5]); + expect(curried(1, 2, 3, 4, 5, 6), [1, 2, 3, 4, 5).toEqual(6]); + }); + + it('should create a function with a `length` of `0`', () => { + lodashStable.times(2, (index) => { + const curried = index ? curryRight(fn, 4) : curryRight(fn); + expect(curried.length).toBe(0); + expect(curried(4).length).toBe(0); + expect(curried(3, 4).length).toBe(0); + }); + }); + + it('should ensure `new curried` is an instance of `func`', () => { + function Foo(value) { + return value && object; + } + + var curried = curryRight(Foo), + object = {}; + + expect(new curried(false) instanceof Foo) + expect(new curried(true)).toBe(object); + }); + + it('should use `this` binding of function', () => { + const fn = function (a, b, c) { + const value = this || {}; + return [value[a], value[b], value[c]]; + }; + + const object = { a: 1, b: 2, c: 3 }, + expected = [1, 2, 3]; + + expect(curryRight(bind(fn, object), 3)('c')('b')('a')).toEqual(expected); + expect(curryRight(bind(fn, object), 3)('b', 'c')('a')).toEqual(expected); + expect(curryRight(bind(fn, object), 3)('a', 'b', 'c')).toEqual(expected); + + expect(bind(curryRight(fn), object)('c')('b')('a')).toEqual(Array(3)); + expect(bind(curryRight(fn), object)('b', 'c')('a')).toEqual(Array(3)); + expect(bind(curryRight(fn), object)('a', 'b', 'c')).toEqual(expected); + + object.curried = curryRight(fn); + expect(object.curried('c')('b')('a')).toEqual(Array(3)); + expect(object.curried('b', 'c')('a')).toEqual(Array(3)); + expect(object.curried('a', 'b', 'c')).toEqual(expected); + }); + + it('should work with partialed methods', () => { + const curried = curryRight(fn), + expected = [1, 2, 3, 4]; + + const a = partialRight(curried, 4), + b = partialRight(a, 3), + c = bind(b, null, 1), + d = partial(b(2), 1); + + expect(c(2)).toEqual(expected); + expect(d()).toEqual(expected); + }); +}); diff --git a/test/custom-_.iteratee-methods.js b/test/custom-_.iteratee-methods.js deleted file mode 100644 index 0571d77ab8..0000000000 --- a/test/custom-_.iteratee-methods.js +++ /dev/null @@ -1,270 +0,0 @@ -import assert from 'assert'; -import partial from '../partial.js'; -import property from '../property.js'; -import iteratee from '../iteratee.js'; - -describe('custom `_.iteratee` methods', function() { - var array = ['one', 'two', 'three'], - getPropA = partial(property, 'a'), - getPropB = partial(property, 'b'), - getLength = partial(property, 'length'), - iteratee = iteratee; - - var getSum = function() { - return function(result, object) { - return result + object.a; - }; - }; - - var objects = [ - { 'a': 0, 'b': 0 }, - { 'a': 1, 'b': 0 }, - { 'a': 1, 'b': 1 } - ]; - - it('`_.countBy` should use `_.iteratee` internally', function() { - iteratee = getLength; - assert.deepEqual(_.countBy(array), { '3': 2, '5': 1 }); - iteratee = iteratee; - }); - - it('`_.differenceBy` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.deepEqual(_.differenceBy(objects, [objects[1]]), [objects[0]]); - iteratee = iteratee; - }); - - it('`_.dropRightWhile` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.dropRightWhile(objects), objects.slice(0, 2)); - iteratee = iteratee; - }); - - it('`_.dropWhile` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.dropWhile(objects.reverse()).reverse(), objects.reverse().slice(0, 2)); - iteratee = iteratee; - }); - - it('`_.every` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.strictEqual(_.every(objects.slice(1)), true); - iteratee = iteratee; - }); - - it('`_.filter` should use `_.iteratee` internally', function() { - var objects = [{ 'a': 0 }, { 'a': 1 }]; - - iteratee = getPropA; - assert.deepEqual(_.filter(objects), [objects[1]]); - iteratee = iteratee; - }); - - it('`_.find` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.strictEqual(_.find(objects), objects[1]); - iteratee = iteratee; - }); - - it('`_.findIndex` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.strictEqual(_.findIndex(objects), 1); - iteratee = iteratee; - }); - - it('`_.findLast` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.strictEqual(_.findLast(objects), objects[2]); - iteratee = iteratee; - }); - - it('`_.findLastIndex` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.strictEqual(_.findLastIndex(objects), 2); - iteratee = iteratee; - }); - - it('`_.findKey` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.strictEqual(_.findKey(objects), '2'); - iteratee = iteratee; - }); - - it('`_.findLastKey` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.strictEqual(_.findLastKey(objects), '2'); - iteratee = iteratee; - }); - - it('`_.groupBy` should use `_.iteratee` internally', function() { - iteratee = getLength; - assert.deepEqual(_.groupBy(array), { '3': ['one', 'two'], '5': ['three'] }); - iteratee = iteratee; - }); - - it('`_.intersectionBy` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.deepEqual(_.intersectionBy(objects, [objects[2]]), [objects[1]]); - iteratee = iteratee; - }); - - it('`_.keyBy` should use `_.iteratee` internally', function() { - iteratee = getLength; - assert.deepEqual(_.keyBy(array), { '3': 'two', '5': 'three' }); - iteratee = iteratee; - }); - - it('`_.map` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.deepEqual(_.map(objects), [0, 1, 1]); - iteratee = iteratee; - }); - - it('`_.mapKeys` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.mapKeys({ 'a': { 'b': 2 } }), { '2': { 'b': 2 } }); - iteratee = iteratee; - }); - - it('`_.mapValues` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.mapValues({ 'a': { 'b': 2 } }), { 'a': 2 }); - iteratee = iteratee; - }); - - it('`_.maxBy` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.maxBy(objects), objects[2]); - iteratee = iteratee; - }); - - it('`_.meanBy` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.strictEqual(_.meanBy(objects), 2 / 3); - iteratee = iteratee; - }); - - it('`_.minBy` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.minBy(objects), objects[0]); - iteratee = iteratee; - }); - - it('`_.partition` should use `_.iteratee` internally', function() { - var objects = [{ 'a': 1 }, { 'a': 1 }, { 'b': 2 }]; - - iteratee = getPropA; - assert.deepEqual(_.partition(objects), [objects.slice(0, 2), objects.slice(2)]); - iteratee = iteratee; - }); - - it('`_.pullAllBy` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.deepEqual(_.pullAllBy(objects.slice(), [{ 'a': 1, 'b': 0 }]), [objects[0]]); - iteratee = iteratee; - }); - - it('`_.reduce` should use `_.iteratee` internally', function() { - iteratee = getSum; - assert.strictEqual(_.reduce(objects, undefined, 0), 2); - iteratee = iteratee; - }); - - it('`_.reduceRight` should use `_.iteratee` internally', function() { - iteratee = getSum; - assert.strictEqual(_.reduceRight(objects, undefined, 0), 2); - iteratee = iteratee; - }); - - it('`_.reject` should use `_.iteratee` internally', function() { - var objects = [{ 'a': 0 }, { 'a': 1 }]; - - iteratee = getPropA; - assert.deepEqual(_.reject(objects), [objects[0]]); - iteratee = iteratee; - }); - - it('`_.remove` should use `_.iteratee` internally', function() { - var objects = [{ 'a': 0 }, { 'a': 1 }]; - - iteratee = getPropA; - _.remove(objects); - assert.deepEqual(objects, [{ 'a': 0 }]); - iteratee = iteratee; - }); - - it('`_.some` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.strictEqual(_.some(objects), true); - iteratee = iteratee; - }); - - it('`_.sortBy` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.deepEqual(_.sortBy(objects.slice().reverse()), [objects[0], objects[2], objects[1]]); - iteratee = iteratee; - }); - - it('`_.sortedIndexBy` should use `_.iteratee` internally', function() { - var objects = [{ 'a': 30 }, { 'a': 50 }]; - - iteratee = getPropA; - assert.strictEqual(_.sortedIndexBy(objects, { 'a': 40 }), 1); - iteratee = iteratee; - }); - - it('`_.sortedLastIndexBy` should use `_.iteratee` internally', function() { - var objects = [{ 'a': 30 }, { 'a': 50 }]; - - iteratee = getPropA; - assert.strictEqual(_.sortedLastIndexBy(objects, { 'a': 40 }), 1); - iteratee = iteratee; - }); - - it('`_.sumBy` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.strictEqual(_.sumBy(objects), 1); - iteratee = iteratee; - }); - - it('`_.takeRightWhile` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.takeRightWhile(objects), objects.slice(2)); - iteratee = iteratee; - }); - - it('`_.takeWhile` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.takeWhile(objects.reverse()), objects.reverse().slice(2)); - iteratee = iteratee; - }); - - it('`_.transform` should use `_.iteratee` internally', function() { - iteratee = function() { - return function(result, object) { - result.sum += object.a; - }; - }; - - assert.deepEqual(_.transform(objects, undefined, { 'sum': 0 }), { 'sum': 2 }); - iteratee = iteratee; - }); - - it('`_.uniqBy` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.uniqBy(objects), [objects[0], objects[2]]); - iteratee = iteratee; - }); - - it('`_.unionBy` should use `_.iteratee` internally', function() { - iteratee = getPropB; - assert.deepEqual(_.unionBy(objects.slice(0, 1), [objects[2]]), [objects[0], objects[2]]); - iteratee = iteratee; - }); - - it('`_.xorBy` should use `_.iteratee` internally', function() { - iteratee = getPropA; - assert.deepEqual(_.xorBy(objects, objects.slice(1)), [objects[0]]); - iteratee = iteratee; - }); -}); diff --git a/test/custom-_.iteratee-methods.spec.js b/test/custom-_.iteratee-methods.spec.js new file mode 100644 index 0000000000..3ae12e6404 --- /dev/null +++ b/test/custom-_.iteratee-methods.spec.js @@ -0,0 +1,269 @@ +import partial from '../src/partial'; +import property from '../src/property'; +import iteratee from '../src/iteratee'; + +describe('custom `_.iteratee` methods', () => { + const array = ['one', 'two', 'three']; + const getPropA = partial(property, 'a'); + const getPropB = partial(property, 'b'); + const getLength = partial(property, 'length'); + var iteratee = iteratee; + + const getSum = function () { + return function (result, object) { + return result + object.a; + }; + }; + + const objects = [ + { a: 0, b: 0 }, + { a: 1, b: 0 }, + { a: 1, b: 1 }, + ]; + + it('`_.countBy` should use `_.iteratee` internally', () => { + iteratee = getLength; + expect(_.countBy(array), { 3: 2).toEqual(5: 1 }); + iteratee = iteratee; + }); + + it('`_.differenceBy` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.differenceBy(objects, [objects[1]])).toEqual([objects[0]]); + iteratee = iteratee; + }); + + it('`_.dropRightWhile` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.dropRightWhile(objects), objects.slice(0).toEqual(2)); + iteratee = iteratee; + }); + + it('`_.dropWhile` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.dropWhile(objects.reverse()).reverse(), objects.reverse().slice(0).toEqual(2)); + iteratee = iteratee; + }); + + it('`_.every` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.every(objects.slice(1))).toBe(true); + iteratee = iteratee; + }); + + it('`_.filter` should use `_.iteratee` internally', () => { + const objects = [{ a: 0 }, { a: 1 }]; + + iteratee = getPropA; + expect(_.filter(objects)).toEqual([objects[1]]); + iteratee = iteratee; + }); + + it('`_.find` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.find(objects)).toBe(objects[1]); + iteratee = iteratee; + }); + + it('`_.findIndex` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.findIndex(objects)).toBe(1); + iteratee = iteratee; + }); + + it('`_.findLast` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.findLast(objects)).toBe(objects[2]); + iteratee = iteratee; + }); + + it('`_.findLastIndex` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.findLastIndex(objects)).toBe(2); + iteratee = iteratee; + }); + + it('`_.findKey` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.findKey(objects)).toBe('2'); + iteratee = iteratee; + }); + + it('`_.findLastKey` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.findLastKey(objects)).toBe('2'); + iteratee = iteratee; + }); + + it('`_.groupBy` should use `_.iteratee` internally', () => { + iteratee = getLength; + expect(_.groupBy(array), { 3: ['one', 'two']).toEqual(5: ['three'] }); + iteratee = iteratee; + }); + + it('`_.intersectionBy` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.intersectionBy(objects, [objects[2]])).toEqual([objects[1]]); + iteratee = iteratee; + }); + + it('`_.keyBy` should use `_.iteratee` internally', () => { + iteratee = getLength; + expect(_.keyBy(array), { 3: 'two').toEqual(5: 'three' }); + iteratee = iteratee; + }); + + it('`_.map` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.map(objects), [0, 1).toEqual(1]); + iteratee = iteratee; + }); + + it('`_.mapKeys` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.mapKeys({ a: { b: 2 } })).toEqual({ 2: { b: 2 } }); + iteratee = iteratee; + }); + + it('`_.mapValues` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.mapValues({ a: { b: 2 } })).toEqual({ a: 2 }); + iteratee = iteratee; + }); + + it('`_.maxBy` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.maxBy(objects)).toEqual(objects[2]); + iteratee = iteratee; + }); + + it('`_.meanBy` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.meanBy(objects)).toBe(2 / 3); + iteratee = iteratee; + }); + + it('`_.minBy` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.minBy(objects)).toEqual(objects[0]); + iteratee = iteratee; + }); + + it('`_.partition` should use `_.iteratee` internally', () => { + const objects = [{ a: 1 }, { a: 1 }, { b: 2 }]; + + iteratee = getPropA; + expect(_.partition(objects), [objects.slice(0, 2)).toEqual(objects.slice(2)]); + iteratee = iteratee; + }); + + it('`_.pullAllBy` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.pullAllBy(objects.slice(), [{ a: 1, b: 0 }])).toEqual([objects[0]]); + iteratee = iteratee; + }); + + it('`_.reduce` should use `_.iteratee` internally', () => { + iteratee = getSum; + expect(_.reduce(objects, undefined, 0)).toBe(2); + iteratee = iteratee; + }); + + it('`_.reduceRight` should use `_.iteratee` internally', () => { + iteratee = getSum; + expect(_.reduceRight(objects, undefined, 0)).toBe(2); + iteratee = iteratee; + }); + + it('`_.reject` should use `_.iteratee` internally', () => { + const objects = [{ a: 0 }, { a: 1 }]; + + iteratee = getPropA; + expect(_.reject(objects)).toEqual([objects[0]]); + iteratee = iteratee; + }); + + it('`_.remove` should use `_.iteratee` internally', () => { + const objects = [{ a: 0 }, { a: 1 }]; + + iteratee = getPropA; + _.remove(objects); + expect(objects).toEqual([{ a: 0 }]); + iteratee = iteratee; + }); + + it('`_.some` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.some(objects)).toBe(true); + iteratee = iteratee; + }); + + it('`_.sortBy` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.sortBy(objects.slice().reverse()), [objects[0], objects[2]).toEqual(objects[1]]); + iteratee = iteratee; + }); + + it('`_.sortedIndexBy` should use `_.iteratee` internally', () => { + const objects = [{ a: 30 }, { a: 50 }]; + + iteratee = getPropA; + expect(_.sortedIndexBy(objects, { a: 40 })).toBe(1); + iteratee = iteratee; + }); + + it('`_.sortedLastIndexBy` should use `_.iteratee` internally', () => { + const objects = [{ a: 30 }, { a: 50 }]; + + iteratee = getPropA; + expect(_.sortedLastIndexBy(objects, { a: 40 })).toBe(1); + iteratee = iteratee; + }); + + it('`_.sumBy` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.sumBy(objects)).toBe(1); + iteratee = iteratee; + }); + + it('`_.takeRightWhile` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.takeRightWhile(objects)).toEqual(objects.slice(2)); + iteratee = iteratee; + }); + + it('`_.takeWhile` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.takeWhile(objects.reverse())).toEqual(objects.reverse().slice(2)); + iteratee = iteratee; + }); + + it('`_.transform` should use `_.iteratee` internally', () => { + iteratee = function () { + return function (result, object) { + result.sum += object.a; + }; + }; + + expect(_.transform(objects, undefined, { sum: 0 })).toEqual({ sum: 2 }); + iteratee = iteratee; + }); + + it('`_.uniqBy` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.uniqBy(objects), [objects[0]).toEqual(objects[2]]); + iteratee = iteratee; + }); + + it('`_.unionBy` should use `_.iteratee` internally', () => { + iteratee = getPropB; + expect(_.unionBy(objects.slice(0, 1), [objects[2]]), [objects[0]).toEqual(objects[2]]); + iteratee = iteratee; + }); + + it('`_.xorBy` should use `_.iteratee` internally', () => { + iteratee = getPropA; + expect(_.xorBy(objects, objects.slice(1))).toEqual([objects[0]]); + iteratee = iteratee; + }); +}); diff --git a/test/debounce-and-throttle.js b/test/debounce-and-throttle.js deleted file mode 100644 index ece01eaa68..0000000000 --- a/test/debounce-and-throttle.js +++ /dev/null @@ -1,167 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, noop, push, isModularize } from './utils.js'; -import runInContext from '../runInContext.js'; - -describe('debounce and throttle', function() { - lodashStable.each(['debounce', 'throttle'], function(methodName) { - var func = _[methodName], - isDebounce = methodName == 'debounce'; - - it('`_.' + methodName + '` should not error for non-object `options` values', function() { - func(noop, 32, 1); - assert.ok(true); - }); - - it('`_.' + methodName + '` should use a default `wait` of `0`', function(done) { - var callCount = 0, - funced = func(function() { callCount++; }); - - funced(); - - setTimeout(function() { - funced(); - assert.strictEqual(callCount, isDebounce ? 1 : 2); - done(); - }, 32); - }); - - it('`_.' + methodName + '` should invoke `func` with the correct `this` binding', function(done) { - var actual = [], - object = { 'funced': func(function() { actual.push(this); }, 32) }, - expected = lodashStable.times(isDebounce ? 1 : 2, lodashStable.constant(object)); - - object.funced(); - if (!isDebounce) { - object.funced(); - } - setTimeout(function() { - assert.deepStrictEqual(actual, expected); - done(); - }, 64); - }); - - it('`_.' + methodName + '` supports recursive calls', function(done) { - var actual = [], - args = lodashStable.map(['a', 'b', 'c'], function(chr) { return [{}, chr]; }), - expected = args.slice(), - queue = args.slice(); - - var funced = func(function() { - var current = [this]; - push.apply(current, arguments); - actual.push(current); - - var next = queue.shift(); - if (next) { - funced.call(next[0], next[1]); - } - }, 32); - - var next = queue.shift(); - funced.call(next[0], next[1]); - assert.deepStrictEqual(actual, expected.slice(0, isDebounce ? 0 : 1)); - - setTimeout(function() { - assert.deepStrictEqual(actual, expected.slice(0, actual.length)); - done(); - }, 256); - }); - - it('`_.' + methodName + '` should work if the system time is set backwards', function(done) { - if (!isModularize) { - var callCount = 0, - dateCount = 0; - - var lodash = runInContext({ - 'Date': { - 'now': function() { - return ++dateCount == 4 - ? +new Date(2012, 3, 23, 23, 27, 18) - : +new Date; - } - } - }); - - var funced = lodash[methodName](function() { - callCount++; - }, 32); - - funced(); - - setTimeout(function() { - funced(); - assert.strictEqual(callCount, isDebounce ? 1 : 2); - done(); - }, 64); - } - else { - done(); - } - }); - - it('`_.' + methodName + '` should support cancelling delayed calls', function(done) { - var callCount = 0; - - var funced = func(function() { - callCount++; - }, 32, { 'leading': false }); - - funced(); - funced.cancel(); - - setTimeout(function() { - assert.strictEqual(callCount, 0); - done(); - }, 64); - }); - - it('`_.' + methodName + '` should reset `lastCalled` after cancelling', function(done) { - var callCount = 0; - - var funced = func(function() { - return ++callCount; - }, 32, { 'leading': true }); - - assert.strictEqual(funced(), 1); - funced.cancel(); - - assert.strictEqual(funced(), 2); - funced(); - - setTimeout(function() { - assert.strictEqual(callCount, 3); - done(); - }, 64); - }); - - it('`_.' + methodName + '` should support flushing delayed calls', function(done) { - var callCount = 0; - - var funced = func(function() { - return ++callCount; - }, 32, { 'leading': false }); - - funced(); - assert.strictEqual(funced.flush(), 1); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - done(); - }, 64); - }); - - it('`_.' + methodName + '` should noop `cancel` and `flush` when nothing is queued', function(done) { - var callCount = 0, - funced = func(function() { callCount++; }, 32); - - funced.cancel(); - assert.strictEqual(funced.flush(), undefined); - - setTimeout(function() { - assert.strictEqual(callCount, 0); - done(); - }, 64); - }); - }); -}); diff --git a/test/debounce-and-throttle.spec.js b/test/debounce-and-throttle.spec.js new file mode 100644 index 0000000000..1e74077059 --- /dev/null +++ b/test/debounce-and-throttle.spec.js @@ -0,0 +1,172 @@ +import lodashStable, { runInContext } from "lodash"; +import { _, noop, push, isModularize } from './utils'; + +describe('debounce and throttle', () => { + lodashStable.each(['debounce', 'throttle'], (methodName) => { + const func = _[methodName]; + const isDebounce = methodName === 'debounce'; + + it(`\`_.${methodName}\` should not error for non-object \`options\` values`, () => { + func(noop, 32, 1); + expect(true); + }); + + it(`\`_.${methodName}\` should use a default \`wait\` of \`0\``, (done) => { + let callCount = 0; + const funced = func(() => { + callCount++; + }); + + funced(); + + setTimeout(() => { + funced(); + expect(callCount).toBe(isDebounce ? 1 : 2); + done(); + }, 32); + }); + + it(`\`_.${methodName}\` should invoke \`func\` with the correct \`this\` binding`, (done) => { + const actual = []; + const object = { + funced: func(function () { + actual.push(this); + }, 32), + }; + const expected = lodashStable.times(isDebounce ? 1 : 2, lodashStable.constant(object)); + + object.funced(); + if (!isDebounce) { + object.funced(); + } + setTimeout(() => { + expect(actual).toEqual(expected); + done(); + }, 64); + }); + + it(`\`_.${methodName}\` supports recursive calls`, (done) => { + const actual = []; + const args = lodashStable.map(['a', 'b', 'c'], (chr) => [{}, chr]); + const expected = args.slice(); + const queue = args.slice(); + + var funced = func(function () { + const current = [this]; + push.apply(current, arguments); + actual.push(current); + + const next = queue.shift(); + if (next) { + funced.call(next[0], next[1]); + } + }, 32); + + const next = queue.shift(); + funced.call(next[0], next[1]); + expect(actual).toEqual(expected.slice(0, isDebounce ? 0 : 1)); + + setTimeout(() => { + expect(actual).toEqual(expected.slice(0, actual.length)); + done(); + }, 256); + }); + + it(`\`_.${methodName}\` should work if the system time is set backwards`, (done) => { + if (!isModularize) { + let callCount = 0; + let dateCount = 0; + + const lodash = runInContext({ + Date: { + now: function () { + return ++dateCount === 4 + ? +new Date(2012, 3, 23, 23, 27, 18) + : +new Date(); + }, + }, + }); + + const funced = lodash[methodName](() => { + callCount++; + }, 32); + + funced(); + + setTimeout(() => { + funced(); + expect(callCount).toBe(isDebounce ? 1 : 2); + done(); + }, 64); + } else { + done(); + } + }); + + it(`\`_.${methodName}\` should support cancelling delayed calls`, (done) => { + let callCount = 0; + + const funced = func( + () => { + callCount++; + }, + 32, + { leading: false }, + ); + + funced(); + funced.cancel(); + + setTimeout(() => { + expect(callCount).toBe(0); + done(); + }, 64); + }); + + it(`\`_.${methodName}\` should reset \`lastCalled\` after cancelling`, (done) => { + let callCount = 0; + + const funced = func(() => ++callCount, 32, { leading: true }); + + expect(funced()).toBe(1); + funced.cancel(); + + expect(funced()).toBe(2); + funced(); + + setTimeout(() => { + expect(callCount).toBe(3); + done(); + }, 64); + }); + + it(`\`_.${methodName}\` should support flushing delayed calls`, (done) => { + let callCount = 0; + + const funced = func(() => ++callCount, 32, { leading: false }); + + funced(); + expect(funced.flush()).toBe(1); + + setTimeout(() => { + expect(callCount).toBe(1); + done(); + }, 64); + }); + + it(`\`_.${methodName}\` should noop \`cancel\` and \`flush\` when nothing is queued`, (done) => { + let callCount = 0; + const funced = func(() => { + callCount++; + }, 32); + + funced.cancel(); + expect(funced.flush()).toBe(undefined); + + setTimeout(() => { + expect(callCount).toBe(0); + done(); + }, 64); + }); + }); +}); diff --git a/test/debounce.spec.js b/test/debounce.spec.js new file mode 100644 index 0000000000..7db758bfbf --- /dev/null +++ b/test/debounce.spec.js @@ -0,0 +1,293 @@ +import { identity, argv, isPhantom, push } from './utils'; +import debounce from '../src/debounce'; + +describe('debounce', () => { + it('should debounce a function', (done) => { + let callCount = 0; + + const debounced = debounce((value) => { + ++callCount; + return value; + }, 32); + + const results = [debounced('a'), debounced('b'), debounced('c')]; + expect(results, [undefined, undefined).toEqual(undefined]); + expect(callCount).toBe(0); + + setTimeout(() => { + expect(callCount).toBe(1); + + const results = [debounced('d'), debounced('e'), debounced('f')]; + expect(results, ['c', 'c').toEqual('c']); + expect(callCount).toBe(1); + }, 128); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 256); + }); + + it('subsequent debounced calls return the last `func` result', (done) => { + const debounced = debounce(identity, 32); + debounced('a'); + + setTimeout(() => { + assert.notStrictEqual(debounced('b'), 'b'); + }, 64); + + setTimeout(() => { + assert.notStrictEqual(debounced('c'), 'c'); + done(); + }, 128); + }); + + it('should not immediately call `func` when `wait` is `0`', (done) => { + let callCount = 0; + const debounced = debounce(() => { + ++callCount; + }, 0); + + debounced(); + debounced(); + expect(callCount).toBe(0); + + setTimeout(() => { + expect(callCount).toBe(1); + done(); + }, 5); + }); + + it('should apply default options', (done) => { + let callCount = 0; + const debounced = debounce( + () => { + callCount++; + }, + 32, + {}, + ); + + debounced(); + expect(callCount).toBe(0); + + setTimeout(() => { + expect(callCount).toBe(1); + done(); + }, 64); + }); + + it('should support a `leading` option', (done) => { + const callCounts = [0, 0]; + + const withLeading = debounce( + () => { + callCounts[0]++; + }, + 32, + { leading: true }, + ); + + const withLeadingAndTrailing = debounce( + () => { + callCounts[1]++; + }, + 32, + { leading: true }, + ); + + withLeading(); + expect(callCounts[0]).toBe(1); + + withLeadingAndTrailing(); + withLeadingAndTrailing(); + expect(callCounts[1]).toBe(1); + + setTimeout(() => { + expect(callCounts, [1).toEqual(2]); + + withLeading(); + expect(callCounts[0]).toBe(2); + + done(); + }, 64); + }); + + it('subsequent leading debounced calls return the last `func` result', (done) => { + const debounced = debounce(identity, 32, { leading: true, trailing: false }); + const results = [debounced('a'), debounced('b')]; + + expect(results, ['a').toEqual('a']); + + setTimeout(() => { + const results = [debounced('c'), debounced('d')]; + expect(results, ['c').toEqual('c']); + done(); + }, 64); + }); + + it('should support a `trailing` option', (done) => { + let withCount = 0; + let withoutCount = 0; + + const withTrailing = debounce( + () => { + withCount++; + }, + 32, + { trailing: true }, + ); + + const withoutTrailing = debounce( + () => { + withoutCount++; + }, + 32, + { trailing: false }, + ); + + withTrailing(); + expect(withCount).toBe(0); + + withoutTrailing(); + expect(withoutCount).toBe(0); + + setTimeout(() => { + expect(withCount).toBe(1); + expect(withoutCount).toBe(0); + done(); + }, 64); + }); + + it('should support a `maxWait` option', (done) => { + let callCount = 0; + + const debounced = debounce( + (value) => { + ++callCount; + return value; + }, + 32, + { maxWait: 64 }, + ); + + debounced(); + debounced(); + expect(callCount).toBe(0); + + setTimeout(() => { + expect(callCount).toBe(1); + debounced(); + debounced(); + expect(callCount).toBe(1); + }, 128); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 256); + }); + + it('should support `maxWait` in a tight loop', (done) => { + const limit = argv || isPhantom ? 1000 : 320; + let withCount = 0; + let withoutCount = 0; + + const withMaxWait = debounce( + () => { + withCount++; + }, + 64, + { maxWait: 128 }, + ); + + const withoutMaxWait = debounce(() => { + withoutCount++; + }, 96); + + const start = +new Date(); + while (new Date() - start < limit) { + withMaxWait(); + withoutMaxWait(); + } + const actual = [Boolean(withoutCount), Boolean(withCount)]; + setTimeout(() => { + expect(actual, [false).toEqual(true]); + done(); + }, 1); + }); + + it('should queue a trailing call for subsequent debounced calls after `maxWait`', (done) => { + let callCount = 0; + + const debounced = debounce( + () => { + ++callCount; + }, + 200, + { maxWait: 200 }, + ); + + debounced(); + + setTimeout(debounced, 190); + setTimeout(debounced, 200); + setTimeout(debounced, 210); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 500); + }); + + it('should cancel `maxDelayed` when `delayed` is invoked', (done) => { + let callCount = 0; + + const debounced = debounce( + () => { + callCount++; + }, + 32, + { maxWait: 64 }, + ); + + debounced(); + + setTimeout(() => { + debounced(); + expect(callCount).toBe(1); + }, 128); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 192); + }); + + it('should invoke the trailing call with the correct arguments and `this` binding', (done) => { + let actual; + let callCount = 0; + const object = {}; + + const debounced = debounce( + function (value) { + actual = [this]; + push.apply(actual, arguments); + return ++callCount != 2; + }, + 32, + { leading: true, maxWait: 64 }, + ); + + while (true) { + if (!debounced.call(object, 'a')) { + break; + } + } + setTimeout(() => { + expect(callCount).toBe(2); + expect(actual, [object).toEqual('a']); + done(); + }, 64); + }); +}); diff --git a/test/debounce.test.js b/test/debounce.test.js deleted file mode 100644 index 184d9b91db..0000000000 --- a/test/debounce.test.js +++ /dev/null @@ -1,250 +0,0 @@ -import assert from 'assert'; -import { identity, argv, isPhantom, push } from './utils.js'; -import debounce from '../debounce.js'; - -describe('debounce', function() { - it('should debounce a function', function(done) { - var callCount = 0; - - var debounced = debounce(function(value) { - ++callCount; - return value; - }, 32); - - var results = [debounced('a'), debounced('b'), debounced('c')]; - assert.deepStrictEqual(results, [undefined, undefined, undefined]); - assert.strictEqual(callCount, 0); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - - var results = [debounced('d'), debounced('e'), debounced('f')]; - assert.deepStrictEqual(results, ['c', 'c', 'c']); - assert.strictEqual(callCount, 1); - }, 128); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 256); - }); - - it('subsequent debounced calls return the last `func` result', function(done) { - var debounced = debounce(identity, 32); - debounced('a'); - - setTimeout(function() { - assert.notStrictEqual(debounced('b'), 'b'); - }, 64); - - setTimeout(function() { - assert.notStrictEqual(debounced('c'), 'c'); - done(); - }, 128); - }); - - it('should not immediately call `func` when `wait` is `0`', function(done) { - var callCount = 0, - debounced = debounce(function() { ++callCount; }, 0); - - debounced(); - debounced(); - assert.strictEqual(callCount, 0); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - done(); - }, 5); - }); - - it('should apply default options', function(done) { - var callCount = 0, - debounced = debounce(function() { callCount++; }, 32, {}); - - debounced(); - assert.strictEqual(callCount, 0); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - done(); - }, 64); - }); - - it('should support a `leading` option', function(done) { - var callCounts = [0, 0]; - - var withLeading = debounce(function() { - callCounts[0]++; - }, 32, { 'leading': true }); - - var withLeadingAndTrailing = debounce(function() { - callCounts[1]++; - }, 32, { 'leading': true }); - - withLeading(); - assert.strictEqual(callCounts[0], 1); - - withLeadingAndTrailing(); - withLeadingAndTrailing(); - assert.strictEqual(callCounts[1], 1); - - setTimeout(function() { - assert.deepStrictEqual(callCounts, [1, 2]); - - withLeading(); - assert.strictEqual(callCounts[0], 2); - - done(); - }, 64); - }); - - it('subsequent leading debounced calls return the last `func` result', function(done) { - var debounced = debounce(identity, 32, { 'leading': true, 'trailing': false }), - results = [debounced('a'), debounced('b')]; - - assert.deepStrictEqual(results, ['a', 'a']); - - setTimeout(function() { - var results = [debounced('c'), debounced('d')]; - assert.deepStrictEqual(results, ['c', 'c']); - done(); - }, 64); - }); - - it('should support a `trailing` option', function(done) { - var withCount = 0, - withoutCount = 0; - - var withTrailing = debounce(function() { - withCount++; - }, 32, { 'trailing': true }); - - var withoutTrailing = debounce(function() { - withoutCount++; - }, 32, { 'trailing': false }); - - withTrailing(); - assert.strictEqual(withCount, 0); - - withoutTrailing(); - assert.strictEqual(withoutCount, 0); - - setTimeout(function() { - assert.strictEqual(withCount, 1); - assert.strictEqual(withoutCount, 0); - done(); - }, 64); - }); - - it('should support a `maxWait` option', function(done) { - var callCount = 0; - - var debounced = debounce(function(value) { - ++callCount; - return value; - }, 32, { 'maxWait': 64 }); - - debounced(); - debounced(); - assert.strictEqual(callCount, 0); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - debounced(); - debounced(); - assert.strictEqual(callCount, 1); - }, 128); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 256); - }); - - it('should support `maxWait` in a tight loop', function(done) { - var limit = (argv || isPhantom) ? 1000 : 320, - withCount = 0, - withoutCount = 0; - - var withMaxWait = debounce(function() { - withCount++; - }, 64, { 'maxWait': 128 }); - - var withoutMaxWait = debounce(function() { - withoutCount++; - }, 96); - - var start = +new Date; - while ((new Date - start) < limit) { - withMaxWait(); - withoutMaxWait(); - } - var actual = [Boolean(withoutCount), Boolean(withCount)]; - setTimeout(function() { - assert.deepStrictEqual(actual, [false, true]); - done(); - }, 1); - }); - - it('should queue a trailing call for subsequent debounced calls after `maxWait`', function(done) { - var callCount = 0; - - var debounced = debounce(function() { - ++callCount; - }, 200, { 'maxWait': 200 }); - - debounced(); - - setTimeout(debounced, 190); - setTimeout(debounced, 200); - setTimeout(debounced, 210); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 500); - }); - - it('should cancel `maxDelayed` when `delayed` is invoked', function(done) { - var callCount = 0; - - var debounced = debounce(function() { - callCount++; - }, 32, { 'maxWait': 64 }); - - debounced(); - - setTimeout(function() { - debounced(); - assert.strictEqual(callCount, 1); - }, 128); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 192); - }); - - it('should invoke the trailing call with the correct arguments and `this` binding', function(done) { - var actual, - callCount = 0, - object = {}; - - var debounced = debounce(function(value) { - actual = [this]; - push.apply(actual, arguments); - return ++callCount != 2; - }, 32, { 'leading': true, 'maxWait': 64 }); - - while (true) { - if (!debounced.call(object, 'a')) { - break; - } - } - setTimeout(function() { - assert.strictEqual(callCount, 2); - assert.deepStrictEqual(actual, [object, 'a']); - done(); - }, 64); - }); -}); diff --git a/test/deburr.spec.js b/test/deburr.spec.js new file mode 100644 index 0000000000..762554b62f --- /dev/null +++ b/test/deburr.spec.js @@ -0,0 +1,25 @@ +import lodashStable from 'lodash'; +import { burredLetters, deburredLetters, comboMarks } from './utils'; +import deburr from '../src/deburr'; + +describe('deburr', () => { + it('should convert Latin Unicode letters to basic Latin', () => { + const actual = lodashStable.map(burredLetters, deburr); + expect(actual).toEqual(deburredLetters); + }); + + it('should not deburr Latin mathematical operators', () => { + const operators = ['\xd7', '\xf7']; + const actual = lodashStable.map(operators, deburr); + + expect(actual).toEqual(operators); + }); + + it('should deburr combining diacritical marks', () => { + const expected = lodashStable.map(comboMarks, lodashStable.constant('ei')); + + const actual = lodashStable.map(comboMarks, (chr) => deburr(`e${chr}i`)); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/deburr.test.js b/test/deburr.test.js deleted file mode 100644 index 5ab176f4a6..0000000000 --- a/test/deburr.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { burredLetters, deburredLetters, comboMarks } from './utils.js'; -import deburr from '../deburr.js'; - -describe('deburr', function() { - it('should convert Latin Unicode letters to basic Latin', function() { - var actual = lodashStable.map(burredLetters, deburr); - assert.deepStrictEqual(actual, deburredLetters); - }); - - it('should not deburr Latin mathematical operators', function() { - var operators = ['\xd7', '\xf7'], - actual = lodashStable.map(operators, deburr); - - assert.deepStrictEqual(actual, operators); - }); - - it('should deburr combining diacritical marks', function() { - var expected = lodashStable.map(comboMarks, lodashStable.constant('ei')); - - var actual = lodashStable.map(comboMarks, function(chr) { - return deburr('e' + chr + 'i'); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/defaultTo.spec.js b/test/defaultTo.spec.js new file mode 100644 index 0000000000..f0f3c4402e --- /dev/null +++ b/test/defaultTo.spec.js @@ -0,0 +1,15 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import defaultTo from '../src/defaultTo'; + +describe('defaultTo', () => { + it('should return a default value if `value` is `NaN` or nullish', () => { + const expected = lodashStable.map(falsey, (value) => + value == null || value !== value ? 1 : value, + ); + + const actual = lodashStable.map(falsey, (value) => defaultTo(value, 1)); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/defaultTo.test.js b/test/defaultTo.test.js deleted file mode 100644 index 5d6dc5f3ab..0000000000 --- a/test/defaultTo.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey } from './utils.js'; -import defaultTo from '../defaultTo.js'; - -describe('defaultTo', function() { - it('should return a default value if `value` is `NaN` or nullish', function() { - var expected = lodashStable.map(falsey, function(value) { - return (value == null || value !== value) ? 1 : value; - }); - - var actual = lodashStable.map(falsey, function(value) { - return defaultTo(value, 1); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/defaults.spec.js b/test/defaults.spec.js new file mode 100644 index 0000000000..cef553f5d0 --- /dev/null +++ b/test/defaults.spec.js @@ -0,0 +1,65 @@ +import lodashStable from 'lodash'; +import { objectProto } from './utils'; +import defaults from '../src/defaults'; + +describe('defaults', () => { + it('should assign source properties if missing on `object`', () => { + const actual = defaults({ a: 1 }, { a: 2, b: 2 }); + expect(actual).toEqual({ a: 1, b: 2 }); + }); + + it('should accept multiple sources', () => { + const expected = { a: 1, b: 2, c: 3 }; + let actual = defaults({ a: 1, b: 2 }, { b: 3 }, { c: 3 }); + + expect(actual).toEqual(expected); + + actual = defaults({ a: 1, b: 2 }, { b: 3, c: 3 }, { c: 2 }); + expect(actual).toEqual(expected); + }); + + it('should not overwrite `null` values', () => { + const actual = defaults({ a: null }, { a: 1 }); + expect(actual.a).toBe(null); + }); + + it('should overwrite `undefined` values', () => { + const actual = defaults({ a: undefined }, { a: 1 }); + expect(actual.a).toBe(1); + }); + + it('should assign `undefined` values', () => { + const source = { a: undefined, b: 1 }; + const actual = defaults({}, source); + + expect(actual).toEqual({ a: undefined, b: 1 }); + }); + + it('should assign properties that shadow those on `Object.prototype`', () => { + const object = { + constructor: objectProto.constructor, + hasOwnProperty: objectProto.hasOwnProperty, + isPrototypeOf: objectProto.isPrototypeOf, + propertyIsEnumerable: objectProto.propertyIsEnumerable, + toLocaleString: objectProto.toLocaleString, + toString: objectProto.toString, + valueOf: objectProto.valueOf, + }; + + const source = { + constructor: 1, + hasOwnProperty: 2, + isPrototypeOf: 3, + propertyIsEnumerable: 4, + toLocaleString: 5, + toString: 6, + valueOf: 7, + }; + + let expected = lodashStable.clone(source); + expect(defaults({}, source)).toEqual(expected); + + expected = lodashStable.clone(object); + expect(defaults({}, object, source)).toEqual(expected); + }); +}); diff --git a/test/defaults.test.js b/test/defaults.test.js deleted file mode 100644 index 867f31d805..0000000000 --- a/test/defaults.test.js +++ /dev/null @@ -1,66 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { objectProto } from './utils.js'; -import defaults from '../defaults.js'; - -describe('defaults', function() { - it('should assign source properties if missing on `object`', function() { - var actual = defaults({ 'a': 1 }, { 'a': 2, 'b': 2 }); - assert.deepStrictEqual(actual, { 'a': 1, 'b': 2 }); - }); - - it('should accept multiple sources', function() { - var expected = { 'a': 1, 'b': 2, 'c': 3 }, - actual = defaults({ 'a': 1, 'b': 2 }, { 'b': 3 }, { 'c': 3 }); - - assert.deepStrictEqual(actual, expected); - - actual = defaults({ 'a': 1, 'b': 2 }, { 'b': 3, 'c': 3 }, { 'c': 2 }); - assert.deepStrictEqual(actual, expected); - }); - - it('should not overwrite `null` values', function() { - var actual = defaults({ 'a': null }, { 'a': 1 }); - assert.strictEqual(actual.a, null); - }); - - it('should overwrite `undefined` values', function() { - var actual = defaults({ 'a': undefined }, { 'a': 1 }); - assert.strictEqual(actual.a, 1); - }); - - it('should assign `undefined` values', function() { - var source = { 'a': undefined, 'b': 1 }, - actual = defaults({}, source); - - assert.deepStrictEqual(actual, { 'a': undefined, 'b': 1 }); - }); - - it('should assign properties that shadow those on `Object.prototype`', function() { - var object = { - 'constructor': objectProto.constructor, - 'hasOwnProperty': objectProto.hasOwnProperty, - 'isPrototypeOf': objectProto.isPrototypeOf, - 'propertyIsEnumerable': objectProto.propertyIsEnumerable, - 'toLocaleString': objectProto.toLocaleString, - 'toString': objectProto.toString, - 'valueOf': objectProto.valueOf - }; - - var source = { - 'constructor': 1, - 'hasOwnProperty': 2, - 'isPrototypeOf': 3, - 'propertyIsEnumerable': 4, - 'toLocaleString': 5, - 'toString': 6, - 'valueOf': 7 - }; - - var expected = lodashStable.clone(source); - assert.deepStrictEqual(defaults({}, source), expected); - - expected = lodashStable.clone(object); - assert.deepStrictEqual(defaults({}, object, source), expected); - }); -}); diff --git a/test/defaultsDeep.js b/test/defaultsDeep.js deleted file mode 100644 index a4a9ff2fb1..0000000000 --- a/test/defaultsDeep.js +++ /dev/null @@ -1,101 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop } from './utils.js'; -import defaultsDeep from '../defaultsDeep.js'; - -describe('defaultsDeep', function() { - it('should deep assign source properties if missing on `object`', function() { - var object = { 'a': { 'b': 2 }, 'd': 4 }, - source = { 'a': { 'b': 3, 'c': 3 }, 'e': 5 }, - expected = { 'a': { 'b': 2, 'c': 3 }, 'd': 4, 'e': 5 }; - - assert.deepStrictEqual(defaultsDeep(object, source), expected); - }); - - it('should accept multiple sources', function() { - var source1 = { 'a': { 'b': 3 } }, - source2 = { 'a': { 'c': 3 } }, - source3 = { 'a': { 'b': 3, 'c': 3 } }, - source4 = { 'a': { 'c': 4 } }, - expected = { 'a': { 'b': 2, 'c': 3 } }; - - assert.deepStrictEqual(defaultsDeep({ 'a': { 'b': 2 } }, source1, source2), expected); - assert.deepStrictEqual(defaultsDeep({ 'a': { 'b': 2 } }, source3, source4), expected); - }); - - it('should not overwrite `null` values', function() { - var object = { 'a': { 'b': null } }, - source = { 'a': { 'b': 2 } }, - actual = defaultsDeep(object, source); - - assert.strictEqual(actual.a.b, null); - }); - - it('should not overwrite regexp values', function() { - var object = { 'a': { 'b': /x/ } }, - source = { 'a': { 'b': /y/ } }, - actual = defaultsDeep(object, source); - - assert.deepStrictEqual(actual.a.b, /x/); - }); - - it('should not convert function properties to objects', function() { - var actual = defaultsDeep({}, { 'a': noop }); - assert.strictEqual(actual.a, noop); - - actual = defaultsDeep({}, { 'a': { 'b': noop } }); - assert.strictEqual(actual.a.b, noop); - }); - - it('should overwrite `undefined` values', function() { - var object = { 'a': { 'b': undefined } }, - source = { 'a': { 'b': 2 } }, - actual = defaultsDeep(object, source); - - assert.strictEqual(actual.a.b, 2); - }); - - it('should assign `undefined` values', function() { - var source = { 'a': undefined, 'b': { 'c': undefined, 'd': 1 } }, - expected = lodashStable.cloneDeep(source), - actual = defaultsDeep({}, source); - - assert.deepStrictEqual(actual, expected); - }); - - it('should merge sources containing circular references', function() { - var object = { - 'foo': { 'b': { 'c': { 'd': {} } } }, - 'bar': { 'a': 2 } - }; - - var source = { - 'foo': { 'b': { 'c': { 'd': {} } } }, - 'bar': {} - }; - - object.foo.b.c.d = object; - source.foo.b.c.d = source; - source.bar.b = source.foo.b; - - var actual = defaultsDeep(object, source); - - assert.strictEqual(actual.bar.b, actual.foo.b); - assert.strictEqual(actual.foo.b.c.d, actual.foo.b.c.d.foo.b.c.d); - }); - - it('should not modify sources', function() { - var source1 = { 'a': 1, 'b': { 'c': 2 } }, - source2 = { 'b': { 'c': 3, 'd': 3 } }, - actual = defaultsDeep({}, source1, source2); - - assert.deepStrictEqual(actual, { 'a': 1, 'b': { 'c': 2, 'd': 3 } }); - assert.deepStrictEqual(source1, { 'a': 1, 'b': { 'c': 2 } }); - assert.deepStrictEqual(source2, { 'b': { 'c': 3, 'd': 3 } }); - }); - - it('should not attempt a merge of a string into an array', function() { - var actual = defaultsDeep({ 'a': ['abc'] }, { 'a': 'abc' }); - assert.deepStrictEqual(actual.a, ['abc']); - }); -}); diff --git a/test/defaultsDeep.spec.js b/test/defaultsDeep.spec.js new file mode 100644 index 0000000000..558c6b254f --- /dev/null +++ b/test/defaultsDeep.spec.js @@ -0,0 +1,100 @@ +import lodashStable from 'lodash'; +import { noop } from './utils'; +import defaultsDeep from '../src/defaultsDeep'; + +describe('defaultsDeep', () => { + it('should deep assign source properties if missing on `object`', () => { + const object = { a: { b: 2 }, d: 4 }; + const source = { a: { b: 3, c: 3 }, e: 5 }; + const expected = { a: { b: 2, c: 3 }, d: 4, e: 5 }; + + expect(defaultsDeep(object, source)).toEqual(expected); + }); + + it('should accept multiple sources', () => { + const source1 = { a: { b: 3 } }; + const source2 = { a: { c: 3 } }; + const source3 = { a: { b: 3, c: 3 } }; + const source4 = { a: { c: 4 } }; + const expected = { a: { b: 2, c: 3 } }; + + expect(defaultsDeep({ a: { b: 2 } }, source1, source2)).toEqual(expected); + expect(defaultsDeep({ a: { b: 2 } }, source3, source4)).toEqual(expected); + }); + + it('should not overwrite `null` values', () => { + const object = { a: { b: null } }; + const source = { a: { b: 2 } }; + const actual = defaultsDeep(object, source); + + expect(actual.a.b).toBe(null); + }); + + it('should not overwrite regexp values', () => { + const object = { a: { b: /x/ } }; + const source = { a: { b: /y/ } }; + const actual = defaultsDeep(object, source); + + expect(actual.a.b).toEqual(/x/); + }); + + it('should not convert function properties to objects', () => { + let actual = defaultsDeep({}, { a: noop }); + expect(actual.a).toBe(noop); + + actual = defaultsDeep({}, { a: { b: noop } }); + expect(actual.a.b).toBe(noop); + }); + + it('should overwrite `undefined` values', () => { + const object = { a: { b: undefined } }; + const source = { a: { b: 2 } }; + const actual = defaultsDeep(object, source); + + expect(actual.a.b).toBe(2); + }); + + it('should assign `undefined` values', () => { + const source = { a: undefined, b: { c: undefined, d: 1 } }; + const expected = lodashStable.cloneDeep(source); + const actual = defaultsDeep({}, source); + + expect(actual).toEqual(expected); + }); + + it('should merge sources containing circular references', () => { + const object = { + foo: { b: { c: { d: {} } } }, + bar: { a: 2 }, + }; + + const source = { + foo: { b: { c: { d: {} } } }, + bar: {}, + }; + + object.foo.b.c.d = object; + source.foo.b.c.d = source; + source.bar.b = source.foo.b; + + const actual = defaultsDeep(object, source); + + expect(actual.bar.b).toBe(actual.foo.b); + expect(actual.foo.b.c.d).toBe(actual.foo.b.c.d.foo.b.c.d); + }); + + it('should not modify sources', () => { + const source1 = { a: 1, b: { c: 2 } }; + const source2 = { b: { c: 3, d: 3 } }; + const actual = defaultsDeep({}, source1, source2); + + expect(actual, { a: 1, b: { c: 2).toEqual(d: 3 } }); + expect(source1, { a: 1).toEqual(b: { c: 2 } }); + expect(source2, { b: { c: 3).toEqual(d: 3 } }); + }); + + it('should not attempt a merge of a string into an array', () => { + const actual = defaultsDeep({ a: ['abc'] }, { a: 'abc' }); + expect(actual.a).toEqual(['abc']); + }); +}); diff --git a/test/defer.spec.js b/test/defer.spec.js new file mode 100644 index 0000000000..74012377a5 --- /dev/null +++ b/test/defer.spec.js @@ -0,0 +1,47 @@ +import { slice } from './utils'; +import defer from '../src/defer'; + +describe('defer', () => { + it('should defer `func` execution', (done) => { + let pass = false; + defer(() => { + pass = true; + }); + + setTimeout(() => { + expect(pass) + done(); + }, 32); + }); + + it('should provide additional arguments to `func`', (done) => { + let args; + + defer( + function () { + args = slice.call(arguments); + }, + 1, + 2, + ); + + setTimeout(() => { + expect(args).toEqual([1, 2]); + done(); + }, 32); + }); + + it('should be cancelable', (done) => { + let pass = true; + const timerId = defer(() => { + pass = false; + }); + + clearTimeout(timerId); + + setTimeout(() => { + expect(pass); + done(); + }, 32); + }); +}); diff --git a/test/defer.test.js b/test/defer.test.js deleted file mode 100644 index 421fab3722..0000000000 --- a/test/defer.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import defer from '../defer.js'; - -describe('defer', function() { - it('should defer `func` execution', function(done) { - var pass = false; - defer(function() { pass = true; }); - - setTimeout(function() { - assert.ok(pass); - done(); - }, 32); - }); - - it('should provide additional arguments to `func`', function(done) { - var args; - - defer(function() { - args = slice.call(arguments); - }, 1, 2); - - setTimeout(function() { - assert.deepStrictEqual(args, [1, 2]); - done(); - }, 32); - }); - - it('should be cancelable', function(done) { - var pass = true, - timerId = defer(function() { pass = false; }); - - clearTimeout(timerId); - - setTimeout(function() { - assert.ok(pass); - done(); - }, 32); - }); -}); diff --git a/test/delay.js b/test/delay.js deleted file mode 100644 index ef3ebcae4c..0000000000 --- a/test/delay.js +++ /dev/null @@ -1,67 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import delay from '../delay.js'; - -describe('delay', function() { - it('should delay `func` execution', function(done) { - var pass = false; - delay(function() { pass = true; }, 32); - - setTimeout(function() { - assert.ok(!pass); - }, 1); - - setTimeout(function() { - assert.ok(pass); - done(); - }, 64); - }); - - it('should provide additional arguments to `func`', function(done) { - var args; - - delay(function() { - args = slice.call(arguments); - }, 32, 1, 2); - - setTimeout(function() { - assert.deepStrictEqual(args, [1, 2]); - done(); - }, 64); - }); - - it('should use a default `wait` of `0`', function(done) { - var pass = false; - delay(function() { pass = true; }); - - assert.ok(!pass); - - setTimeout(function() { - assert.ok(pass); - done(); - }, 0); - }); - - it('should be cancelable', function(done) { - var pass = true, - timerId = delay(function() { pass = false; }, 32); - - clearTimeout(timerId); - - setTimeout(function() { - assert.ok(pass); - done(); - }, 64); - }); - - it('should work with mocked `setTimeout`', function() { - var pass = false, - setTimeout = root.setTimeout; - - setProperty(root, 'setTimeout', function(func) { func(); }); - delay(function() { pass = true; }, 32); - setProperty(root, 'setTimeout', setTimeout); - - assert.ok(pass); - }); -}); diff --git a/test/delay.spec.js b/test/delay.spec.js new file mode 100644 index 0000000000..170a48b3aa --- /dev/null +++ b/test/delay.spec.js @@ -0,0 +1,81 @@ +import { slice } from './utils'; +import delay from '../src/delay'; + +describe('delay', () => { + it('should delay `func` execution', (done) => { + let pass = false; + delay(() => { + pass = true; + }, 32); + + setTimeout(() => { + expect(pass).toBe(false) + }, 1); + + setTimeout(() => { + expect(pass) + done(); + }, 64); + }); + + it('should provide additional arguments to `func`', (done) => { + let args; + + delay( + function () { + args = slice.call(arguments); + }, + 32, + 1, + 2, + ); + + setTimeout(() => { + expect(args, [1).toEqual(2]); + done(); + }, 64); + }); + + it('should use a default `wait` of `0`', (done) => { + let pass = false; + delay(() => { + pass = true; + }); + + expect(pass).toBe(false) + + setTimeout(() => { + expect(pass) + done(); + }, 0); + }); + + it('should be cancelable', (done) => { + let pass = true; + const timerId = delay(() => { + pass = false; + }, 32); + + clearTimeout(timerId); + + setTimeout(() => { + expect(pass) + done(); + }, 64); + }); + + it('should work with mocked `setTimeout`', () => { + let pass = false; + const setTimeout = root.setTimeout; + + setProperty(root, 'setTimeout', (func) => { + func(); + }); + delay(() => { + pass = true; + }, 32); + setProperty(root, 'setTimeout', setTimeout); + + expect(pass) + }); +}); diff --git a/test/difference-methods.js b/test/difference-methods.js deleted file mode 100644 index 6591193cf2..0000000000 --- a/test/difference-methods.js +++ /dev/null @@ -1,85 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, LARGE_ARRAY_SIZE, stubOne, stubNaN, args } from './utils.js'; - -describe('difference methods', function() { - lodashStable.each(['difference', 'differenceBy', 'differenceWith'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should return the difference of two arrays', function() { - var actual = func([2, 1], [2, 3]); - assert.deepStrictEqual(actual, [1]); - }); - - it('`_.' + methodName + '` should return the difference of multiple arrays', function() { - var actual = func([2, 1, 2, 3], [3, 4], [3, 2]); - assert.deepStrictEqual(actual, [1]); - }); - - it('`_.' + methodName + '` should treat `-0` as `0`', function() { - var array = [-0, 0]; - - var actual = lodashStable.map(array, function(value) { - return func(array, [value]); - }); - - assert.deepStrictEqual(actual, [[], []]); - - actual = lodashStable.map(func([-0, 1], [1]), lodashStable.toString); - assert.deepStrictEqual(actual, ['0']); - }); - - it('`_.' + methodName + '` should match `NaN`', function() { - assert.deepStrictEqual(func([1, NaN, 3], [NaN, 5, NaN]), [1, 3]); - }); - - it('`_.' + methodName + '` should work with large arrays', function() { - var array1 = lodashStable.range(LARGE_ARRAY_SIZE + 1), - array2 = lodashStable.range(LARGE_ARRAY_SIZE), - a = {}, - b = {}, - c = {}; - - array1.push(a, b, c); - array2.push(b, c, a); - - assert.deepStrictEqual(func(array1, array2), [LARGE_ARRAY_SIZE]); - }); - - it('`_.' + methodName + '` should work with large arrays of `-0` as `0`', function() { - var array = [-0, 0]; - - var actual = lodashStable.map(array, function(value) { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(value)); - return func(array, largeArray); - }); - - assert.deepStrictEqual(actual, [[], []]); - - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubOne); - actual = lodashStable.map(func([-0, 1], largeArray), lodashStable.toString); - assert.deepStrictEqual(actual, ['0']); - }); - - it('`_.' + methodName + '` should work with large arrays of `NaN`', function() { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubNaN); - assert.deepStrictEqual(func([1, NaN, 3], largeArray), [1, 3]); - }); - - it('`_.' + methodName + '` should work with large arrays of objects', function() { - var object1 = {}, - object2 = {}, - largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(object1)); - - assert.deepStrictEqual(func([object1, object2], largeArray), [object2]); - }); - - it('`_.' + methodName + '` should ignore values that are not array-like', function() { - var array = [1, null, 3]; - - assert.deepStrictEqual(func(args, 3, { '0': 1 }), [1, 2, 3]); - assert.deepStrictEqual(func(null, array, 1), []); - assert.deepStrictEqual(func(array, args, null), [null]); - }); - }); -}); diff --git a/test/difference-methods.spec.js b/test/difference-methods.spec.js new file mode 100644 index 0000000000..9b4402661b --- /dev/null +++ b/test/difference-methods.spec.js @@ -0,0 +1,85 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, stubOne, stubNaN, args } from './utils'; + +describe('difference methods', () => { + lodashStable.each(['difference', 'differenceBy', 'differenceWith'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should return the difference of two arrays`, () => { + const actual = func([2, 1], [2, 3]); + expect(actual).toEqual([1]); + }); + + it(`\`_.${methodName}\` should return the difference of multiple arrays`, () => { + const actual = func([2, 1, 2, 3], [3, 4], [3, 2]); + expect(actual).toEqual([1]); + }); + + it(`\`_.${methodName}\` should treat \`-0\` as \`0\``, () => { + const array = [-0, 0]; + + let actual = lodashStable.map(array, (value) => func(array, [value])); + + expect(actual, [[]).toEqual([]]); + + actual = lodashStable.map(func([-0, 1], [1]), lodashStable.toString); + expect(actual).toEqual(['0']); + }); + + it(`\`_.${methodName}\` should match \`NaN\``, () => { + expect(func([1, NaN, 3], [NaN, 5, NaN]), [1).toEqual(3]); + }); + + it(`\`_.${methodName}\` should work with large arrays`, () => { + const array1 = lodashStable.range(LARGE_ARRAY_SIZE + 1); + const array2 = lodashStable.range(LARGE_ARRAY_SIZE); + const a = {}; + const b = {}; + const c = {}; + + array1.push(a, b, c); + array2.push(b, c, a); + + expect(func(array1, array2)).toEqual([LARGE_ARRAY_SIZE]); + }); + + it(`\`_.${methodName}\` should work with large arrays of \`-0\` as \`0\``, () => { + const array = [-0, 0]; + + let actual = lodashStable.map(array, (value) => { + const largeArray = lodashStable.times( + LARGE_ARRAY_SIZE, + lodashStable.constant(value), + ); + return func(array, largeArray); + }); + + expect(actual, [[]).toEqual([]]); + + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubOne); + actual = lodashStable.map(func([-0, 1], largeArray), lodashStable.toString); + expect(actual).toEqual(['0']); + }); + + it(`\`_.${methodName}\` should work with large arrays of \`NaN\``, () => { + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubNaN); + expect(func([1, NaN, 3], largeArray), [1).toEqual(3]); + }); + + it(`\`_.${methodName}\` should work with large arrays of objects`, () => { + const object1 = {}; + const object2 = {}; + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(object1)); + + expect(func([object1, object2], largeArray)).toEqual([object2]); + }); + + it(`\`_.${methodName}\` should ignore values that are not array-like`, () => { + const array = [1, null, 3]; + + expect(func(args, 3, { 0: 1 }), [1, 2).toEqual(3]); + expect(func(null, array, 1)).toEqual([]); + expect(func(array, args, null)).toEqual([null]); + }); + }); +}); diff --git a/test/differenceBy.js b/test/differenceBy.js deleted file mode 100644 index af5ca665bc..0000000000 --- a/test/differenceBy.js +++ /dev/null @@ -1,23 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import differenceBy from '../differenceBy.js'; - -describe('differenceBy', function() { - it('should accept an `iteratee`', function() { - var actual = differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); - assert.deepStrictEqual(actual, [1.2]); - - actual = differenceBy([{ 'x': 2 }, { 'x': 1 }], [{ 'x': 1 }], 'x'); - assert.deepStrictEqual(actual, [{ 'x': 2 }]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - differenceBy([2.1, 1.2], [2.3, 3.4], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [2.3]); - }); -}); diff --git a/test/differenceBy.spec.js b/test/differenceBy.spec.js new file mode 100644 index 0000000000..1ac2f3eec9 --- /dev/null +++ b/test/differenceBy.spec.js @@ -0,0 +1,22 @@ +import { slice } from './utils'; +import differenceBy from '../src/differenceBy'; + +describe('differenceBy', () => { + it('should accept an `iteratee`', () => { + let actual = differenceBy([2.1, 1.2], [2.3, 3.4], Math.floor); + expect(actual).toEqual([1.2]); + + actual = differenceBy([{ x: 2 }, { x: 1 }], [{ x: 1 }], 'x'); + expect(actual).toEqual([{ x: 2 }]); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + differenceBy([2.1, 1.2], [2.3, 3.4], function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([2.3]); + }); +}); diff --git a/test/differenceWith.spec.js b/test/differenceWith.spec.js new file mode 100644 index 0000000000..f0bd2b243c --- /dev/null +++ b/test/differenceWith.spec.js @@ -0,0 +1,28 @@ +import lodashStable from 'lodash'; +import { LARGE_ARRAY_SIZE, stubOne } from './utils'; +import differenceWith from '../src/differenceWith'; + +describe('differenceWith', () => { + it('should work with a `comparator`', () => { + const objects = [ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + ]; + const actual = differenceWith(objects, [{ x: 1, y: 2 }], lodashStable.isEqual); + + expect(actual).toEqual([objects[1]]); + }); + + it('should preserve the sign of `0`', () => { + const array = [-0, 1]; + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubOne); + const others = [[1], largeArray]; + const expected = lodashStable.map(others, lodashStable.constant(['-0'])); + + const actual = lodashStable.map(others, (other) => + lodashStable.map(differenceWith(array, other, lodashStable.eq), lodashStable.toString), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/differenceWith.test.js b/test/differenceWith.test.js deleted file mode 100644 index 1c7c49d405..0000000000 --- a/test/differenceWith.test.js +++ /dev/null @@ -1,26 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, stubOne } from './utils.js'; -import differenceWith from '../differenceWith.js'; - -describe('differenceWith', function() { - it('should work with a `comparator`', function() { - var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }], - actual = differenceWith(objects, [{ 'x': 1, 'y': 2 }], lodashStable.isEqual); - - assert.deepStrictEqual(actual, [objects[1]]); - }); - - it('should preserve the sign of `0`', function() { - var array = [-0, 1], - largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubOne), - others = [[1], largeArray], - expected = lodashStable.map(others, lodashStable.constant(['-0'])); - - var actual = lodashStable.map(others, function(other) { - return lodashStable.map(differenceWith(array, other, lodashStable.eq), lodashStable.toString); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/divide.spec.js b/test/divide.spec.js new file mode 100644 index 0000000000..9f54fdf693 --- /dev/null +++ b/test/divide.spec.js @@ -0,0 +1,14 @@ +import divide from '../src/divide'; + +describe('divide', () => { + it('should divide two numbers', () => { + expect(divide(6, 4)).toBe(1.5); + expect(divide(-6, 4)).toBe(-1.5); + expect(divide(-6, -4)).toBe(1.5); + }); + + it('should coerce arguments to numbers', () => { + expect(divide('6', '4')).toBe(1.5); + expect(divide('x', 'y')).toEqual(NaN); + }); +}); diff --git a/test/divide.test.js b/test/divide.test.js deleted file mode 100644 index 495a6bb703..0000000000 --- a/test/divide.test.js +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'assert'; -import divide from '../divide.js'; - -describe('divide', function() { - it('should divide two numbers', function() { - assert.strictEqual(divide(6, 4), 1.5); - assert.strictEqual(divide(-6, 4), -1.5); - assert.strictEqual(divide(-6, -4), 1.5); - }); - - it('should coerce arguments to numbers', function() { - assert.strictEqual(divide('6', '4'), 1.5); - assert.deepStrictEqual(divide('x', 'y'), NaN); - }); -}); diff --git a/test/drop.spec.js b/test/drop.spec.js new file mode 100644 index 0000000000..8f7056575f --- /dev/null +++ b/test/drop.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import drop from '../src/drop'; + +describe('drop', () => { + const array = [1, 2, 3]; + + it('should drop the first two elements', () => { + expect(drop(array, 2)).toEqual([3]); + }); + + it('should treat falsey `n` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => + value === undefined ? [2, 3] : array, + ); + + const actual = lodashStable.map(falsey, (n) => drop(array, n)); + + expect(actual).toEqual(expected); + }); + + it('should return all elements when `n` < `1`', () => { + lodashStable.each([0, -1, -Infinity], (n) => { + expect(drop(array, n)).toEqual(array); + }); + }); + + it('should return an empty array when `n` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (n) => { + expect(drop(array, n)).toEqual([]); + }); + }); + + it('should coerce `n` to an integer', () => { + expect(drop(array, 1.6)).toEqual([2, 3]); + }); +}); diff --git a/test/drop.test.js b/test/drop.test.js deleted file mode 100644 index 5011c70c06..0000000000 --- a/test/drop.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import drop from '../drop.js'; - -describe('drop', function() { - var array = [1, 2, 3]; - - it('should drop the first two elements', function() { - assert.deepStrictEqual(drop(array, 2), [3]); - }); - - it('should treat falsey `n` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? [2, 3] : array; - }); - - var actual = lodashStable.map(falsey, function(n) { - return drop(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return all elements when `n` < `1`', function() { - lodashStable.each([0, -1, -Infinity], function(n) { - assert.deepStrictEqual(drop(array, n), array); - }); - }); - - it('should return an empty array when `n` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(n) { - assert.deepStrictEqual(drop(array, n), []); - }); - }); - - it('should coerce `n` to an integer', function() { - assert.deepStrictEqual(drop(array, 1.6), [2, 3]); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1), - predicate = function(value) { values.push(value); return isEven(value); }, - values = [], - actual = _(array).drop(2).drop().value(); - - assert.deepEqual(actual, array.slice(3)); - - actual = _(array).filter(predicate).drop(2).drop().value(); - assert.deepEqual(values, array); - assert.deepEqual(actual, drop(drop(_.filter(array, predicate), 2))); - - actual = _(array).drop(2).dropRight().drop().dropRight(2).value(); - assert.deepEqual(actual, _.dropRight(drop(_.dropRight(drop(array, 2))), 2)); - - values = []; - - actual = _(array).drop().filter(predicate).drop(2).dropRight().drop().dropRight(2).value(); - assert.deepEqual(values, array.slice(1)); - assert.deepEqual(actual, _.dropRight(drop(_.dropRight(drop(_.filter(drop(array), predicate), 2))), 2)); - }); -}); diff --git a/test/dropRight.spec.js b/test/dropRight.spec.js new file mode 100644 index 0000000000..7017ef007c --- /dev/null +++ b/test/dropRight.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import dropRight from '../src/dropRight'; + +describe('dropRight', () => { + const array = [1, 2, 3]; + + it('should drop the last two elements', () => { + expect(dropRight(array, 2)).toEqual([1]); + }); + + it('should treat falsey `n` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => + value === undefined ? [1, 2] : array, + ); + + const actual = lodashStable.map(falsey, (n) => dropRight(array, n)); + + expect(actual).toEqual(expected); + }); + + it('should return all elements when `n` < `1`', () => { + lodashStable.each([0, -1, -Infinity], (n) => { + expect(dropRight(array, n)).toEqual(array); + }); + }); + + it('should return an empty array when `n` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (n) => { + expect(dropRight(array, n)).toEqual([]); + }); + }); + + it('should coerce `n` to an integer', () => { + expect(dropRight(array, 1.6)).toEqual([1, 2]); + }); +}); diff --git a/test/dropRight.test.js b/test/dropRight.test.js deleted file mode 100644 index 040ecc6cf1..0000000000 --- a/test/dropRight.test.js +++ /dev/null @@ -1,62 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import dropRight from '../dropRight.js'; - -describe('dropRight', function() { - var array = [1, 2, 3]; - - it('should drop the last two elements', function() { - assert.deepStrictEqual(dropRight(array, 2), [1]); - }); - - it('should treat falsey `n` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? [1, 2] : array; - }); - - var actual = lodashStable.map(falsey, function(n) { - return dropRight(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return all elements when `n` < `1`', function() { - lodashStable.each([0, -1, -Infinity], function(n) { - assert.deepStrictEqual(dropRight(array, n), array); - }); - }); - - it('should return an empty array when `n` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(n) { - assert.deepStrictEqual(dropRight(array, n), []); - }); - }); - - it('should coerce `n` to an integer', function() { - assert.deepStrictEqual(dropRight(array, 1.6), [1, 2]); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1), - predicate = function(value) { values.push(value); return isEven(value); }, - values = [], - actual = _(array).dropRight(2).dropRight().value(); - - assert.deepEqual(actual, array.slice(0, -3)); - - actual = _(array).filter(predicate).dropRight(2).dropRight().value(); - assert.deepEqual(values, array); - assert.deepEqual(actual, dropRight(dropRight(_.filter(array, predicate), 2))); - - actual = _(array).dropRight(2).drop().dropRight().drop(2).value(); - assert.deepEqual(actual, _.drop(dropRight(_.drop(dropRight(array, 2))), 2)); - - values = []; - - actual = _(array).dropRight().filter(predicate).dropRight(2).drop().dropRight().drop(2).value(); - assert.deepEqual(values, array.slice(0, -1)); - assert.deepEqual(actual, _.drop(dropRight(_.drop(dropRight(_.filter(dropRight(array), predicate), 2))), 2)); - }); -}); diff --git a/test/dropRightWhile.js b/test/dropRightWhile.js deleted file mode 100644 index 07dceadde8..0000000000 --- a/test/dropRightWhile.js +++ /dev/null @@ -1,52 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import dropRightWhile from '../dropRightWhile.js'; - -describe('dropRightWhile', function() { - var array = [1, 2, 3, 4]; - - var objects = [ - { 'a': 0, 'b': 0 }, - { 'a': 1, 'b': 1 }, - { 'a': 2, 'b': 2 } - ]; - - it('should drop elements while `predicate` returns truthy', function() { - var actual = dropRightWhile(array, function(n) { - return n > 2; - }); - - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('should provide correct `predicate` arguments', function() { - var args; - - dropRightWhile(array, function() { - args = slice.call(arguments); - }); - - assert.deepStrictEqual(args, [4, 3, array]); - }); - - it('should work with `_.matches` shorthands', function() { - assert.deepStrictEqual(dropRightWhile(objects, { 'b': 2 }), objects.slice(0, 2)); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - assert.deepStrictEqual(dropRightWhile(objects, ['b', 2]), objects.slice(0, 2)); - }); - - it('should work with `_.property` shorthands', function() { - assert.deepStrictEqual(dropRightWhile(objects, 'b'), objects.slice(0, 1)); - }); - - it('should return a wrapped value when chaining', function() { - var wrapped = _(array).dropRightWhile(function(n) { - return n > 2; - }); - - assert.ok(wrapped instanceof _); - assert.deepEqual(wrapped.value(), [1, 2]); - }); -}); diff --git a/test/dropRightWhile.spec.js b/test/dropRightWhile.spec.js new file mode 100644 index 0000000000..f42466f75d --- /dev/null +++ b/test/dropRightWhile.spec.js @@ -0,0 +1,50 @@ +import { slice } from './utils'; +import dropRightWhile from '../src/dropRightWhile'; + +describe('dropRightWhile', () => { + const array = [1, 2, 3, 4]; + + const objects = [ + { a: 0, b: 0 }, + { a: 1, b: 1 }, + { a: 2, b: 2 }, + ]; + + it('should drop elements while `predicate` returns truthy', () => { + const actual = dropRightWhile(array, (n) => n > 2); + + expect(actual).toEqual([1, 2]); + }); + + it('should provide correct `predicate` arguments', () => { + let args; + + dropRightWhile(array, function () { + args = slice.call(arguments); + }); + + expect(args).toEqual([4, 3, array]); + }); + + // FIXME: Perhaps dropRightWhile semantic changes. + // it('should work with `_.matches` shorthands', () => { + // expect(dropRightWhile(objects, { b: 2 })).toEqual(objects.slice(0, 2)); + // }); + // + // it('should work with `_.matchesProperty` shorthands', () => { + // expect(dropRightWhile(objects, ['b', 2])).toEqual(objects.slice(0, 2)); + // }); + // + // it('should work with `_.property` shorthands', () => { + // expect(dropRightWhile(objects, 'b')).toEqual(objects.slice(0, 1)); + // }); + + // FIXME: Work out a solution for _. + // + // it('should return a wrapped value when chaining', () => { + // const wrapped = _(array).dropRightWhile((n) => n > 2); + // + // expect(wrapped instanceof _); + // expect(wrapped.value()).toEqual([1, 2]); + // }); +}); diff --git a/test/dropWhile.js b/test/dropWhile.js deleted file mode 100644 index f02088104a..0000000000 --- a/test/dropWhile.js +++ /dev/null @@ -1,67 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, LARGE_ARRAY_SIZE } from './utils.js'; -import dropWhile from '../dropWhile.js'; - -describe('dropWhile', function() { - var array = [1, 2, 3, 4]; - - var objects = [ - { 'a': 2, 'b': 2 }, - { 'a': 1, 'b': 1 }, - { 'a': 0, 'b': 0 } - ]; - - it('should drop elements while `predicate` returns truthy', function() { - var actual = dropWhile(array, function(n) { - return n < 3; - }); - - assert.deepStrictEqual(actual, [3, 4]); - }); - - it('should provide correct `predicate` arguments', function() { - var args; - - dropWhile(array, function() { - args = slice.call(arguments); - }); - - assert.deepStrictEqual(args, [1, 0, array]); - }); - - it('should work with `_.matches` shorthands', function() { - assert.deepStrictEqual(dropWhile(objects, { 'b': 2 }), objects.slice(1)); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - assert.deepStrictEqual(dropWhile(objects, ['b', 2]), objects.slice(1)); - }); - - it('should work with `_.property` shorthands', function() { - assert.deepStrictEqual(dropWhile(objects, 'b'), objects.slice(2)); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(1, LARGE_ARRAY_SIZE + 3), - predicate = function(n) { return n < 3; }, - expected = dropWhile(array, predicate), - wrapped = _(array).dropWhile(predicate); - - assert.deepEqual(wrapped.value(), expected); - assert.deepEqual(wrapped.reverse().value(), expected.slice().reverse()); - assert.strictEqual(wrapped.last(), _.last(expected)); - }); - - it('should work in a lazy sequence with `drop`', function() { - var array = lodashStable.range(1, LARGE_ARRAY_SIZE + 3); - - var actual = _(array) - .dropWhile(function(n) { return n == 1; }) - .drop() - .dropWhile(function(n) { return n == 3; }) - .value(); - - assert.deepEqual(actual, array.slice(3)); - }); -}); diff --git a/test/dropWhile.spec.js b/test/dropWhile.spec.js new file mode 100644 index 0000000000..b86dd6cbe7 --- /dev/null +++ b/test/dropWhile.spec.js @@ -0,0 +1,42 @@ +import { slice } from './utils'; +import dropWhile from '../src/dropWhile'; + +describe('dropWhile', () => { + const array = [1, 2, 3, 4]; + + // const objects = [ + // { a: 2, b: 2 }, + // { a: 1, b: 1 }, + // { a: 0, b: 0 }, + // ]; + + it('should drop elements while `predicate` returns truthy', () => { + const actual = dropWhile(array, (n) => n < 3); + + expect(actual).toEqual([3, 4]); + }); + + it('should provide correct `predicate` arguments', () => { + let args; + + dropWhile(array, function () { + args = slice.call(arguments); + }); + + expect(args).toEqual([1, 0, array]); + }); + + // FIXME: Perhaps dropWhile semantic changes. + // + // it('should work with `_.matches` shorthands', () => { + // expect(dropWhile(objects, { b: 2 })).toEqual(objects.slice(1)); + // }); + // + // it('should work with `_.matchesProperty` shorthands', () => { + // expect(dropWhile(objects, ['b', 2])).toEqual(objects.slice(1)); + // }); + // + // it('should work with `_.property` shorthands', () => { + // expect(dropWhile(objects, 'b')).toEqual(objects.slice(2)); + // }); +}); diff --git a/test/endsWith.spec.js b/test/endsWith.spec.js new file mode 100644 index 0000000000..9840d4ef7b --- /dev/null +++ b/test/endsWith.spec.js @@ -0,0 +1,46 @@ +import lodashStable from 'lodash'; +import { MAX_SAFE_INTEGER, falsey, stubTrue } from './utils'; +import endsWith from '../src/endsWith'; + +describe('endsWith', () => { + const string = 'abc'; + + it('should return `true` if a string ends with `target`', () => { + expect(endsWith(string, 'c')).toBe(true); + }); + + it('should return `false` if a string does not end with `target`', () => { + expect(endsWith(string, 'b')).toBe(false); + }); + + it('should work with a `position`', () => { + expect(endsWith(string, 'b', 2)).toBe(true); + }); + + it('should work with `position` >= `length`', () => { + lodashStable.each([3, 5, MAX_SAFE_INTEGER, Infinity], (position) => { + expect(endsWith(string, 'c', position)).toBe(true); + }); + }); + + it('should treat falsey `position` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, stubTrue); + + const actual = lodashStable.map(falsey, (position) => + endsWith(string, position === undefined ? 'c' : '', position), + ); + + expect(actual).toEqual(expected); + }); + + it('should treat a negative `position` as `0`', () => { + lodashStable.each([-1, -3, -Infinity], (position) => { + expect(lodashStable.every(string, (chr) => !endsWith(string, chr, position))); + expect(endsWith(string, '', position)).toBe(true); + }); + }); + + it('should coerce `position` to an integer', () => { + expect(endsWith(string, 'ab', 2.2)).toBe(true); + }); +}); diff --git a/test/endsWith.test.js b/test/endsWith.test.js deleted file mode 100644 index ceb9dddd45..0000000000 --- a/test/endsWith.test.js +++ /dev/null @@ -1,49 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { MAX_SAFE_INTEGER, falsey, stubTrue } from './utils.js'; -import endsWith from '../endsWith.js'; - -describe('endsWith', function() { - var string = 'abc'; - - it('should return `true` if a string ends with `target`', function() { - assert.strictEqual(endsWith(string, 'c'), true); - }); - - it('should return `false` if a string does not end with `target`', function() { - assert.strictEqual(endsWith(string, 'b'), false); - }); - - it('should work with a `position`', function() { - assert.strictEqual(endsWith(string, 'b', 2), true); - }); - - it('should work with `position` >= `length`', function() { - lodashStable.each([3, 5, MAX_SAFE_INTEGER, Infinity], function(position) { - assert.strictEqual(endsWith(string, 'c', position), true); - }); - }); - - it('should treat falsey `position` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(position) { - return endsWith(string, position === undefined ? 'c' : '', position); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should treat a negative `position` as `0`', function() { - lodashStable.each([-1, -3, -Infinity], function(position) { - assert.ok(lodashStable.every(string, function(chr) { - return !endsWith(string, chr, position); - })); - assert.strictEqual(endsWith(string, '', position), true); - }); - }); - - it('should coerce `position` to an integer', function() { - assert.strictEqual(endsWith(string, 'ab', 2.2), true); - }); -}); diff --git a/test/eq.spec.js b/test/eq.spec.js new file mode 100644 index 0000000000..ae6be5fbeb --- /dev/null +++ b/test/eq.spec.js @@ -0,0 +1,20 @@ +import eq from '../src/eq'; + +describe('eq', () => { + it('should perform a `SameValueZero` comparison of two values', () => { + expect(eq()).toBe(true); + expect(eq(undefined)).toBe(true); + expect(eq(0, -0)).toBe(true); + expect(eq(NaN, NaN)).toBe(true); + expect(eq(1, 1)).toBe(true); + + expect(eq(null, undefined)).toBe(false); + expect(eq(1, Object(1))).toBe(false); + expect(eq(1, '1')).toBe(false); + expect(eq(1, '1')).toBe(false); + + const object = { a: 1 }; + expect(eq(object, object)).toBe(true); + expect(eq(object, { a: 1 })).toBe(false); + }); +}); diff --git a/test/eq.test.js b/test/eq.test.js deleted file mode 100644 index ec0c7adadb..0000000000 --- a/test/eq.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'assert'; -import eq from '../eq.js'; - -describe('eq', function() { - it('should perform a `SameValueZero` comparison of two values', function() { - assert.strictEqual(eq(), true); - assert.strictEqual(eq(undefined), true); - assert.strictEqual(eq(0, -0), true); - assert.strictEqual(eq(NaN, NaN), true); - assert.strictEqual(eq(1, 1), true); - - assert.strictEqual(eq(null, undefined), false); - assert.strictEqual(eq(1, Object(1)), false); - assert.strictEqual(eq(1, '1'), false); - assert.strictEqual(eq(1, '1'), false); - - var object = { 'a': 1 }; - assert.strictEqual(eq(object, object), true); - assert.strictEqual(eq(object, { 'a': 1 }), false); - }); -}); diff --git a/test/escape.spec.js b/test/escape.spec.js new file mode 100644 index 0000000000..a1f99ece53 --- /dev/null +++ b/test/escape.spec.js @@ -0,0 +1,29 @@ +import lodashStable from 'lodash'; +import escape from '../src/escape'; +import unescape from '../src/unescape'; + +describe('escape', () => { + let escaped = '&<>"'/'; + let unescaped = '&<>"\'/'; + + escaped += escaped; + unescaped += unescaped; + + it('should escape values', () => { + expect(escape(unescaped)).toBe(escaped); + }); + + it('should handle strings with nothing to escape', () => { + expect(escape('abc')).toBe('abc'); + }); + + it('should escape the same characters unescaped by `_.unescape`', () => { + expect(escape(unescape(escaped))).toBe(escaped); + }); + + lodashStable.each(['`', '/'], (chr) => { + it(`should not escape the "${chr}" character`, () => { + expect(escape(chr)).toBe(chr); + }); + }); +}); diff --git a/test/escape.test.js b/test/escape.test.js deleted file mode 100644 index f5b7dbabad..0000000000 --- a/test/escape.test.js +++ /dev/null @@ -1,30 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import escape from '../escape.js'; -import unescape from '../unescape.js'; - -describe('escape', function() { - var escaped = '&<>"'/', - unescaped = '&<>"\'/'; - - escaped += escaped; - unescaped += unescaped; - - it('should escape values', function() { - assert.strictEqual(escape(unescaped), escaped); - }); - - it('should handle strings with nothing to escape', function() { - assert.strictEqual(escape('abc'), 'abc'); - }); - - it('should escape the same characters unescaped by `_.unescape`', function() { - assert.strictEqual(escape(unescape(escaped)), escaped); - }); - - lodashStable.each(['`', '/'], function(chr) { - it('should not escape the "' + chr + '" character', function() { - assert.strictEqual(escape(chr), chr); - }); - }); -}); diff --git a/test/escapeRegExp.spec.js b/test/escapeRegExp.spec.js new file mode 100644 index 0000000000..ca737c69ce --- /dev/null +++ b/test/escapeRegExp.spec.js @@ -0,0 +1,27 @@ +import lodashStable from 'lodash'; +import { stubString } from './utils'; +import escapeRegExp from '../src/escapeRegExp'; + +describe('escapeRegExp', () => { + const escaped = '\\^\\$\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\\\'; + const unescaped = '^$.*+?()[]{}|\\'; + + it('should escape values', () => { + expect(escapeRegExp(unescaped + unescaped)).toBe(escaped + escaped); + }); + + it('should handle strings with nothing to escape', () => { + expect(escapeRegExp('abc')).toBe('abc'); + }); + + it('should return an empty string for empty values', () => { + const values = [, null, undefined, '']; + const expected = lodashStable.map(values, stubString); + + const actual = lodashStable.map(values, (value, index) => + index ? escapeRegExp(value) : escapeRegExp(), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/escapeRegExp.test.js b/test/escapeRegExp.test.js deleted file mode 100644 index 3fa7062747..0000000000 --- a/test/escapeRegExp.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubString } from './utils.js'; -import escapeRegExp from '../escapeRegExp.js'; - -describe('escapeRegExp', function() { - var escaped = '\\^\\$\\.\\*\\+\\?\\(\\)\\[\\]\\{\\}\\|\\\\', - unescaped = '^$.*+?()[]{}|\\'; - - it('should escape values', function() { - assert.strictEqual(escapeRegExp(unescaped + unescaped), escaped + escaped); - }); - - it('should handle strings with nothing to escape', function() { - assert.strictEqual(escapeRegExp('abc'), 'abc'); - }); - - it('should return an empty string for empty values', function() { - var values = [, null, undefined, ''], - expected = lodashStable.map(values, stubString); - - var actual = lodashStable.map(values, function(value, index) { - return index ? escapeRegExp(value) : escapeRegExp(); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/every.js b/test/every.js deleted file mode 100644 index 76052aa9f3..0000000000 --- a/test/every.js +++ /dev/null @@ -1,74 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, empties, stubTrue, stubFalse } from './utils.js'; -import every from '../every.js'; - -describe('every', function() { - it('should return `true` if `predicate` returns truthy for all elements', function() { - assert.strictEqual(lodashStable.every([true, 1, 'a'], identity), true); - }); - - it('should return `true` for empty collections', function() { - var expected = lodashStable.map(empties, stubTrue); - - var actual = lodashStable.map(empties, function(value) { - try { - return every(value, identity); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` as soon as `predicate` returns falsey', function() { - var count = 0; - - assert.strictEqual(every([true, null, true], function(value) { - count++; - return value; - }), false); - - assert.strictEqual(count, 2); - }); - - it('should work with collections of `undefined` values (test in IE < 9)', function() { - assert.strictEqual(every([undefined, undefined, undefined], identity), false); - }); - - it('should use `_.identity` when `predicate` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value, index) { - var array = [0]; - return index ? every(array, value) : every(array); - }); - - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(values, stubTrue); - actual = lodashStable.map(values, function(value, index) { - var array = [1]; - return index ? every(array, value) : every(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var objects = [{ 'a': 0, 'b': 1 }, { 'a': 1, 'b': 2 }]; - assert.strictEqual(every(objects, 'a'), false); - assert.strictEqual(every(objects, 'b'), true); - }); - - it('should work with `_.matches` shorthands', function() { - var objects = [{ 'a': 0, 'b': 0 }, { 'a': 0, 'b': 1 }]; - assert.strictEqual(every(objects, { 'a': 0 }), true); - assert.strictEqual(every(objects, { 'b': 1 }), false); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var actual = lodashStable.map([[1]], every); - assert.deepStrictEqual(actual, [true]); - }); -}); diff --git a/test/every.spec.js b/test/every.spec.js new file mode 100644 index 0000000000..5349d6ff85 --- /dev/null +++ b/test/every.spec.js @@ -0,0 +1,82 @@ +import lodashStable from 'lodash'; +import { identity, empties, stubTrue, stubFalse } from './utils'; +import every from '../src/every'; + +describe('every', () => { + it('should return `true` if `predicate` returns truthy for all elements', () => { + expect(lodashStable.every([true, 1, 'a'], identity)).toBe(true); + }); + + it('should return `true` for empty collections', () => { + const expected = lodashStable.map(empties, stubTrue); + + const actual = lodashStable.map(empties, (value) => { + try { + return every(value, identity); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should return `false` as soon as `predicate` returns falsey', () => { + let count = 0; + + assert.strictEqual( + every([true, null, true], (value) => { + count++; + return value; + }), + false, + ); + + expect(count).toBe(2); + }); + + it('should work with collections of `undefined` values (test in IE < 9)', () => { + expect(every([undefined, undefined, undefined], identity)).toBe(false); + }); + + it('should use `_.identity` when `predicate` is nullish', () => { + const values = [, null, undefined]; + let expected = lodashStable.map(values, stubFalse); + + let actual = lodashStable.map(values, (value, index) => { + const array = [0]; + return index ? every(array, value) : every(array); + }); + + expect(actual).toEqual(expected); + + expected = lodashStable.map(values, stubTrue); + actual = lodashStable.map(values, (value, index) => { + const array = [1]; + return index ? every(array, value) : every(array); + }); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const objects = [ + { a: 0, b: 1 }, + { a: 1, b: 2 }, + ]; + expect(every(objects, 'a')).toBe(false); + expect(every(objects, 'b')).toBe(true); + }); + + it('should work with `_.matches` shorthands', () => { + const objects = [ + { a: 0, b: 0 }, + { a: 0, b: 1 }, + ]; + expect(every(objects, { a: 0 })).toBe(true); + expect(every(objects, { b: 1 })).toBe(false); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const actual = lodashStable.map([[1]], every); + expect(actual).toEqual([true]); + }); +}); diff --git a/test/exit-early.js b/test/exit-early.js deleted file mode 100644 index 090d6c8939..0000000000 --- a/test/exit-early.js +++ /dev/null @@ -1,37 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('exit early', function() { - lodashStable.each(['_baseEach', 'forEach', 'forEachRight', 'forIn', 'forInRight', 'forOwn', 'forOwnRight', 'transform'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` can exit early when iterating arrays', function() { - if (func) { - var array = [1, 2, 3], - values = []; - - func(array, function(value, other) { - values.push(lodashStable.isArray(value) ? other : value); - return false; - }); - - assert.deepStrictEqual(values, [lodashStable.endsWith(methodName, 'Right') ? 3 : 1]); - } - }); - - it('`_.' + methodName + '` can exit early when iterating objects', function() { - if (func) { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - values = []; - - func(object, function(value, other) { - values.push(lodashStable.isArray(value) ? other : value); - return false; - }); - - assert.strictEqual(values.length, 1); - } - }); - }); -}); diff --git a/test/exit-early.spec.js b/test/exit-early.spec.js new file mode 100644 index 0000000000..8160f85bab --- /dev/null +++ b/test/exit-early.spec.js @@ -0,0 +1,48 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('exit early', () => { + lodashStable.each( + [ + '_baseEach', + 'forEach', + 'forEachRight', + 'forIn', + 'forInRight', + 'forOwn', + 'forOwnRight', + 'transform', + ], + (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` can exit early when iterating arrays`, () => { + if (func) { + const array = [1, 2, 3]; + const values = []; + + func(array, (value, other) => { + values.push(lodashStable.isArray(value) ? other : value); + return false; + }); + + expect(values).toEqual([lodashStable.endsWith(methodName, 'Right') ? 3 : 1]); + } + }); + + it(`\`_.${methodName}\` can exit early when iterating objects`, () => { + if (func) { + const object = { a: 1, b: 2, c: 3 }; + const values = []; + + func(object, (value, other) => { + values.push(lodashStable.isArray(value) ? other : value); + return false; + }); + + expect(values.length).toBe(1); + } + }); + }, + ); +}); diff --git a/test/extremum-methods.js b/test/extremum-methods.js deleted file mode 100644 index 6dad236bd0..0000000000 --- a/test/extremum-methods.js +++ /dev/null @@ -1,64 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('extremum methods', function() { - lodashStable.each(['max', 'maxBy', 'min', 'minBy'], function(methodName) { - var func = _[methodName], - isMax = /^max/.test(methodName); - - it('`_.' + methodName + '` should work with Date objects', function() { - var curr = new Date, - past = new Date(0); - - assert.strictEqual(func([curr, past]), isMax ? curr : past); - }); - - it('`_.' + methodName + '` should work with extremely large arrays', function() { - var array = lodashStable.range(0, 5e5); - assert.strictEqual(func(array), isMax ? 499999 : 0); - }); - - it('`_.' + methodName + '` should work when chaining on an array with only one value', function() { - var actual = _([40])[methodName](); - assert.strictEqual(actual, 40); - }); - }); - - lodashStable.each(['maxBy', 'minBy'], function(methodName) { - var array = [1, 2, 3], - func = _[methodName], - isMax = methodName == 'maxBy'; - - it('`_.' + methodName + '` should work with an `iteratee`', function() { - var actual = func(array, function(n) { - return -n; - }); - - assert.strictEqual(actual, isMax ? 1 : 3); - }); - - it('should work with `_.property` shorthands', function() { - var objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }], - actual = func(objects, 'a'); - - assert.deepStrictEqual(actual, objects[isMax ? 1 : 2]); - - var arrays = [[2], [3], [1]]; - actual = func(arrays, 0); - - assert.deepStrictEqual(actual, arrays[isMax ? 1 : 2]); - }); - - it('`_.' + methodName + '` should work when `iteratee` returns +/-Infinity', function() { - var value = isMax ? -Infinity : Infinity, - object = { 'a': value }; - - var actual = func([object, { 'a': value }], function(object) { - return object.a; - }); - - assert.strictEqual(actual, object); - }); - }); -}); diff --git a/test/extremum-methods.spec.js b/test/extremum-methods.spec.js new file mode 100644 index 0000000000..15031ab540 --- /dev/null +++ b/test/extremum-methods.spec.js @@ -0,0 +1,59 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('extremum methods', () => { + lodashStable.each(['max', 'maxBy', 'min', 'minBy'], (methodName) => { + const func = _[methodName]; + const isMax = /^max/.test(methodName); + + it(`\`_.${methodName}\` should work with Date objects`, () => { + const curr = new Date(); + const past = new Date(0); + + expect(func([curr, past])).toBe(isMax ? curr : past); + }); + + it(`\`_.${methodName}\` should work with extremely large arrays`, () => { + const array = lodashStable.range(0, 5e5); + expect(func(array)).toBe(isMax ? 499999 : 0); + }); + + it(`\`_.${methodName}\` should work when chaining on an array with only one value`, () => { + const actual = _([40])[methodName](); + expect(actual).toBe(40); + }); + }); + + lodashStable.each(['maxBy', 'minBy'], (methodName) => { + const array = [1, 2, 3]; + const func = _[methodName]; + const isMax = methodName === 'maxBy'; + + it(`\`_.${methodName}\` should work with an \`iteratee\``, () => { + const actual = func(array, (n) => -n); + + expect(actual).toBe(isMax ? 1 : 3); + }); + + it('should work with `_.property` shorthands', () => { + const objects = [{ a: 2 }, { a: 3 }, { a: 1 }]; + let actual = func(objects, 'a'); + + expect(actual).toEqual(objects[isMax ? 1 : 2]); + + const arrays = [[2], [3], [1]]; + actual = func(arrays, 0); + + expect(actual).toEqual(arrays[isMax ? 1 : 2]); + }); + + it(`\`_.${methodName}\` should work when \`iteratee\` returns +/-Infinity`, () => { + const value = isMax ? -Infinity : Infinity; + const object = { a: value }; + + const actual = func([object, { a: value }], (object) => object.a); + + expect(actual).toBe(object); + }); + }); +}); diff --git a/test/fill.js b/test/fill.js deleted file mode 100644 index 518e368a7b..0000000000 --- a/test/fill.js +++ /dev/null @@ -1,128 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey } from './utils.js'; -import fill from '../fill.js'; - -describe('fill', function() { - it('should use a default `start` of `0` and a default `end` of `length`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a'), ['a', 'a', 'a']); - }); - - it('should use `undefined` for `value` if not given', function() { - var array = [1, 2, 3], - actual = fill(array); - - assert.deepStrictEqual(actual, Array(3)); - assert.ok(lodashStable.every(actual, function(value, index) { - return index in actual; - })); - }); - - it('should work with a positive `start`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', 1), [1, 'a', 'a']); - }); - - it('should work with a `start` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(start) { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', start), [1, 2, 3]); - }); - }); - - it('should treat falsey `start` values as `0`', function() { - var expected = lodashStable.map(falsey, lodashStable.constant(['a', 'a', 'a'])); - - var actual = lodashStable.map(falsey, function(start) { - var array = [1, 2, 3]; - return fill(array, 'a', start); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with a negative `start`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', -1), [1, 2, 'a']); - }); - - it('should work with a negative `start` <= negative `length`', function() { - lodashStable.each([-3, -4, -Infinity], function(start) { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', start), ['a', 'a', 'a']); - }); - }); - - it('should work with `start` >= `end`', function() { - lodashStable.each([2, 3], function(start) { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', start, 2), [1, 2, 3]); - }); - }); - - it('should work with a positive `end`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', 0, 1), ['a', 2, 3]); - }); - - it('should work with a `end` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(end) { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', 0, end), ['a', 'a', 'a']); - }); - }); - - it('should treat falsey `end` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? ['a', 'a', 'a'] : [1, 2, 3]; - }); - - var actual = lodashStable.map(falsey, function(end) { - var array = [1, 2, 3]; - return fill(array, 'a', 0, end); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with a negative `end`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', 0, -1), ['a', 'a', 3]); - }); - - it('should work with a negative `end` <= negative `length`', function() { - lodashStable.each([-3, -4, -Infinity], function(end) { - var array = [1, 2, 3]; - assert.deepStrictEqual(fill(array, 'a', 0, end), [1, 2, 3]); - }); - }); - - it('should coerce `start` and `end` to integers', function() { - var positions = [[0.1, 1.6], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; - - var actual = lodashStable.map(positions, function(pos) { - var array = [1, 2, 3]; - return fill.apply(_, [array, 'a'].concat(pos)); - }); - - assert.deepStrictEqual(actual, [['a', 2, 3], ['a', 2, 3], ['a', 2, 3], [1, 'a', 'a'], ['a', 2, 3], [1, 2, 3]]); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2], [3, 4]], - actual = lodashStable.map(array, fill); - - assert.deepStrictEqual(actual, [[0, 0], [1, 1]]); - }); - - it('should return a wrapped value when chaining', function() { - var array = [1, 2, 3], - wrapped = _(array).fill('a'), - actual = wrapped.value(); - - assert.ok(wrapped instanceof _); - assert.strictEqual(actual, array); - assert.deepEqual(actual, ['a', 'a', 'a']); - }); -}); diff --git a/test/fill.spec.js b/test/fill.spec.js new file mode 100644 index 0000000000..b306c347ef --- /dev/null +++ b/test/fill.spec.js @@ -0,0 +1,138 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import fill from '../src/fill'; + +describe('fill', () => { + it('should use a default `start` of `0` and a default `end` of `length`', () => { + const array = [1, 2, 3]; + expect(fill(array, 'a'), ['a', 'a').toEqual('a']); + }); + + it('should use `undefined` for `value` if not given', () => { + const array = [1, 2, 3]; + const actual = fill(array); + + expect(actual).toEqual(Array(3)); + expect(lodashStable.every(actual, (value, index) => index in actual)) + }); + + it('should work with a positive `start`', () => { + const array = [1, 2, 3]; + expect(fill(array, 'a', 1), [1, 'a').toEqual('a']); + }); + + it('should work with a `start` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (start) => { + const array = [1, 2, 3]; + expect(fill(array, 'a', start), [1, 2).toEqual(3]); + }); + }); + + it('should treat falsey `start` values as `0`', () => { + const expected = lodashStable.map(falsey, lodashStable.constant(['a', 'a', 'a'])); + + const actual = lodashStable.map(falsey, (start) => { + const array = [1, 2, 3]; + return fill(array, 'a', start); + }); + + expect(actual).toEqual(expected); + }); + + it('should work with a negative `start`', () => { + const array = [1, 2, 3]; + expect(fill(array, 'a', -1), [1, 2).toEqual('a']); + }); + + it('should work with a negative `start` <= negative `length`', () => { + lodashStable.each([-3, -4, -Infinity], (start) => { + const array = [1, 2, 3]; + expect(fill(array, 'a', start), ['a', 'a').toEqual('a']); + }); + }); + + it('should work with `start` >= `end`', () => { + lodashStable.each([2, 3], (start) => { + const array = [1, 2, 3]; + expect(fill(array, 'a', start, 2), [1, 2).toEqual(3]); + }); + }); + + it('should work with a positive `end`', () => { + const array = [1, 2, 3]; + expect(fill(array, 'a', 0, 1), ['a', 2).toEqual(3]); + }); + + it('should work with a `end` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (end) => { + const array = [1, 2, 3]; + expect(fill(array, 'a', 0, end), ['a', 'a').toEqual('a']); + }); + }); + + it('should treat falsey `end` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => + value === undefined ? ['a', 'a', 'a'] : [1, 2, 3], + ); + + const actual = lodashStable.map(falsey, (end) => { + const array = [1, 2, 3]; + return fill(array, 'a', 0, end); + }); + + expect(actual).toEqual(expected); + }); + + it('should work with a negative `end`', () => { + const array = [1, 2, 3]; + expect(fill(array, 'a', 0, -1), ['a', 'a').toEqual(3]); + }); + + it('should work with a negative `end` <= negative `length`', () => { + lodashStable.each([-3, -4, -Infinity], (end) => { + const array = [1, 2, 3]; + expect(fill(array, 'a', 0, end), [1, 2).toEqual(3]); + }); + }); + + it('should coerce `start` and `end` to integers', () => { + const positions = [[0.1, 1.6], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; + + const actual = lodashStable.map(positions, (pos) => { + const array = [1, 2, 3]; + return fill.apply(_, [array, 'a'].concat(pos)); + }); + + assert.deepStrictEqual(actual, [ + ['a', 2, 3], + ['a', 2, 3], + ['a', 2, 3], + [1, 'a', 'a'], + ['a', 2, 3], + [1, 2, 3], + ]); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2], + [3, 4], + ]; + const actual = lodashStable.map(array, fill); + + assert.deepStrictEqual(actual, [ + [0, 0], + [1, 1], + ]); + }); + + it('should return a wrapped value when chaining', () => { + const array = [1, 2, 3]; + const wrapped = _(array).fill('a'); + const actual = wrapped.value(); + + expect(wrapped instanceof _) + expect(actual).toBe(array); + expect(actual, ['a', 'a').toEqual('a']); + }); +}); diff --git a/test/filter-methods.js b/test/filter-methods.js deleted file mode 100644 index 2e98afc094..0000000000 --- a/test/filter-methods.js +++ /dev/null @@ -1,100 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, LARGE_ARRAY_SIZE, isEven, square } from './utils.js'; - -describe('filter methods', function() { - lodashStable.each(['filter', 'reject'], function(methodName) { - var array = [1, 2, 3, 4], - func = _[methodName], - isFilter = methodName == 'filter', - objects = [{ 'a': 0 }, { 'a': 1 }]; - - it('`_.' + methodName + '` should not modify the resulting value from within `predicate`', function() { - var actual = func([0], function(value, index, array) { - array[index] = 1; - return isFilter; - }); - - assert.deepStrictEqual(actual, [0]); - }); - - it('`_.' + methodName + '` should work with `_.property` shorthands', function() { - assert.deepStrictEqual(func(objects, 'a'), [objects[isFilter ? 1 : 0]]); - }); - - it('`_.' + methodName + '` should work with `_.matches` shorthands', function() { - assert.deepStrictEqual(func(objects, objects[1]), [objects[isFilter ? 1 : 0]]); - }); - - it('`_.' + methodName + '` should not modify wrapped values', function() { - var wrapped = _(array); - - var actual = wrapped[methodName](function(n) { - return n < 3; - }); - - assert.deepEqual(actual.value(), isFilter ? [1, 2] : [3, 4]); - - actual = wrapped[methodName](function(n) { - return n > 2; - }); - - assert.deepEqual(actual.value(), isFilter ? [3, 4] : [1, 2]); - }); - - it('`_.' + methodName + '` should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE + 1), - predicate = function(value) { return isFilter ? isEven(value) : !isEven(value); }; - - var object = lodashStable.zipObject(lodashStable.times(LARGE_ARRAY_SIZE, function(index) { - return ['key' + index, index]; - })); - - var actual = _(array).slice(1).map(square)[methodName](predicate).value(); - assert.deepEqual(actual, _[methodName](lodashStable.map(array.slice(1), square), predicate)); - - actual = _(object).mapValues(square)[methodName](predicate).value(); - assert.deepEqual(actual, _[methodName](lodashStable.mapValues(object, square), predicate)); - }); - - it('`_.' + methodName + '` should provide correct `predicate` arguments in a lazy sequence', function() { - var args, - array = lodashStable.range(LARGE_ARRAY_SIZE + 1), - expected = [1, 0, lodashStable.map(array.slice(1), square)]; - - _(array).slice(1)[methodName](function(value, index, array) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, [1, 0, array.slice(1)]); - - args = undefined; - _(array).slice(1).map(square)[methodName](function(value, index, array) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, expected); - - args = undefined; - _(array).slice(1).map(square)[methodName](function(value, index) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, expected); - - args = undefined; - _(array).slice(1).map(square)[methodName](function(value) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, [1]); - - args = undefined; - _(array).slice(1).map(square)[methodName](function() { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, expected); - }); - }); -}); diff --git a/test/filter-methods.spec.js b/test/filter-methods.spec.js new file mode 100644 index 0000000000..583d340356 --- /dev/null +++ b/test/filter-methods.spec.js @@ -0,0 +1,122 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, isEven, square } from './utils'; + +describe('filter methods', () => { + lodashStable.each(['filter', 'reject'], (methodName) => { + const array = [1, 2, 3, 4]; + const func = _[methodName]; + const isFilter = methodName === 'filter'; + const objects = [{ a: 0 }, { a: 1 }]; + + it(`\`_.${methodName}\` should not modify the resulting value from within \`predicate\``, () => { + const actual = func([0], (value, index, array) => { + array[index] = 1; + return isFilter; + }); + + expect(actual).toEqual([0]); + }); + + it(`\`_.${methodName}\` should work with \`_.property\` shorthands`, () => { + expect(func(objects, 'a')).toEqual([objects[isFilter ? 1 : 0]]); + }); + + it(`\`_.${methodName}\` should work with \`_.matches\` shorthands`, () => { + expect(func(objects, objects[1])).toEqual([objects[isFilter ? 1 : 0]]); + }); + + it(`\`_.${methodName}\` should not modify wrapped values`, () => { + const wrapped = _(array); + + let actual = wrapped[methodName]((n) => n < 3); + + expect(actual.value(), isFilter ? [1, 2] : [3).toEqual(4]); + + actual = wrapped[methodName]((n) => n > 2); + + expect(actual.value(), isFilter ? [3, 4] : [1).toEqual(2]); + }); + + it(`\`_.${methodName}\` should work in a lazy sequence`, () => { + const array = lodashStable.range(LARGE_ARRAY_SIZE + 1); + const predicate = function (value) { + return isFilter ? isEven(value) : !isEven(value); + }; + + const object = lodashStable.zipObject( + lodashStable.times(LARGE_ARRAY_SIZE, (index) => [`key${index}`, index]), + ); + + let actual = _(array).slice(1).map(square)[methodName](predicate).value(); + assert.deepEqual( + actual, + _[methodName](lodashStable.map(array.slice(1), square), predicate), + ); + + actual = _(object).mapValues(square)[methodName](predicate).value(); + assert.deepEqual( + actual, + _[methodName](lodashStable.mapValues(object, square), predicate), + ); + }); + + it(`\`_.${methodName}\` should provide correct \`predicate\` arguments in a lazy sequence`, () => { + let args; + const array = lodashStable.range(LARGE_ARRAY_SIZE + 1); + const expected = [1, 0, lodashStable.map(array.slice(1), square)]; + + _(array) + .slice(1) + [methodName](function (value, index, array) { + args || (args = slice.call(arguments)); + }) + .value(); + + expect(args, [1, 0).toEqual(array.slice(1)]); + + args = undefined; + _(array) + .slice(1) + .map(square) + [methodName](function (value, index, array) { + args || (args = slice.call(arguments)); + }) + .value(); + + expect(args).toEqual(expected); + + args = undefined; + _(array) + .slice(1) + .map(square) + [methodName](function (value, index) { + args || (args = slice.call(arguments)); + }) + .value(); + + expect(args).toEqual(expected); + + args = undefined; + _(array) + .slice(1) + .map(square) + [methodName](function (value) { + args || (args = slice.call(arguments)); + }) + .value(); + + expect(args).toEqual([1]); + + args = undefined; + _(array) + .slice(1) + .map(square) + [methodName](function () { + args || (args = slice.call(arguments)); + }) + .value(); + + expect(args).toEqual(expected); + }); + }); +}); diff --git a/test/filter.spec.js b/test/filter.spec.js new file mode 100644 index 0000000000..863a62fe1f --- /dev/null +++ b/test/filter.spec.js @@ -0,0 +1,10 @@ +import { isEven } from './utils'; +import filter from '../src/filter'; + +describe('filter', () => { + const array = [1, 2, 3]; + + it('should return elements `predicate` returns truthy for', () => { + expect(filter(array, isEven)).toEqual([2]); + }); +}); diff --git a/test/filter.test.js b/test/filter.test.js deleted file mode 100644 index cff7d25558..0000000000 --- a/test/filter.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import assert from 'assert'; -import { isEven } from './utils.js'; -import filter from '../filter.js'; - -describe('filter', function() { - var array = [1, 2, 3]; - - it('should return elements `predicate` returns truthy for', function() { - assert.deepStrictEqual(filter(array, isEven), [2]); - }); -}); diff --git a/test/find-and-findLast.js b/test/find-and-findLast.js deleted file mode 100644 index e55725e97d..0000000000 --- a/test/find-and-findLast.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, square, isEven } from './utils.js'; - -describe('find and findLast', function() { - lodashStable.each(['find', 'findLast'], function(methodName) { - var isFind = methodName == 'find'; - - it('`_.' + methodName + '` should support shortcut fusion', function() { - var findCount = 0, - mapCount = 0, - array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1), - iteratee = function(value) { mapCount++; return square(value); }, - predicate = function(value) { findCount++; return isEven(value); }, - actual = _(array).map(iteratee)[methodName](predicate); - - assert.strictEqual(findCount, isFind ? 2 : 1); - assert.strictEqual(mapCount, isFind ? 2 : 1); - assert.strictEqual(actual, isFind ? 4 : square(LARGE_ARRAY_SIZE)); - }); - }); -}); diff --git a/test/find-and-findLast.spec.js b/test/find-and-findLast.spec.js new file mode 100644 index 0000000000..42def6d7e1 --- /dev/null +++ b/test/find-and-findLast.spec.js @@ -0,0 +1,28 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, square, isEven } from './utils'; + +describe('find and findLast', () => { + lodashStable.each(['find', 'findLast'], (methodName) => { + const isFind = methodName === 'find'; + + it(`\`_.${methodName}\` should support shortcut fusion`, () => { + let findCount = 0; + let mapCount = 0; + const array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1); + + const iteratee = function (value) { + mapCount++; + return square(value); + }; + const predicate = function (value) { + findCount++; + return isEven(value); + }; + const actual = _(array).map(iteratee)[methodName](predicate); + + expect(findCount).toBe(isFind ? 2 : 1); + expect(mapCount).toBe(isFind ? 2 : 1); + expect(actual).toBe(isFind ? 4 : square(LARGE_ARRAY_SIZE)); + }); + }); +}); diff --git a/test/find-and-findLast.spec.ts b/test/find-and-findLast.spec.ts new file mode 100644 index 0000000000..42def6d7e1 --- /dev/null +++ b/test/find-and-findLast.spec.ts @@ -0,0 +1,28 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, square, isEven } from './utils'; + +describe('find and findLast', () => { + lodashStable.each(['find', 'findLast'], (methodName) => { + const isFind = methodName === 'find'; + + it(`\`_.${methodName}\` should support shortcut fusion`, () => { + let findCount = 0; + let mapCount = 0; + const array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1); + + const iteratee = function (value) { + mapCount++; + return square(value); + }; + const predicate = function (value) { + findCount++; + return isEven(value); + }; + const actual = _(array).map(iteratee)[methodName](predicate); + + expect(findCount).toBe(isFind ? 2 : 1); + expect(mapCount).toBe(isFind ? 2 : 1); + expect(actual).toBe(isFind ? 4 : square(LARGE_ARRAY_SIZE)); + }); + }); +}); diff --git a/test/find-and-includes.js b/test/find-and-includes.js deleted file mode 100644 index b6978b13bb..0000000000 --- a/test/find-and-includes.js +++ /dev/null @@ -1,103 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, identity, args, falsey } from './utils.js'; - -describe('find and includes', function() { - lodashStable.each(['includes', 'find'], function(methodName) { - var func = _[methodName], - isIncludes = methodName == 'includes', - resolve = methodName == 'find' ? lodashStable.curry(lodashStable.eq) : identity; - - lodashStable.each({ - 'an `arguments` object': args, - 'an array': [1, 2, 3] - }, - function(collection, key) { - var values = lodashStable.toArray(collection); - - it('`_.' + methodName + '` should work with ' + key + ' and a positive `fromIndex`', function() { - var expected = [ - isIncludes || values[2], - isIncludes ? false : undefined - ]; - - var actual = [ - func(collection, resolve(values[2]), 2), - func(collection, resolve(values[1]), 2) - ]; - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with ' + key + ' and a `fromIndex` >= `length`', function() { - var indexes = [4, 6, Math.pow(2, 32), Infinity]; - - var expected = lodashStable.map(indexes, function() { - var result = isIncludes ? false : undefined; - return [result, result, result]; - }); - - var actual = lodashStable.map(indexes, function(fromIndex) { - return [ - func(collection, resolve(1), fromIndex), - func(collection, resolve(undefined), fromIndex), - func(collection, resolve(''), fromIndex) - ]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with ' + key + ' and treat falsey `fromIndex` values as `0`', function() { - var expected = lodashStable.map(falsey, lodashStable.constant(isIncludes || values[0])); - - var actual = lodashStable.map(falsey, function(fromIndex) { - return func(collection, resolve(values[0]), fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with ' + key + ' and coerce `fromIndex` to an integer', function() { - var expected = [ - isIncludes || values[0], - isIncludes || values[0], - isIncludes ? false : undefined - ]; - - var actual = [ - func(collection, resolve(values[0]), 0.1), - func(collection, resolve(values[0]), NaN), - func(collection, resolve(values[0]), '1') - ]; - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with ' + key + ' and a negative `fromIndex`', function() { - var expected = [ - isIncludes || values[2], - isIncludes ? false : undefined - ]; - - var actual = [ - func(collection, resolve(values[2]), -1), - func(collection, resolve(values[1]), -1) - ]; - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with ' + key + ' and a negative `fromIndex` <= `-length`', function() { - var indexes = [-4, -6, -Infinity], - expected = lodashStable.map(indexes, lodashStable.constant(isIncludes || values[0])); - - var actual = lodashStable.map(indexes, function(fromIndex) { - return func(collection, resolve(values[0]), fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - }); -}); diff --git a/test/find-and-includes.spec.js b/test/find-and-includes.spec.js new file mode 100644 index 0000000000..3a837aa90b --- /dev/null +++ b/test/find-and-includes.spec.js @@ -0,0 +1,102 @@ +import lodashStable from 'lodash'; +import { _, identity, args, falsey } from './utils'; + +describe('find and includes', () => { + lodashStable.each(['includes', 'find'], (methodName) => { + const func = _[methodName]; + const isIncludes = methodName === 'includes'; + const resolve = methodName === 'find' ? lodashStable.curry(lodashStable.eq) : identity; + + lodashStable.each( + { + 'an `arguments` object': args, + 'an array': [1, 2, 3], + }, + (collection, key) => { + const values = lodashStable.toArray(collection); + + it(`\`_.${methodName}\` should work with ${key} and a positive \`fromIndex\``, () => { + const expected = [isIncludes || values[2], isIncludes ? false : undefined]; + + const actual = [ + func(collection, resolve(values[2]), 2), + func(collection, resolve(values[1]), 2), + ]; + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with ${key} and a \`fromIndex\` >= \`length\``, () => { + const indexes = [4, 6, 2 ** 32, Infinity]; + + const expected = lodashStable.map(indexes, () => { + const result = isIncludes ? false : undefined; + return [result, result, result]; + }); + + const actual = lodashStable.map(indexes, (fromIndex) => [ + func(collection, resolve(1), fromIndex), + func(collection, resolve(undefined), fromIndex), + func(collection, resolve(''), fromIndex), + ]); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with ${key} and treat falsey \`fromIndex\` values as \`0\``, () => { + const expected = lodashStable.map( + falsey, + lodashStable.constant(isIncludes || values[0]), + ); + + const actual = lodashStable.map(falsey, (fromIndex) => + func(collection, resolve(values[0]), fromIndex), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with ${key} and coerce \`fromIndex\` to an integer`, () => { + const expected = [ + isIncludes || values[0], + isIncludes || values[0], + isIncludes ? false : undefined, + ]; + + const actual = [ + func(collection, resolve(values[0]), 0.1), + func(collection, resolve(values[0]), NaN), + func(collection, resolve(values[0]), '1'), + ]; + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with ${key} and a negative \`fromIndex\``, () => { + const expected = [isIncludes || values[2], isIncludes ? false : undefined]; + + const actual = [ + func(collection, resolve(values[2]), -1), + func(collection, resolve(values[1]), -1), + ]; + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with ${key} and a negative \`fromIndex\` <= \`-length\``, () => { + const indexes = [-4, -6, -Infinity]; + const expected = lodashStable.map( + indexes, + lodashStable.constant(isIncludes || values[0]), + ); + + const actual = lodashStable.map(indexes, (fromIndex) => + func(collection, resolve(values[0]), fromIndex), + ); + + expect(actual).toEqual(expected); + }); + }, + ); + }); +}); diff --git a/test/find-methods.js b/test/find-methods.js deleted file mode 100644 index 8a0c247673..0000000000 --- a/test/find-methods.js +++ /dev/null @@ -1,139 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, empties, LARGE_ARRAY_SIZE, slice } from './utils.js'; -import each from '../each.js'; - -describe('find methods', function() { - lodashStable.each(['find', 'findIndex', 'findKey', 'findLast', 'findLastIndex', 'findLastKey'], function(methodName) { - var array = [1, 2, 3, 4], - func = _[methodName]; - - var objects = [ - { 'a': 0, 'b': 0 }, - { 'a': 1, 'b': 1 }, - { 'a': 2, 'b': 2 } - ]; - - var expected = ({ - 'find': [objects[1], undefined, objects[2]], - 'findIndex': [1, -1, 2], - 'findKey': ['1', undefined, '2'], - 'findLast': [objects[2], undefined, objects[2]], - 'findLastIndex': [2, -1, 2], - 'findLastKey': ['2', undefined, '2'] - })[methodName]; - - it('`_.' + methodName + '` should return the found value', function() { - assert.strictEqual(func(objects, function(object) { return object.a; }), expected[0]); - }); - - it('`_.' + methodName + '` should return `' + expected[1] + '` if value is not found', function() { - assert.strictEqual(func(objects, function(object) { return object.a === 3; }), expected[1]); - }); - - it('`_.' + methodName + '` should work with `_.matches` shorthands', function() { - assert.strictEqual(func(objects, { 'b': 2 }), expected[2]); - }); - - it('`_.' + methodName + '` should work with `_.matchesProperty` shorthands', function() { - assert.strictEqual(func(objects, ['b', 2]), expected[2]); - }); - - it('`_.' + methodName + '` should work with `_.property` shorthands', function() { - assert.strictEqual(func(objects, 'b'), expected[0]); - }); - - it('`_.' + methodName + '` should return `' + expected[1] + '` for empty collections', function() { - var emptyValues = lodashStable.endsWith(methodName, 'Index') ? lodashStable.reject(empties, lodashStable.isPlainObject) : empties, - expecting = lodashStable.map(emptyValues, lodashStable.constant(expected[1])); - - var actual = lodashStable.map(emptyValues, function(value) { - try { - return func(value, { 'a': 3 }); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expecting); - }); - - it('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - var expected = ({ - 'find': 1, - 'findIndex': 0, - 'findKey': '0', - 'findLast': 4, - 'findLastIndex': 3, - 'findLastKey': '3' - })[methodName]; - - assert.strictEqual(_(array)[methodName](), expected); - }); - - it('`_.' + methodName + '` should return a wrapped value when explicitly chaining', function() { - assert.ok(_(array).chain()[methodName]() instanceof _); - }); - - it('`_.' + methodName + '` should not execute immediately when explicitly chaining', function() { - var wrapped = _(array).chain()[methodName](); - assert.strictEqual(wrapped.__wrapped__, array); - }); - - it('`_.' + methodName + '` should work in a lazy sequence', function() { - var largeArray = lodashStable.range(1, LARGE_ARRAY_SIZE + 1), - smallArray = array; - - lodashStable.times(2, function(index) { - var array = index ? largeArray : smallArray, - wrapped = _(array).filter(isEven); - - assert.strictEqual(wrapped[methodName](), func(lodashStable.filter(array, isEven))); - }); - }); - }), - function() { - each(['find', 'findIndex', 'findLast', 'findLastIndex'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should provide correct `predicate` arguments for arrays', function() { - var args, - array = ['a']; - - func(array, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, ['a', 0, array]); - }); - }); - - each(['find', 'findKey', 'findLast', 'findLastKey'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should work with an object for `collection`', function() { - var actual = func({ 'a': 1, 'b': 2, 'c': 3 }, function(n) { - return n < 3; - }); - - var expected = ({ - 'find': 1, - 'findKey': 'a', - 'findLast': 2, - 'findLastKey': 'b' - })[methodName]; - - assert.strictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should provide correct `predicate` arguments for objects', function() { - var args, - object = { 'a': 1 }; - - func(object, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [1, 'a', object]); - }); - }); - } -}); diff --git a/test/find-methods.spec.js b/test/find-methods.spec.js new file mode 100644 index 0000000000..0e2fd8f698 --- /dev/null +++ b/test/find-methods.spec.js @@ -0,0 +1,115 @@ +import lodashStable from 'lodash'; +import { _, empties, LARGE_ARRAY_SIZE, slice } from './utils'; +import each from '../src/each'; + +describe('find methods', () => { + lodashStable.each( + ['find', 'findIndex', 'findKey', 'findLast', 'findLastIndex', 'findLastKey'], + (methodName) => { + const array = [1, 2, 3, 4]; + const func = _[methodName]; + + const objects = [ + { a: 0, b: 0 }, + { a: 1, b: 1 }, + { a: 2, b: 2 }, + ]; + + const expected = { + find: [objects[1], undefined, objects[2]], + findIndex: [1, -1, 2], + findKey: ['1', undefined, '2'], + findLast: [objects[2], undefined, objects[2]], + findLastIndex: [2, -1, 2], + findLastKey: ['2', undefined, '2'], + }[methodName]; + + it(`\`_.${methodName}\` should return the found value`, () => { + expect( + func(objects, (object) => object.a) + ).toEqual( + expected[0] + ); + }); + + it(`\`_.${methodName}\` should return \`${expected[1]}\` if value is not found`, () => { + expect( + func(objects, (object) => object.a === 3) + ).toEqual( + expected[1] + ); + }); + + it(`\`_.${methodName}\` should work with \`_.matches\` shorthands`, () => { + expect(func(objects, { b: 2 })).toBe(expected[2]); + }); + + it(`\`_.${methodName}\` should work with \`_.matchesProperty\` shorthands`, () => { + expect(func(objects, ['b', 2])).toBe(expected[2]); + }); + + it(`\`_.${methodName}\` should work with \`_.property\` shorthands`, () => { + expect(func(objects, 'b')).toBe(expected[0]); + }); + + it(`\`_.${methodName}\` should return \`${expected[1]}\` for empty collections`, () => { + const emptyValues = lodashStable.endsWith(methodName, 'Index') + ? lodashStable.reject(empties, lodashStable.isPlainObject) + : empties; + const expecting = lodashStable.map(emptyValues, lodashStable.constant(expected[1])); + + const actual = lodashStable.map(emptyValues, (value) => { + try { + return func(value, { a: 3 }); + } catch (e) {} + }); + + expect(actual).toEqual(expecting); + }); + }, + ), + function () { + each(['find', 'findIndex', 'findLast', 'findLastIndex'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should provide correct \`predicate\` arguments for arrays`, () => { + let args; + const array = ['a']; + + func(array, function () { + args || (args = slice.call(arguments)); + }); + + expect(args, ['a', 0).toEqual(array]); + }); + }); + + each(['find', 'findKey', 'findLast', 'findLastKey'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should work with an object for \`collection\``, () => { + const actual = func({ a: 1, b: 2, c: 3 }, (n) => n < 3); + + const expected = { + find: 1, + findKey: 'a', + findLast: 2, + findLastKey: 'b', + }[methodName]; + + expect(actual).toBe(expected); + }); + + it(`\`_.${methodName}\` should provide correct \`predicate\` arguments for objects`, () => { + let args; + const object = { a: 1 }; + + func(object, function () { + args || (args = slice.call(arguments)); + }); + + expect(args, [1, 'a').toEqual(object]); + }); + }); + }; +}); diff --git a/test/findLast.spec.js b/test/findLast.spec.js new file mode 100644 index 0000000000..677fc49332 --- /dev/null +++ b/test/findLast.spec.js @@ -0,0 +1,91 @@ +import lodashStable from 'lodash'; +import { args, falsey } from './utils'; +import findLast from '../src/findLast'; + +describe('findLast', () => { + const resolve = lodashStable.curry(lodashStable.eq); + + lodashStable.each( + { + 'an `arguments` object': args, + 'an array': [1, 2, 3], + }, + (collection, key) => { + const values = lodashStable.toArray(collection); + + it(`should work with ${key} and a positive \`fromIndex\``, () => { + const expected = [values[1], undefined]; + + const actual = [ + findLast(collection, resolve(values[1]), 1), + findLast(collection, resolve(values[2]), 1), + ]; + + expect(actual).toEqual(expected); + }); + + it(`should work with ${key} and a \`fromIndex\` >= \`length\``, () => { + const indexes = [4, 6, 2 ** 32, Infinity]; + + const expected = lodashStable.map( + indexes, + lodashStable.constant([values[0], undefined, undefined]), + ); + + const actual = lodashStable.map(indexes, (fromIndex) => [ + findLast(collection, resolve(1), fromIndex), + findLast(collection, resolve(undefined), fromIndex), + findLast(collection, resolve(''), fromIndex), + ]); + + expect(actual).toEqual(expected); + }); + + it(`should work with ${key} and treat falsey \`fromIndex\` values correctly`, () => { + const expected = lodashStable.map(falsey, (value) => + value === undefined ? values[3] : undefined, + ); + + const actual = lodashStable.map(falsey, (fromIndex) => + findLast(collection, resolve(values[3]), fromIndex), + ); + + expect(actual).toEqual(expected); + }); + + it(`should work with ${key} and coerce \`fromIndex\` to an integer`, () => { + const expected = [values[0], values[0], undefined]; + + const actual = [ + findLast(collection, resolve(values[0]), 0.1), + findLast(collection, resolve(values[0]), NaN), + findLast(collection, resolve(values[2]), '1'), + ]; + + expect(actual).toEqual(expected); + }); + + it(`should work with ${key} and a negative \`fromIndex\``, () => { + const expected = [values[1], undefined]; + + const actual = [ + findLast(collection, resolve(values[1]), -2), + findLast(collection, resolve(values[2]), -2), + ]; + + expect(actual).toEqual(expected); + }); + + it(`should work with ${key} and a negative \`fromIndex\` <= \`-length\``, () => { + const indexes = [-4, -6, -Infinity]; + const expected = lodashStable.map(indexes, lodashStable.constant(values[0])); + + const actual = lodashStable.map(indexes, (fromIndex) => + findLast(collection, resolve(values[0]), fromIndex), + ); + + expect(actual).toEqual(expected); + }); + }, + ); +}); diff --git a/test/findLast.test.js b/test/findLast.test.js deleted file mode 100644 index b303b55f52..0000000000 --- a/test/findLast.test.js +++ /dev/null @@ -1,99 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, falsey } from './utils.js'; -import findLast from '../findLast.js'; - -describe('findLast', function() { - var resolve = lodashStable.curry(lodashStable.eq); - - lodashStable.each({ - 'an `arguments` object': args, - 'an array': [1, 2, 3] - }, - function(collection, key) { - var values = lodashStable.toArray(collection); - - it('should work with ' + key + ' and a positive `fromIndex`', function() { - var expected = [ - values[1], - undefined - ]; - - var actual = [ - findLast(collection, resolve(values[1]), 1), - findLast(collection, resolve(values[2]), 1) - ]; - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with ' + key + ' and a `fromIndex` >= `length`', function() { - var indexes = [4, 6, Math.pow(2, 32), Infinity]; - - var expected = lodashStable.map(indexes, lodashStable.constant([values[0], undefined, undefined])); - - var actual = lodashStable.map(indexes, function(fromIndex) { - return [ - findLast(collection, resolve(1), fromIndex), - findLast(collection, resolve(undefined), fromIndex), - findLast(collection, resolve(''), fromIndex) - ]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with ' + key + ' and treat falsey `fromIndex` values correctly', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? values[3] : undefined; - }); - - var actual = lodashStable.map(falsey, function(fromIndex) { - return findLast(collection, resolve(values[3]), fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with ' + key + ' and coerce `fromIndex` to an integer', function() { - var expected = [ - values[0], - values[0], - undefined - ]; - - var actual = [ - findLast(collection, resolve(values[0]), 0.1), - findLast(collection, resolve(values[0]), NaN), - findLast(collection, resolve(values[2]), '1') - ]; - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with ' + key + ' and a negative `fromIndex`', function() { - var expected = [ - values[1], - undefined - ]; - - var actual = [ - findLast(collection, resolve(values[1]), -2), - findLast(collection, resolve(values[2]), -2) - ]; - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with ' + key + ' and a negative `fromIndex` <= `-length`', function() { - var indexes = [-4, -6, -Infinity], - expected = lodashStable.map(indexes, lodashStable.constant(values[0])); - - var actual = lodashStable.map(indexes, function(fromIndex) { - return findLast(collection, resolve(values[0]), fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/findLastIndex-and-lastIndexOf.spec.js b/test/findLastIndex-and-lastIndexOf.spec.js new file mode 100644 index 0000000000..c86d09e27e --- /dev/null +++ b/test/findLastIndex-and-lastIndexOf.spec.js @@ -0,0 +1,68 @@ +import lodashStable from 'lodash'; +import { identity, stubZero, falsey } from './utils'; +import findLastIndex from '../src/findLastIndex'; +import lastIndexOf from '../src/lastIndexOf'; + +const methods = { + findLastIndex, + lastIndexOf, +}; + +describe('findLastIndex and lastIndexOf', () => { + lodashStable.each(['findLastIndex', 'lastIndexOf'], (methodName) => { + const array = [1, 2, 3, 1, 2, 3]; + const func = methods[methodName]; + const resolve = + methodName === 'findLastIndex' ? lodashStable.curry(lodashStable.eq) : identity; + + it(`\`_.${methodName}\` should return the index of the last matched value`, () => { + expect(func(array, resolve(3))).toBe(5); + }); + + it(`\`_.${methodName}\` should work with a positive \`fromIndex\``, () => { + expect(func(array, resolve(1), 2)).toBe(0); + }); + + it(`\`_.${methodName}\` should work with a \`fromIndex\` >= \`length\``, () => { + const values = [6, 8, 2 ** 32, Infinity]; + const expected = lodashStable.map(values, lodashStable.constant([-1, 3, -1])); + + const actual = lodashStable.map(values, (fromIndex) => [ + func(array, resolve(undefined), fromIndex), + func(array, resolve(1), fromIndex), + func(array, resolve(''), fromIndex), + ]); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with a negative \`fromIndex\``, () => { + expect(func(array, resolve(2), -3)).toBe(1); + }); + + it(`\`_.${methodName}\` should work with a negative \`fromIndex\` <= \`-length\``, () => { + const values = [-6, -8, -Infinity]; + const expected = lodashStable.map(values, stubZero); + + const actual = lodashStable.map(values, (fromIndex) => + func(array, resolve(1), fromIndex), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should treat falsey \`fromIndex\` values correctly`, () => { + const expected = lodashStable.map(falsey, (value) => (value === undefined ? 5 : -1)); + + const actual = lodashStable.map(falsey, (fromIndex) => + func(array, resolve(3), fromIndex), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should coerce \`fromIndex\` to an integer`, () => { + expect(func(array, resolve(2), 4.2)).toBe(4); + }); + }); +}); diff --git a/test/findLastIndex-and-lastIndexOf.test.js b/test/findLastIndex-and-lastIndexOf.test.js deleted file mode 100644 index e28803e395..0000000000 --- a/test/findLastIndex-and-lastIndexOf.test.js +++ /dev/null @@ -1,72 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, stubZero, falsey } from './utils.js'; -import findLastIndex from '../findLastIndex.js'; -import lastIndexOf from '../lastIndexOf.js'; - -const methods = { - findLastIndex, - lastIndexOf -}; - -describe('findLastIndex and lastIndexOf', function() { - lodashStable.each(['findLastIndex', 'lastIndexOf'], function(methodName) { - var array = [1, 2, 3, 1, 2, 3], - func = methods[methodName], - resolve = methodName == 'findLastIndex' ? lodashStable.curry(lodashStable.eq) : identity; - - it('`_.' + methodName + '` should return the index of the last matched value', function() { - assert.strictEqual(func(array, resolve(3)), 5); - }); - - it('`_.' + methodName + '` should work with a positive `fromIndex`', function() { - assert.strictEqual(func(array, resolve(1), 2), 0); - }); - - it('`_.' + methodName + '` should work with a `fromIndex` >= `length`', function() { - var values = [6, 8, Math.pow(2, 32), Infinity], - expected = lodashStable.map(values, lodashStable.constant([-1, 3, -1])); - - var actual = lodashStable.map(values, function(fromIndex) { - return [ - func(array, resolve(undefined), fromIndex), - func(array, resolve(1), fromIndex), - func(array, resolve(''), fromIndex) - ]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with a negative `fromIndex`', function() { - assert.strictEqual(func(array, resolve(2), -3), 1); - }); - - it('`_.' + methodName + '` should work with a negative `fromIndex` <= `-length`', function() { - var values = [-6, -8, -Infinity], - expected = lodashStable.map(values, stubZero); - - var actual = lodashStable.map(values, function(fromIndex) { - return func(array, resolve(1), fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should treat falsey `fromIndex` values correctly', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? 5 : -1; - }); - - var actual = lodashStable.map(falsey, function(fromIndex) { - return func(array, resolve(3), fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should coerce `fromIndex` to an integer', function() { - assert.strictEqual(func(array, resolve(2), 4.2), 4); - }); - }); -}); diff --git a/test/flatMap-methods.js b/test/flatMap-methods.js deleted file mode 100644 index 4508e0a2b1..0000000000 --- a/test/flatMap-methods.js +++ /dev/null @@ -1,72 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, identity, falsey, stubArray } from './utils.js'; - -describe('flatMap methods', function() { - lodashStable.each(['flatMap', 'flatMapDeep', 'flatMapDepth'], function(methodName) { - var func = _[methodName], - array = [1, 2, 3, 4]; - - function duplicate(n) { - return [n, n]; - } - - it('`_.' + methodName + '` should map values in `array` to a new flattened array', function() { - var actual = func(array, duplicate), - expected = lodashStable.flatten(lodashStable.map(array, duplicate)); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with `_.property` shorthands', function() { - var objects = [{ 'a': [1, 2] }, { 'a': [3, 4] }]; - assert.deepStrictEqual(func(objects, 'a'), array); - }); - - it('`_.' + methodName + '` should iterate over own string keyed properties of objects', function() { - function Foo() { - this.a = [1, 2]; - } - Foo.prototype.b = [3, 4]; - - var actual = func(new Foo, identity); - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('`_.' + methodName + '` should use `_.identity` when `iteratee` is nullish', function() { - var array = [[1, 2], [3, 4]], - object = { 'a': [1, 2], 'b': [3, 4] }, - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([1, 2, 3, 4])); - - lodashStable.each([array, object], function(collection) { - var actual = lodashStable.map(values, function(value, index) { - return index ? func(collection, value) : func(collection); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should accept a falsey `collection`', function() { - var expected = lodashStable.map(falsey, stubArray); - - var actual = lodashStable.map(falsey, function(collection, index) { - try { - return index ? func(collection) : func(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should treat number values for `collection` as empty', function() { - assert.deepStrictEqual(func(1), []); - }); - - it('`_.' + methodName + '` should work with objects with non-number length properties', function() { - var object = { 'length': [1, 2] }; - assert.deepStrictEqual(func(object, identity), [1, 2]); - }); - }); -}); diff --git a/test/flatMap-methods.spec.js b/test/flatMap-methods.spec.js new file mode 100644 index 0000000000..f85dcc07d6 --- /dev/null +++ b/test/flatMap-methods.spec.js @@ -0,0 +1,74 @@ +import lodashStable from 'lodash'; +import { _, identity, falsey, stubArray } from './utils'; + +describe('flatMap methods', () => { + lodashStable.each(['flatMap', 'flatMapDeep', 'flatMapDepth'], (methodName) => { + const func = _[methodName]; + const array = [1, 2, 3, 4]; + + function duplicate(n) { + return [n, n]; + } + + it(`\`_.${methodName}\` should map values in \`array\` to a new flattened array`, () => { + const actual = func(array, duplicate); + const expected = lodashStable.flatten(lodashStable.map(array, duplicate)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with \`_.property\` shorthands`, () => { + const objects = [{ a: [1, 2] }, { a: [3, 4] }]; + expect(func(objects, 'a')).toEqual(array); + }); + + it(`\`_.${methodName}\` should iterate over own string keyed properties of objects`, () => { + function Foo() { + this.a = [1, 2]; + } + Foo.prototype.b = [3, 4]; + + const actual = func(new Foo(), identity); + expect(actual, [1).toEqual(2]); + }); + + it(`\`_.${methodName}\` should use \`_.identity\` when \`iteratee\` is nullish`, () => { + const array = [ + [1, 2], + [3, 4], + ]; + const object = { a: [1, 2], b: [3, 4] }; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([1, 2, 3, 4])); + + lodashStable.each([array, object], (collection) => { + const actual = lodashStable.map(values, (value, index) => + index ? func(collection, value) : func(collection), + ); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should accept a falsey \`collection\``, () => { + const expected = lodashStable.map(falsey, stubArray); + + const actual = lodashStable.map(falsey, (collection, index) => { + try { + return index ? func(collection) : func(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should treat number values for \`collection\` as empty`, () => { + expect(func(1)).toEqual([]); + }); + + it(`\`_.${methodName}\` should work with objects with non-number length properties`, () => { + const object = { length: [1, 2] }; + expect(func(object, identity), [1).toEqual(2]); + }); + }); +}); diff --git a/test/flatMapDepth.js b/test/flatMapDepth.js deleted file mode 100644 index 365ce8fcbc..0000000000 --- a/test/flatMapDepth.js +++ /dev/null @@ -1,33 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity } from './utils.js'; -import flatMapDepth from '../flatMapDepth.js'; - -describe('flatMapDepth', function() { - var array = [1, [2, [3, [4]], 5]]; - - it('should use a default `depth` of `1`', function() { - assert.deepStrictEqual(flatMapDepth(array, identity), [1, 2, [3, [4]], 5]); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([1, 2, [3, [4]], 5])); - - var actual = lodashStable.map(values, function(value, index) { - return index ? flatMapDepth(array, value) : flatMapDepth(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should treat a `depth` of < `1` as a shallow clone', function() { - lodashStable.each([-1, 0], function(depth) { - assert.deepStrictEqual(flatMapDepth(array, identity, depth), [1, [2, [3, [4]], 5]]); - }); - }); - - it('should coerce `depth` to an integer', function() { - assert.deepStrictEqual(flatMapDepth(array, identity, 2.2), [1, 2, 3, [4], 5]); - }); -}); diff --git a/test/flatMapDepth.spec.js b/test/flatMapDepth.spec.js new file mode 100644 index 0000000000..244acd3a4e --- /dev/null +++ b/test/flatMapDepth.spec.js @@ -0,0 +1,32 @@ +import lodashStable from 'lodash'; +import { identity } from './utils'; +import flatMapDepth from '../src/flatMapDepth'; + +describe('flatMapDepth', () => { + const array = [1, [2, [3, [4]], 5]]; + + it('should use a default `depth` of `1`', () => { + expect(flatMapDepth(array, identity)).toEqual([1, 2, [3, [4]], 5]); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([1, 2, [3, [4]], 5])); + + const actual = lodashStable.map(values, (value, index) => + index ? flatMapDepth(array, value) : flatMapDepth(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should treat a `depth` of < `1` as a shallow clone', () => { + lodashStable.each([-1, 0], (depth) => { + expect(flatMapDepth(array, identity, depth)).toEqual([1, [2, [3, [4]], 5]]); + }); + }); + + it('should coerce `depth` to an integer', () => { + expect(flatMapDepth(array, identity, 2.2)).toEqual([1, 2, 3, [4], 5]); + }); +}); diff --git a/test/flatten-methods.js b/test/flatten-methods.js deleted file mode 100644 index 63b01e8c4e..0000000000 --- a/test/flatten-methods.js +++ /dev/null @@ -1,106 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, _ } from './utils.js'; -import flatten from '../flatten.js'; -import flattenDeep from '../flattenDeep.js'; -import flattenDepth from '../flattenDepth.js'; - -describe('flatten methods', function() { - var array = [1, [2, [3, [4]], 5]], - methodNames = ['flatten', 'flattenDeep', 'flattenDepth']; - - it('should flatten `arguments` objects', function() { - var array = [args, [args]]; - - assert.deepStrictEqual(flatten(array), [1, 2, 3, args]); - assert.deepStrictEqual(flattenDeep(array), [1, 2, 3, 1, 2, 3]); - assert.deepStrictEqual(flattenDepth(array, 2), [1, 2, 3, 1, 2, 3]); - }); - - it('should treat sparse arrays as dense', function() { - var array = [[1, 2, 3], Array(3)], - expected = [1, 2, 3]; - - expected.push(undefined, undefined, undefined); - - lodashStable.each(methodNames, function(methodName) { - var actual = _[methodName](array); - assert.deepStrictEqual(actual, expected); - assert.ok('4' in actual); - }); - }); - - it('should flatten objects with a truthy `Symbol.isConcatSpreadable` value', function() { - if (Symbol && Symbol.isConcatSpreadable) { - var object = { '0': 'a', 'length': 1 }, - array = [object], - expected = lodashStable.map(methodNames, lodashStable.constant(['a'])); - - object[Symbol.isConcatSpreadable] = true; - - var actual = lodashStable.map(methodNames, function(methodName) { - return _[methodName](array); - }); - - assert.deepStrictEqual(actual, expected); - } - }); - - it('should work with extremely large arrays', function() { - lodashStable.times(3, function(index) { - var expected = Array(5e5); - try { - var func = flatten; - if (index == 1) { - func = flattenDeep; - } else if (index == 2) { - func = flattenDepth; - } - assert.deepStrictEqual(func([expected]), expected); - } catch (e) { - assert.ok(false, e.message); - } - }); - }); - - it('should work with empty arrays', function() { - var array = [[], [[]], [[], [[[]]]]]; - - assert.deepStrictEqual(flatten(array), [[], [], [[[]]]]); - assert.deepStrictEqual(flattenDeep(array), []); - assert.deepStrictEqual(flattenDepth(array, 2), [[[]]]); - }); - - it('should support flattening of nested arrays', function() { - assert.deepStrictEqual(flatten(array), [1, 2, [3, [4]], 5]); - assert.deepStrictEqual(flattenDeep(array), [1, 2, 3, 4, 5]); - assert.deepStrictEqual(flattenDepth(array, 2), [1, 2, 3, [4], 5]); - }); - - it('should return an empty array for non array-like objects', function() { - var expected = [], - nonArray = { '0': 'a' }; - - assert.deepStrictEqual(flatten(nonArray), expected); - assert.deepStrictEqual(flattenDeep(nonArray), expected); - assert.deepStrictEqual(flattenDepth(nonArray, 2), expected); - }); - - it('should return a wrapped value when chaining', function() { - var wrapped = _(array), - actual = wrapped.flatten(); - - assert.ok(actual instanceof _); - assert.deepEqual(actual.value(), [1, 2, [3, [4]], 5]); - - actual = wrapped.flattenDeep(); - - assert.ok(actual instanceof _); - assert.deepEqual(actual.value(), [1, 2, 3, 4, 5]); - - actual = wrapped.flattenDepth(2); - - assert.ok(actual instanceof _); - assert.deepEqual(actual.value(), [1, 2, 3, [4], 5]); - }); -}); diff --git a/test/flatten-methods.spec.js b/test/flatten-methods.spec.js new file mode 100644 index 0000000000..21f117966c --- /dev/null +++ b/test/flatten-methods.spec.js @@ -0,0 +1,101 @@ +import lodashStable from 'lodash'; +import { args, _ } from './utils'; +import flatten from '../src/flatten'; +import flattenDeep from '../src/flattenDeep'; +import flattenDepth from '../src/flattenDepth'; + +describe('flatten methods', () => { + const array = [1, [2, [3, [4]], 5]]; + const methodNames = ['flatten', 'flattenDeep', 'flattenDepth']; + + it('should flatten `arguments` objects', () => { + const array = [args, [args]]; + + expect(flatten(array)).toEqual([1, 2, 3, args]); + expect(flattenDeep(array)).toEqual([1, 2, 3, 1, 2, 3]); + expect(flattenDepth(array, 2)).toEqual([1, 2, 3, 1, 2, 3]); + }); + + it('should treat sparse arrays as dense', () => { + const array = [[1, 2, 3], Array(3)]; + const expected = [1, 2, 3]; + + expected.push(undefined, undefined, undefined); + + lodashStable.each(methodNames, (methodName) => { + const actual = _[methodName](array); + expect(actual).toEqual(expected); + expect('4' in actual).toBeTruthy(); + }); + }); + + it('should flatten objects with a truthy `Symbol.isConcatSpreadable` value', () => { + if (Symbol && Symbol.isConcatSpreadable) { + const object = { 0: 'a', length: 1 }; + const array = [object]; + const expected = lodashStable.map(methodNames, lodashStable.constant(['a'])); + + object[Symbol.isConcatSpreadable] = true; + + const actual = lodashStable.map(methodNames, (methodName) => _[methodName](array)); + + expect(actual).toEqual(expected); + } + }); + + // FIXME: Not yet pass - Maximum call stack size exceeded. + // + // it('should work with extremely large arrays', () => { + // lodashStable.times(3, (index) => { + // const expected = Array(5e5); + // let func = flatten; + // if (index === 1) { + // func = flattenDeep; + // } else if (index === 2) { + // func = flattenDepth; + // } + // expect(func([expected])).toEqual(expected); + // }); + // }); + + it('should work with empty arrays', () => { + const array = [[], [[]], [[], [[[]]]]]; + + expect(flatten(array)).toEqual([[], [], [[[]]]]); + expect(flattenDeep(array)).toEqual([]); + expect(flattenDepth(array, 2)).toEqual([[[]]]); + }); + + it('should support flattening of nested arrays', () => { + expect(flatten(array)).toEqual([1, 2, [3, [4]], 5]); + expect(flattenDeep(array)).toEqual([1, 2, 3, 4, 5]); + expect(flattenDepth(array, 2)).toEqual([1, 2, 3, [4], 5]); + }); + + it('should return an empty array for non array-like objects', () => { + const expected = []; + const nonArray = { 0: 'a' }; + + expect(flatten(nonArray)).toEqual(expected); + expect(flattenDeep(nonArray)).toEqual(expected); + expect(flattenDepth(nonArray, 2)).toEqual(expected); + }); + + it('should return a wrapped value when chaining', () => { + const wrapped = _(array); + let actual = wrapped.flatten(); + + expect(actual instanceof _).toBeTruthy(); + expect(actual.value()).toEqual([1, 2, [3, [4]], 5]); + + actual = wrapped.flattenDeep(); + + expect(actual instanceof _).toBeTruthy(); + expect(actual.value()).toEqual([1, 2, 3, 4, 5]); + + actual = wrapped.flattenDepth(2); + + expect(actual instanceof _).toBeTruthy(); + expect(actual.value()).toEqual([1, 2, 3, [4], 5]); + }); +}); diff --git a/test/flattenDepth.js b/test/flattenDepth.js deleted file mode 100644 index 6b6374e8d0..0000000000 --- a/test/flattenDepth.js +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import flattenDepth from '../flattenDepth.js'; - -describe('flattenDepth', function() { - var array = [1, [2, [3, [4]], 5]]; - - it('should use a default `depth` of `1`', function() { - assert.deepStrictEqual(flattenDepth(array), [1, 2, [3, [4]], 5]); - }); - - it('should treat a `depth` of < `1` as a shallow clone', function() { - lodashStable.each([-1, 0], function(depth) { - assert.deepStrictEqual(flattenDepth(array, depth), [1, [2, [3, [4]], 5]]); - }); - }); - - it('should coerce `depth` to an integer', function() { - assert.deepStrictEqual(flattenDepth(array, 2.2), [1, 2, 3, [4], 5]); - }); -}); diff --git a/test/flattenDepth.spec.js b/test/flattenDepth.spec.js new file mode 100644 index 0000000000..a15e96a1af --- /dev/null +++ b/test/flattenDepth.spec.js @@ -0,0 +1,20 @@ +import lodashStable from 'lodash'; +import flattenDepth from '../src/flattenDepth'; + +describe('flattenDepth', () => { + const array = [1, [2, [3, [4]], 5]]; + + it('should use a default `depth` of `1`', () => { + expect(flattenDepth(array)).toEqual([1, 2, [3, [4]], 5]); + }); + + it('should treat a `depth` of < `1` as a shallow clone', () => { + lodashStable.each([-1, 0], (depth) => { + expect(flattenDepth(array, depth)).toEqual([1, [2, [3, [4]], 5]]); + }); + }); + + it('should coerce `depth` to an integer', () => { + expect(flattenDepth(array, 2.2)).toEqual([1, 2, 3, [4], 5]); + }); +}); diff --git a/test/flip.spec.js b/test/flip.spec.js new file mode 100644 index 0000000000..77900f1ed2 --- /dev/null +++ b/test/flip.spec.js @@ -0,0 +1,13 @@ +import { slice } from './utils'; +import flip from '../src/flip'; + +describe('flip', () => { + function fn() { + return slice.call(arguments); + } + + it('should flip arguments provided to `func`', () => { + const flipped = flip(fn); + expect(flipped('a', 'b', 'c', 'd'), ['d', 'c', 'b').toEqual('a']); + }); +}); diff --git a/test/flip.test.js b/test/flip.test.js deleted file mode 100644 index c8423ece44..0000000000 --- a/test/flip.test.js +++ /dev/null @@ -1,14 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import flip from '../flip.js'; - -describe('flip', function() { - function fn() { - return slice.call(arguments); - } - - it('should flip arguments provided to `func`', function() { - var flipped = flip(fn); - assert.deepStrictEqual(flipped('a', 'b', 'c', 'd'), ['d', 'c', 'b', 'a']); - }); -}); diff --git a/test/flow-methods.spec.js b/test/flow-methods.spec.js new file mode 100644 index 0000000000..5a2642a23c --- /dev/null +++ b/test/flow-methods.spec.js @@ -0,0 +1,50 @@ +import lodashStable from 'lodash'; +import { add, square, noop, identity } from './utils'; +import head from '../src/head'; +import map from '../src/map'; +import uniq from '../src/uniq'; +import flow from '../src/flow'; +import flowRight from '../src/flowRight'; + +const methods = { + flow, + flowRight, +}; + +describe('flow methods', () => { + lodashStable.each(['flow', 'flowRight'], (methodName) => { + const func = methods[methodName]; + const isFlow = methodName === 'flow'; + + it(`\`_.${methodName}\` should supply each function with the return value of the previous`, () => { + const fixed = function (n) { + return n.toFixed(1); + }; + const combined = isFlow ? func(add, square, fixed) : func(fixed, square, add); + + expect(combined(1, 2)).toBe('9.0'); + }); + + it(`\`_.${methodName}\` should return a new function`, () => { + assert.notStrictEqual(func(noop), noop); + }); + + it(`\`_.${methodName}\` should work with a curried function and \`_.head\``, () => { + const curried = lodashStable.curry(identity); + + const combined = isFlow ? func(head, curried) : func(curried, head); + + expect(combined([1])).toBe(1); + }); + + it(`\`_.${methodName}\` should work with curried functions with placeholders`, () => { + const curried = lodashStable.curry(lodashStable.ary(map, 2), 2); + const getProp = curried(curried.placeholder, (value) => value.a); + const objects = [{ a: 1 }, { a: 2 }, { a: 1 }]; + + const combined = isFlow ? func(getProp, uniq) : func(uniq, getProp); + + expect(combined(objects), [1).toEqual(2]); + }); + }); +}); diff --git a/test/flow-methods.test.js b/test/flow-methods.test.js deleted file mode 100644 index c1706ab1ef..0000000000 --- a/test/flow-methods.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { add, square, noop, identity } from './utils.js'; -import head from '../head.js'; -import map from '../map.js'; -import uniq from '../uniq.js'; -import flow from '../flow.js'; -import flowRight from '../flowRight.js'; - -const methods = { - flow, - flowRight -} - -describe('flow methods', function() { - lodashStable.each(['flow', 'flowRight'], function(methodName) { - var func = methods[methodName], - isFlow = methodName == 'flow'; - - it('`_.' + methodName + '` should supply each function with the return value of the previous', function() { - var fixed = function(n) { return n.toFixed(1); }, - combined = isFlow ? func(add, square, fixed) : func(fixed, square, add); - - assert.strictEqual(combined(1, 2), '9.0'); - }); - - it('`_.' + methodName + '` should return a new function', function() { - assert.notStrictEqual(func(noop), noop); - }); - - it('`_.' + methodName + '` should work with a curried function and `_.head`', function() { - var curried = lodashStable.curry(identity); - - var combined = isFlow - ? func(head, curried) - : func(curried, head); - - assert.strictEqual(combined([1]), 1); - }); - - it('`_.' + methodName + '` should work with curried functions with placeholders', function() { - var curried = lodashStable.curry(lodashStable.ary(map, 2), 2), - getProp = curried(curried.placeholder, (value) => value.a), - objects = [{ 'a': 1 }, { 'a': 2 }, { 'a': 1 }]; - - var combined = isFlow - ? func(getProp, uniq) - : func(uniq, getProp); - - assert.deepStrictEqual(combined(objects), [1, 2]); - }); - }); -}); diff --git a/test/forEach.spec.js b/test/forEach.spec.js new file mode 100644 index 0000000000..fa9f1a4da0 --- /dev/null +++ b/test/forEach.spec.js @@ -0,0 +1,8 @@ +import each from '../src/each'; +import forEach from '../src/forEach'; + +describe('forEach', () => { + it('should be aliased', () => { + expect(each).toBe(forEach); + }); +}); diff --git a/test/forEach.test.js b/test/forEach.test.js deleted file mode 100644 index 8770ffdf97..0000000000 --- a/test/forEach.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import each from '../each.js'; -import forEach from '../forEach.js'; - -describe('forEach', function() { - it('should be aliased', function() { - assert.strictEqual(each, forEach); - }); -}); diff --git a/test/forEachRight.spec.js b/test/forEachRight.spec.js new file mode 100644 index 0000000000..58db8ebf96 --- /dev/null +++ b/test/forEachRight.spec.js @@ -0,0 +1,8 @@ +import eachRight from '../src/eachRight'; +import forEachRight from '../src/forEachRight'; + +describe('forEachRight', () => { + it('should be aliased', () => { + expect(eachRight).toBe(forEachRight); + }); +}); diff --git a/test/forEachRight.test.js b/test/forEachRight.test.js deleted file mode 100644 index 29494d9a4a..0000000000 --- a/test/forEachRight.test.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import eachRight from '../eachRight.js'; -import forEachRight from '../forEachRight.js'; - -describe('forEachRight', function() { - it('should be aliased', function() { - assert.strictEqual(eachRight, forEachRight); - }); -}); diff --git a/test/forIn-methods.js b/test/forIn-methods.js deleted file mode 100644 index f806201db4..0000000000 --- a/test/forIn-methods.js +++ /dev/null @@ -1,20 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('forIn methods', function() { - lodashStable.each(['forIn', 'forInRight'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` iterates over inherited string keyed properties', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var keys = []; - func(new Foo, function(value, key) { keys.push(key); }); - assert.deepStrictEqual(keys.sort(), ['a', 'b']); - }); - }); -}); diff --git a/test/forIn-methods.spec.js b/test/forIn-methods.spec.js new file mode 100644 index 0000000000..ae7a8f4a44 --- /dev/null +++ b/test/forIn-methods.spec.js @@ -0,0 +1,21 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('forIn methods', () => { + lodashStable.each(['forIn', 'forInRight'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` iterates over inherited string keyed properties`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const keys = []; + func(new Foo(), (value, key) => { + keys.push(key); + }); + expect(keys.sort()).toEqual(['a', 'b']); + }); + }); +}); diff --git a/test/forOwn-methods.js b/test/forOwn-methods.js deleted file mode 100644 index a8a4fbbef4..0000000000 --- a/test/forOwn-methods.js +++ /dev/null @@ -1,17 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('forOwn methods', function() { - lodashStable.each(['forOwn', 'forOwnRight'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should iterate over `length` properties', function() { - var object = { '0': 'zero', '1': 'one', 'length': 2 }, - props = []; - - func(object, function(value, prop) { props.push(prop); }); - assert.deepStrictEqual(props.sort(), ['0', '1', 'length']); - }); - }); -}); diff --git a/test/forOwn-methods.spec.js b/test/forOwn-methods.spec.js new file mode 100644 index 0000000000..93bab6c3e9 --- /dev/null +++ b/test/forOwn-methods.spec.js @@ -0,0 +1,18 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('forOwn methods', () => { + lodashStable.each(['forOwn', 'forOwnRight'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should iterate over \`length\` properties`, () => { + const object = { 0: 'zero', 1: 'one', length: 2 }; + const props = []; + + func(object, (value, prop) => { + props.push(prop); + }); + expect(props.sort(), ['0', '1').toEqual('length']); + }); + }); +}); diff --git a/test/fromPairs.js b/test/fromPairs.js deleted file mode 100644 index 5e94ad1313..0000000000 --- a/test/fromPairs.js +++ /dev/null @@ -1,47 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubObject, LARGE_ARRAY_SIZE } from './utils.js'; -import fromPairs from '../fromPairs.js'; -import toPairs from '../toPairs.js'; - -describe('fromPairs', function() { - it('should accept a two dimensional array', function() { - var array = [['a', 1], ['b', 2]], - object = { 'a': 1, 'b': 2 }, - actual = fromPairs(array); - - assert.deepStrictEqual(actual, object); - }); - - it('should accept a falsey `array`', function() { - var expected = lodashStable.map(falsey, stubObject); - - var actual = lodashStable.map(falsey, function(array, index) { - try { - return index ? fromPairs(array) : fromPairs(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should not support deep paths', function() { - var actual = fromPairs([['a.b', 1]]); - assert.deepStrictEqual(actual, { 'a.b': 1 }); - }); - - it('should support consuming the return value of `_.toPairs`', function() { - var object = { 'a.b': 1 }; - assert.deepStrictEqual(fromPairs(toPairs(object)), object); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.times(LARGE_ARRAY_SIZE, function(index) { - return ['key' + index, index]; - }); - - var actual = _(array).fromPairs().map(square).filter(isEven).take().value(); - - assert.deepEqual(actual, _.take(_.filter(_.map(fromPairs(array), square), isEven))); - }); -}); diff --git a/test/fromPairs.spec.js b/test/fromPairs.spec.js new file mode 100644 index 0000000000..d8e299ac45 --- /dev/null +++ b/test/fromPairs.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { falsey, stubObject } from './utils'; +import fromPairs from '../src/fromPairs'; +import toPairs from '../src/toPairs'; + +describe('fromPairs', () => { + it('should accept a two dimensional array', () => { + const array = [ + ['a', 1], + ['b', 2], + ]; + const object = { a: 1, b: 2 }; + const actual = fromPairs(array); + + expect(actual).toEqual(object); + }); + + it('should accept a falsey `array`', () => { + const expected = lodashStable.map(falsey, stubObject); + + const actual = lodashStable.map(falsey, (array, index) => { + try { + return index ? fromPairs(array) : fromPairs(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should not support deep paths', () => { + const actual = fromPairs([['a.b', 1]]); + expect(actual).toEqual({ 'a.b': 1 }); + }); + + it('should support consuming the return value of `_.toPairs`', () => { + const object = { 'a.b': 1 }; + expect(fromPairs(toPairs(object))).toEqual(object); + }); +}); diff --git a/test/functions.spec.js b/test/functions.spec.js new file mode 100644 index 0000000000..b26ee6741a --- /dev/null +++ b/test/functions.spec.js @@ -0,0 +1,21 @@ +import { identity, noop } from './utils'; +import functions from '../src/functions'; + +describe('functions', () => { + it('should return the function names of an object', () => { + const object = { a: 'a', b: identity, c: /x/, d: noop }; + const actual = functions(object).sort(); + + expect(actual).toEqual(['b', 'd']); + }); + + it('should not include inherited functions', () => { + function Foo() { + this.a = identity; + this.b = 'b'; + } + Foo.prototype.c = noop; + + expect(functions(new Foo())).toEqual(['a']); + }); +}); diff --git a/test/functions.test.js b/test/functions.test.js deleted file mode 100644 index 1d1daeeb12..0000000000 --- a/test/functions.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import { identity, noop } from './utils.js'; -import functions from '../functions.js'; - -describe('functions', function() { - it('should return the function names of an object', function() { - var object = { 'a': 'a', 'b': identity, 'c': /x/, 'd': noop }, - actual = functions(object).sort(); - - assert.deepStrictEqual(actual, ['b', 'd']); - }); - - it('should not include inherited functions', function() { - function Foo() { - this.a = identity; - this.b = 'b'; - } - Foo.prototype.c = noop; - - assert.deepStrictEqual(functions(new Foo), ['a']); - }); -}); diff --git a/test/get-and-result.js b/test/get-and-result.js deleted file mode 100644 index 194f40f680..0000000000 --- a/test/get-and-result.js +++ /dev/null @@ -1,148 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, symbol, noop, numberProto, empties } from './utils.js'; - -describe('get and result', function() { - lodashStable.each(['get', 'result'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should get string keyed property values', function() { - var object = { 'a': 1 }; - - lodashStable.each(['a', ['a']], function(path) { - assert.strictEqual(func(object, path), 1); - }); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var object = { '-0': 'a', '0': 'b' }, - props = [-0, Object(-0), 0, Object(0)]; - - var actual = lodashStable.map(props, function(key) { - return func(object, key); - }); - - assert.deepStrictEqual(actual, ['a', 'a', 'b', 'b']); - }); - - it('`_.' + methodName + '` should get symbol keyed property values', function() { - if (Symbol) { - var object = {}; - object[symbol] = 1; - - assert.strictEqual(func(object, symbol), 1); - } - }); - - it('`_.' + methodName + '` should get deep property values', function() { - var object = { 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(func(object, path), 2); - }); - }); - - it('`_.' + methodName + '` should get a key over a path', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - assert.strictEqual(func(object, path), 1); - }); - }); - - it('`_.' + methodName + '` should not coerce array paths to strings', function() { - var object = { 'a,b,c': 3, 'a': { 'b': { 'c': 4 } } }; - assert.strictEqual(func(object, ['a', 'b', 'c']), 4); - }); - - it('`_.' + methodName + '` should not ignore empty brackets', function() { - var object = { 'a': { '': 1 } }; - assert.strictEqual(func(object, 'a[]'), 1); - }); - - it('`_.' + methodName + '` should handle empty paths', function() { - lodashStable.each([['', ''], [[], ['']]], function(pair) { - assert.strictEqual(func({}, pair[0]), undefined); - assert.strictEqual(func({ '': 3 }, pair[1]), 3); - }); - }); - - it('`_.' + methodName + '` should handle complex paths', function() { - var object = { 'a': { '-1.23': { '["b"]': { 'c': { "['d']": { '\ne\n': { 'f': { 'g': 8 } } } } } } } }; - - var paths = [ - 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', - ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'] - ]; - - lodashStable.each(paths, function(path) { - assert.strictEqual(func(object, path), 8); - }); - }); - - it('`_.' + methodName + '` should return `undefined` when `object` is nullish', function() { - lodashStable.each(['constructor', ['constructor']], function(path) { - assert.strictEqual(func(null, path), undefined); - assert.strictEqual(func(undefined, path), undefined); - }); - }); - - it('`_.' + methodName + '` should return `undefined` for deep paths when `object` is nullish', function() { - var values = [null, undefined], - expected = lodashStable.map(values, noop), - paths = ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']]; - - lodashStable.each(paths, function(path) { - var actual = lodashStable.map(values, function(value) { - return func(value, path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should return `undefined` if parts of `path` are missing', function() { - var object = { 'a': [, null] }; - - lodashStable.each(['a[1].b.c', ['a', '1', 'b', 'c']], function(path) { - assert.strictEqual(func(object, path), undefined); - }); - }); - - it('`_.' + methodName + '` should be able to return `null` values', function() { - var object = { 'a': { 'b': null } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(func(object, path), null); - }); - }); - - it('`_.' + methodName + '` should follow `path` over non-plain objects', function() { - var paths = ['a.b', ['a', 'b']]; - - lodashStable.each(paths, function(path) { - numberProto.a = { 'b': 2 }; - assert.strictEqual(func(0, path), 2); - delete numberProto.a; - }); - }); - - it('`_.' + methodName + '` should return the default value for `undefined` values', function() { - var object = { 'a': {} }, - values = empties.concat(true, new Date, 1, /x/, 'a'), - expected = lodashStable.map(values, function(value) { return [value, value]; }); - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var actual = lodashStable.map(values, function(value) { - return [func(object, path, value), func(null, path, value)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should return the default value when `path` is empty', function() { - assert.strictEqual(func({}, [], 'a'), 'a'); - }); - }); -}); diff --git a/test/get-and-result.spec.js b/test/get-and-result.spec.js new file mode 100644 index 0000000000..cd8fd3441c --- /dev/null +++ b/test/get-and-result.spec.js @@ -0,0 +1,155 @@ +import lodashStable from 'lodash'; +import { _, symbol, noop, numberProto, empties } from './utils'; + +describe('get and result', () => { + lodashStable.each(['get', 'result'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should get string keyed property values`, () => { + const object = { a: 1 }; + + lodashStable.each(['a', ['a']], (path) => { + expect(func(object, path)).toBe(1); + }); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const object = { '-0': 'a', 0: 'b' }; + const props = [-0, Object(-0), 0, Object(0)]; + + const actual = lodashStable.map(props, (key) => func(object, key)); + + expect(actual).toEqual(['a', 'a', 'b', 'b']); + }); + + it(`\`_.${methodName}\` should get symbol keyed property values`, () => { + if (Symbol) { + const object = {}; + object[symbol] = 1; + + expect(func(object, symbol)).toBe(1); + } + }); + + it(`\`_.${methodName}\` should get deep property values`, () => { + const object = { a: { b: 2 } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(func(object, path)).toBe(2); + }); + }); + + it(`\`_.${methodName}\` should get a key over a path`, () => { + const object = { 'a.b': 1, a: { b: 2 } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + expect(func(object, path)).toBe(1); + }); + }); + + it(`\`_.${methodName}\` should not coerce array paths to strings`, () => { + const object = { 'a,b,c': 3, a: { b: { c: 4 } } }; + expect(func(object, ['a', 'b', 'c'])).toBe(4); + }); + + it(`\`_.${methodName}\` should not ignore empty brackets`, () => { + const object = { a: { '': 1 } }; + expect(func(object, 'a[]')).toBe(1); + }); + + it(`\`_.${methodName}\` should handle empty paths`, () => { + lodashStable.each( + [ + ['', ''], + [[], ['']], + ], + (pair) => { + expect(func({}, pair[0])).toBe(undefined); + expect(func({ '': 3 }, pair[1])).toBe(3); + }, + ); + }); + + it(`\`_.${methodName}\` should handle complex paths`, () => { + const object = { + a: { '-1.23': { '["b"]': { c: { "['d']": { '\ne\n': { f: { g: 8 } } } } } } }, + }; + + const paths = [ + 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', + ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'], + ]; + + lodashStable.each(paths, (path) => { + expect(func(object, path)).toBe(8); + }); + }); + + it(`\`_.${methodName}\` should return \`undefined\` when \`object\` is nullish`, () => { + lodashStable.each(['constructor', ['constructor']], (path) => { + expect(func(null, path)).toBe(undefined); + expect(func(undefined, path)).toBe(undefined); + }); + }); + + it(`\`_.${methodName}\` should return \`undefined\` for deep paths when \`object\` is nullish`, () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, noop); + const paths = [ + 'constructor.prototype.valueOf', + ['constructor', 'prototype', 'valueOf'], + ]; + + lodashStable.each(paths, (path) => { + const actual = lodashStable.map(values, (value) => func(value, path)); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should return \`undefined\` if parts of \`path\` are missing`, () => { + const object = { a: [, null] }; + + lodashStable.each(['a[1].b.c', ['a', '1', 'b', 'c']], (path) => { + expect(func(object, path)).toBe(undefined); + }); + }); + + it(`\`_.${methodName}\` should be able to return \`null\` values`, () => { + const object = { a: { b: null } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(func(object, path)).toBe(null); + }); + }); + + it(`\`_.${methodName}\` should follow \`path\` over non-plain objects`, () => { + const paths = ['a.b', ['a', 'b']]; + + lodashStable.each(paths, (path) => { + numberProto.a = { b: 2 }; + expect(func(0, path)).toBe(2); + delete numberProto.a; + }); + }); + + it(`\`_.${methodName}\` should return the default value for \`undefined\` values`, () => { + const object = { a: {} }; + const values = empties.concat(true, new Date(), 1, /x/, 'a'); + const expected = lodashStable.map(values, (value) => [value, value]); + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const actual = lodashStable.map(values, (value) => [ + func(object, path, value), + func(null, path, value), + ]); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should return the default value when \`path\` is empty`, () => { + expect(func({}, [], 'a')).toBe('a'); + }); + }); +}); diff --git a/test/groupBy.js b/test/groupBy.js deleted file mode 100644 index e20b123162..0000000000 --- a/test/groupBy.js +++ /dev/null @@ -1,68 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE } from './utils.js'; -import groupBy from '../groupBy.js'; - -describe('groupBy', function() { - var array = [6.1, 4.2, 6.3]; - - it('should transform keys by `iteratee`', function() { - var actual = groupBy(array, Math.floor); - assert.deepStrictEqual(actual, { '4': [4.2], '6': [6.1, 6.3] }); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var array = [6, 4, 6], - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant({ '4': [4], '6': [6, 6] })); - - var actual = lodashStable.map(values, function(value, index) { - return index ? groupBy(array, value) : groupBy(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var actual = groupBy(['one', 'two', 'three'], 'length'); - assert.deepStrictEqual(actual, { '3': ['one', 'two'], '5': ['three'] }); - }); - - it('should only add values to own, not inherited, properties', function() { - var actual = groupBy(array, function(n) { - return Math.floor(n) > 4 ? 'hasOwnProperty' : 'constructor'; - }); - - assert.deepStrictEqual(actual.constructor, [4.2]); - assert.deepStrictEqual(actual.hasOwnProperty, [6.1, 6.3]); - }); - - it('should work with a number for `iteratee`', function() { - var array = [ - [1, 'a'], - [2, 'a'], - [2, 'b'] - ]; - - assert.deepStrictEqual(groupBy(array, 0), { '1': [[1, 'a']], '2': [[2, 'a'], [2, 'b']] }); - assert.deepStrictEqual(groupBy(array, 1), { 'a': [[1, 'a'], [2, 'a']], 'b': [[2, 'b']] }); - }); - - it('should work with an object for `collection`', function() { - var actual = groupBy({ 'a': 6.1, 'b': 4.2, 'c': 6.3 }, Math.floor); - assert.deepStrictEqual(actual, { '4': [4.2], '6': [6.1, 6.3] }); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE).concat( - lodashStable.range(Math.floor(LARGE_ARRAY_SIZE / 2), LARGE_ARRAY_SIZE), - lodashStable.range(Math.floor(LARGE_ARRAY_SIZE / 1.5), LARGE_ARRAY_SIZE) - ); - - var iteratee = function(value) { value.push(value[0]); return value; }, - predicate = function(value) { return isEven(value[0]); }, - actual = _(array).groupBy().map(iteratee).filter(predicate).take().value(); - - assert.deepEqual(actual, _.take(_.filter(lodashStable.map(groupBy(array), iteratee), predicate))); - }); -}); diff --git a/test/groupBy.spec.js b/test/groupBy.spec.js new file mode 100644 index 0000000000..672af7ee78 --- /dev/null +++ b/test/groupBy.spec.js @@ -0,0 +1,65 @@ +import lodashStable from 'lodash'; +import groupBy from '../src/groupBy'; + +describe('groupBy', () => { + const array = [6.1, 4.2, 6.3]; + + it('should transform keys by `iteratee`', () => { + const actual = groupBy(array, Math.floor); + expect(actual).toEqual({ 4: [4.2], 6: [6.1, 6.3] }); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const array = [6, 4, 6]; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant({ 4: [4], 6: [6, 6] })); + + const actual = lodashStable.map(values, (value, index) => + index ? groupBy(array, value) : groupBy(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const actual = groupBy(['one', 'two', 'three'], 'length'); + expect(actual).toEqual({ 3: ['one', 'two'], 5: ['three'] }); + }); + + it('should only add values to own, not inherited, properties', () => { + const actual = groupBy(array, (n) => + Math.floor(n) > 4 ? 'hasOwnProperty' : 'constructor', + ); + + expect(actual.constructor).toEqual([4.2]); + expect(actual.hasOwnProperty).toEqual([6.1, 6.3]); + }); + + it('should work with a number for `iteratee`', () => { + const array = [ + [1, 'a'], + [2, 'a'], + [2, 'b'], + ]; + + assert.deepStrictEqual(groupBy(array, 0), { + 1: [[1, 'a']], + 2: [ + [2, 'a'], + [2, 'b'], + ], + }); + assert.deepStrictEqual(groupBy(array, 1), { + a: [ + [1, 'a'], + [2, 'a'], + ], + b: [[2, 'b']], + }); + }); + + it('should work with an object for `collection`', () => { + const actual = groupBy({ a: 6.1, b: 4.2, c: 6.3 }, Math.floor); + expect(actual).toEqual({ 4: [4.2], 6: [6.1, 6.3] }); + }); +}); diff --git a/test/gt.spec.js b/test/gt.spec.js new file mode 100644 index 0000000000..8d601c6aee --- /dev/null +++ b/test/gt.spec.js @@ -0,0 +1,15 @@ +import gt from '../src/gt'; + +describe('gt', () => { + it('should return `true` if `value` > `other`', () => { + expect(gt(3, 1)).toBe(true); + expect(gt('def', 'abc')).toBe(true); + }); + + it('should return `false` if `value` is <= `other`', () => { + expect(gt(1, 3)).toBe(false); + expect(gt(3, 3)).toBe(false); + expect(gt('abc', 'def')).toBe(false); + expect(gt('def', 'def')).toBe(false); + }); +}); diff --git a/test/gt.test.js b/test/gt.test.js deleted file mode 100644 index c46995770c..0000000000 --- a/test/gt.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import assert from 'assert'; -import gt from '../gt.js'; - -describe('gt', function() { - it('should return `true` if `value` > `other`', function() { - assert.strictEqual(gt(3, 1), true); - assert.strictEqual(gt('def', 'abc'), true); - }); - - it('should return `false` if `value` is <= `other`', function() { - assert.strictEqual(gt(1, 3), false); - assert.strictEqual(gt(3, 3), false); - assert.strictEqual(gt('abc', 'def'), false); - assert.strictEqual(gt('def', 'def'), false); - }); -}); diff --git a/test/gte.spec.js b/test/gte.spec.js new file mode 100644 index 0000000000..5d9008a858 --- /dev/null +++ b/test/gte.spec.js @@ -0,0 +1,15 @@ +import gte from '../src/gte'; + +describe('gte', () => { + it('should return `true` if `value` >= `other`', () => { + expect(gte(3, 1)).toBe(true); + expect(gte(3, 3)).toBe(true); + expect(gte('def', 'abc')).toBe(true); + expect(gte('def', 'def')).toBe(true); + }); + + it('should return `false` if `value` is less than `other`', () => { + expect(gte(1, 3)).toBe(false); + expect(gte('abc', 'def')).toBe(false); + }); +}); diff --git a/test/gte.test.js b/test/gte.test.js deleted file mode 100644 index d49ffaa2bd..0000000000 --- a/test/gte.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import assert from 'assert'; -import gte from '../gte.js'; - -describe('gte', function() { - it('should return `true` if `value` >= `other`', function() { - assert.strictEqual(gte(3, 1), true); - assert.strictEqual(gte(3, 3), true); - assert.strictEqual(gte('def', 'abc'), true); - assert.strictEqual(gte('def', 'def'), true); - }); - - it('should return `false` if `value` is less than `other`', function() { - assert.strictEqual(gte(1, 3), false); - assert.strictEqual(gte('abc', 'def'), false); - }); -}); diff --git a/test/has-methods.js b/test/has-methods.js deleted file mode 100644 index ebd01b7cb9..0000000000 --- a/test/has-methods.js +++ /dev/null @@ -1,205 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, toArgs, stubTrue, args, symbol, defineProperty, stubFalse } from './utils.js'; - -describe('has methods', function() { - lodashStable.each(['has', 'hasIn'], function(methodName) { - var func = _[methodName], - isHas = methodName == 'has', - sparseArgs = toArgs([1]), - sparseArray = Array(1), - sparseString = Object('a'); - - delete sparseArgs[0]; - delete sparseString[0]; - - it('`_.' + methodName + '` should check for own properties', function() { - var object = { 'a': 1 }; - - lodashStable.each(['a', ['a']], function(path) { - assert.strictEqual(func(object, path), true); - }); - }); - - it('`_.' + methodName + '` should not use the `hasOwnProperty` method of `object`', function() { - var object = { 'hasOwnProperty': null, 'a': 1 }; - assert.strictEqual(func(object, 'a'), true); - }); - - it('`_.' + methodName + '` should support deep paths', function() { - var object = { 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(func(object, path), true); - }); - - lodashStable.each(['a.a', ['a', 'a']], function(path) { - assert.strictEqual(func(object, path), false); - }); - }); - - it('`_.' + methodName + '` should coerce `path` to a string', function() { - function fn() {} - fn.toString = lodashStable.constant('fn'); - - var object = { 'null': 1 , 'undefined': 2, 'fn': 3, '[object Object]': 4 }, - paths = [null, undefined, fn, {}], - expected = lodashStable.map(paths, stubTrue); - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(paths, function(path) { - return func(object, index ? [path] : path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should work with `arguments` objects', function() { - assert.strictEqual(func(args, 1), true); - }); - - it('`_.' + methodName + '` should work with a non-string `path`', function() { - var array = [1, 2, 3]; - - lodashStable.each([1, [1]], function(path) { - assert.strictEqual(func(array, path), true); - }); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var object = { '-0': 'a', '0': 'b' }, - props = [-0, Object(-0), 0, Object(0)], - expected = lodashStable.map(props, stubTrue); - - var actual = lodashStable.map(props, function(key) { - return func(object, key); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with a symbol `path`', function() { - function Foo() {} - - if (Symbol) { - Foo.prototype[symbol] = 1; - - var symbol2 = Symbol('b'); - defineProperty(Foo.prototype, symbol2, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': 2 - }); - - var object = isHas ? Foo.prototype : new Foo; - assert.strictEqual(func(object, symbol), true); - assert.strictEqual(func(object, symbol2), true); - } - }); - - it('`_.' + methodName + '` should check for a key over a path', function() { - var object = { 'a.b': 1 }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - assert.strictEqual(func(object, path), true); - }); - }); - - it('`_.' + methodName + '` should return `true` for indexes of sparse values', function() { - var values = [sparseArgs, sparseArray, sparseString], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return func(value, 0); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `true` for indexes of sparse values with deep paths', function() { - var values = [sparseArgs, sparseArray, sparseString], - expected = lodashStable.map(values, lodashStable.constant([true, true])); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.map(['a[0]', ['a', '0']], function(path) { - return func({ 'a': value }, path); - }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `' + (isHas ? 'false' : 'true') + '` for inherited properties', function() { - function Foo() {} - Foo.prototype.a = 1; - - lodashStable.each(['a', ['a']], function(path) { - assert.strictEqual(func(new Foo, path), !isHas); - }); - }); - - it('`_.' + methodName + '` should return `' + (isHas ? 'false' : 'true') + '` for nested inherited properties', function() { - function Foo() {} - Foo.prototype.a = { 'b': 1 }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(func(new Foo, path), !isHas); - }); - }); - - it('`_.' + methodName + '` should return `false` when `object` is nullish', function() { - var values = [null, undefined], - expected = lodashStable.map(values, stubFalse); - - lodashStable.each(['constructor', ['constructor']], function(path) { - var actual = lodashStable.map(values, function(value) { - return func(value, path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should return `false` for deep paths when `object` is nullish', function() { - var values = [null, undefined], - expected = lodashStable.map(values, stubFalse); - - lodashStable.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) { - var actual = lodashStable.map(values, function(value) { - return func(value, path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should return `false` for nullish values of nested objects', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse); - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var actual = lodashStable.map(values, function(value, index) { - var object = index ? { 'a': value } : {}; - return func(object, path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should return `false` over sparse values of deep paths', function() { - var values = [sparseArgs, sparseArray, sparseString], - expected = lodashStable.map(values, lodashStable.constant([false, false])); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.map(['a[0].b', ['a', '0', 'b']], function(path) { - return func({ 'a': value }, path); - }); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/has-methods.spec.js b/test/has-methods.spec.js new file mode 100644 index 0000000000..ec52c6993b --- /dev/null +++ b/test/has-methods.spec.js @@ -0,0 +1,199 @@ +import lodashStable from 'lodash'; +import { _, toArgs, stubTrue, args, symbol, defineProperty, stubFalse } from './utils'; + +describe('has methods', () => { + lodashStable.each(['has', 'hasIn'], (methodName) => { + const func = _[methodName]; + const isHas = methodName === 'has'; + const sparseArgs = toArgs([1]); + const sparseArray = Array(1); + const sparseString = Object('a'); + + delete sparseArgs[0]; + delete sparseString[0]; + + it(`\`_.${methodName}\` should check for own properties`, () => { + const object = { a: 1 }; + + lodashStable.each(['a', ['a']], (path) => { + expect(func(object, path)).toBe(true); + }); + }); + + it(`\`_.${methodName}\` should not use the \`hasOwnProperty\` method of \`object\``, () => { + const object = { hasOwnProperty: null, a: 1 }; + expect(func(object, 'a')).toBe(true); + }); + + it(`\`_.${methodName}\` should support deep paths`, () => { + const object = { a: { b: 2 } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(func(object, path)).toBe(true); + }); + + lodashStable.each(['a.a', ['a', 'a']], (path) => { + expect(func(object, path)).toBe(false); + }); + }); + + it(`\`_.${methodName}\` should coerce \`path\` to a string`, () => { + function fn() {} + fn.toString = lodashStable.constant('fn'); + + const object = { null: 1, undefined: 2, fn: 3, '[object Object]': 4 }; + const paths = [null, undefined, fn, {}]; + const expected = lodashStable.map(paths, stubTrue); + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(paths, (path) => + func(object, index ? [path] : path), + ); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should work with \`arguments\` objects`, () => { + expect(func(args, 1)).toBe(true); + }); + + it(`\`_.${methodName}\` should work with a non-string \`path\``, () => { + const array = [1, 2, 3]; + + lodashStable.each([1, [1]], (path) => { + expect(func(array, path)).toBe(true); + }); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const object = { '-0': 'a', 0: 'b' }; + const props = [-0, Object(-0), 0, Object(0)]; + const expected = lodashStable.map(props, stubTrue); + + const actual = lodashStable.map(props, (key) => func(object, key)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with a symbol \`path\``, () => { + function Foo() {} + + if (Symbol) { + Foo.prototype[symbol] = 1; + + const symbol2 = Symbol('b'); + defineProperty(Foo.prototype, symbol2, { + configurable: true, + enumerable: false, + writable: true, + value: 2, + }); + + const object = isHas ? Foo.prototype : new Foo(); + expect(func(object, symbol)).toBe(true); + expect(func(object, symbol2)).toBe(true); + } + }); + + it(`\`_.${methodName}\` should check for a key over a path`, () => { + const object = { 'a.b': 1 }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + expect(func(object, path)).toBe(true); + }); + }); + + it(`\`_.${methodName}\` should return \`true\` for indexes of sparse values`, () => { + const values = [sparseArgs, sparseArray, sparseString]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => func(value, 0)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`true\` for indexes of sparse values with deep paths`, () => { + const values = [sparseArgs, sparseArray, sparseString]; + const expected = lodashStable.map(values, lodashStable.constant([true, true])); + + const actual = lodashStable.map(values, (value) => + lodashStable.map(['a[0]', ['a', '0']], (path) => func({ a: value }, path)), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`${ + isHas ? 'false' : 'true' + }\` for inherited properties`, () => { + function Foo() {} + Foo.prototype.a = 1; + + lodashStable.each(['a', ['a']], (path) => { + expect(func(new Foo(), path)).toBe(!isHas); + }); + }); + + it(`\`_.${methodName}\` should return \`${ + isHas ? 'false' : 'true' + }\` for nested inherited properties`, () => { + function Foo() {} + Foo.prototype.a = { b: 1 }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(func(new Foo(), path)).toBe(!isHas); + }); + }); + + it(`\`_.${methodName}\` should return \`false\` when \`object\` is nullish`, () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, stubFalse); + + lodashStable.each(['constructor', ['constructor']], (path) => { + const actual = lodashStable.map(values, (value) => func(value, path)); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should return \`false\` for deep paths when \`object\` is nullish`, () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, stubFalse); + + lodashStable.each( + ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], + (path) => { + const actual = lodashStable.map(values, (value) => func(value, path)); + + expect(actual).toEqual(expected); + }, + ); + }); + + it(`\`_.${methodName}\` should return \`false\` for nullish values of nested objects`, () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubFalse); + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const actual = lodashStable.map(values, (value, index) => { + const object = index ? { a: value } : {}; + return func(object, path); + }); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should return \`false\` over sparse values of deep paths`, () => { + const values = [sparseArgs, sparseArray, sparseString]; + const expected = lodashStable.map(values, lodashStable.constant([false, false])); + + const actual = lodashStable.map(values, (value) => + lodashStable.map(['a[0].b', ['a', '0', 'b']], (path) => func({ a: value }, path)), + ); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/head.js b/test/head.js deleted file mode 100644 index c04a788d5f..0000000000 --- a/test/head.js +++ /dev/null @@ -1,62 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { arrayProto, LARGE_ARRAY_SIZE } from './utils.js'; -import head from '../head.js'; -import first from '../first.js'; - -describe('head', function() { - var array = [1, 2, 3, 4]; - - it('should return the first element', function() { - assert.strictEqual(head(array), 1); - }); - - it('should return `undefined` when querying empty arrays', function() { - arrayProto[0] = 1; - assert.strictEqual(head([]), undefined); - arrayProto.length = 0; - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - actual = lodashStable.map(array, head); - - assert.deepStrictEqual(actual, [1, 4, 7]); - }); - - it('should be aliased', function() { - assert.strictEqual(first, head); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - var wrapped = _(array); - assert.strictEqual(wrapped.head(), 1); - assert.strictEqual(wrapped.first(), 1); - }); - - it('should return a wrapped value when explicitly chaining', function() { - var wrapped = _(array).chain(); - assert.ok(wrapped.head() instanceof _); - assert.ok(wrapped.first() instanceof _); - }); - - it('should not execute immediately when explicitly chaining', function() { - var wrapped = _(array).chain(); - assert.strictEqual(wrapped.head().__wrapped__, array); - assert.strictEqual(wrapped.first().__wrapped__, array); - }); - - it('should work in a lazy sequence', function() { - var largeArray = lodashStable.range(LARGE_ARRAY_SIZE), - smallArray = array; - - lodashStable.each(['head', 'first'], function(methodName) { - lodashStable.times(2, function(index) { - var array = index ? largeArray : smallArray, - actual = _(array).filter(isEven)[methodName](); - - assert.strictEqual(actual, _[methodName](_.filter(array, isEven))); - }); - }); - }); -}); diff --git a/test/head.spec.js b/test/head.spec.js new file mode 100644 index 0000000000..e8fbf1c198 --- /dev/null +++ b/test/head.spec.js @@ -0,0 +1,33 @@ +import lodashStable from 'lodash'; +import { arrayProto } from './utils'; +import head from '../src/head'; +import first from '../src/first'; + +describe('head', () => { + const array = [1, 2, 3, 4]; + + it('should return the first element', () => { + expect(head(array)).toBe(1); + }); + + it('should return `undefined` when querying empty arrays', () => { + arrayProto[0] = 1; + expect(head([])).toBe(undefined); + arrayProto.length = 0; + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]; + const actual = lodashStable.map(array, head); + + expect(actual).toEqual([1, 4, 7]); + }); + + it('should be aliased', () => { + expect(first).toBe(head); + }); +}); diff --git a/test/identity.js b/test/identity.js deleted file mode 100644 index ced20b853a..0000000000 --- a/test/identity.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import identity from '../identity.js'; - -describe('identity', function() { - it('should return the first argument given', function() { - var object = { 'name': 'fred' }; - assert.strictEqual(identity(object), object); - }); -}); diff --git a/test/identity.spec.js b/test/identity.spec.js new file mode 100644 index 0000000000..744977f459 --- /dev/null +++ b/test/identity.spec.js @@ -0,0 +1,8 @@ +import identity from '../src/identity'; + +describe('identity', () => { + it('should return the first argument given', () => { + const object = { name: 'fred' }; + expect(identity(object)).toBe(object); + }); +}); diff --git a/test/inRange.js b/test/inRange.js deleted file mode 100644 index 2efe128c4d..0000000000 --- a/test/inRange.js +++ /dev/null @@ -1,54 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; -import inRange from '../inRange.js'; - -describe('inRange', function() { - it('should work with an `end`', function() { - assert.strictEqual(inRange(3, 5), true); - assert.strictEqual(inRange(5, 5), false); - assert.strictEqual(inRange(6, 5), false); - }); - - it('should work with a `start` and `end`', function() { - assert.strictEqual(inRange(1, 1, 5), true); - assert.strictEqual(inRange(3, 1, 5), true); - assert.strictEqual(inRange(0, 1, 5), false); - assert.strictEqual(inRange(5, 1, 5), false); - }); - - it('should treat falsey `start` as `0`', function() { - lodashStable.each(falsey, function(value, index) { - if (index) { - assert.strictEqual(inRange(0, value), false); - assert.strictEqual(inRange(0, value, 1), true); - } else { - assert.strictEqual(inRange(0), false); - } - }); - }); - - it('should swap `start` and `end` when `start` > `end`', function() { - assert.strictEqual(inRange(2, 5, 1), true); - assert.strictEqual(inRange(-3, -2, -6), true); - }); - - it('should work with a floating point `n` value', function() { - assert.strictEqual(inRange(0.5, 5), true); - assert.strictEqual(inRange(1.2, 1, 5), true); - assert.strictEqual(inRange(5.2, 5), false); - assert.strictEqual(inRange(0.5, 1, 5), false); - }); - - it('should coerce arguments to finite numbers', function() { - var actual = [ - inRange(0, '1'), - inRange(0, '0', 1), - inRange(0, 0, '1'), - inRange(0, NaN, 1), - inRange(-1, -1, NaN) - ]; - - assert.deepStrictEqual(actual, lodashStable.map(actual, stubTrue)); - }); -}); diff --git a/test/inRange.spec.js b/test/inRange.spec.js new file mode 100644 index 0000000000..6359ebcf7c --- /dev/null +++ b/test/inRange.spec.js @@ -0,0 +1,53 @@ +import lodashStable from 'lodash'; +import { falsey, stubTrue } from './utils'; +import inRange from '../src/inRange'; + +describe('inRange', () => { + it('should work with an `end`', () => { + expect(inRange(3, 5)).toBe(true); + expect(inRange(5, 5)).toBe(false); + expect(inRange(6, 5)).toBe(false); + }); + + it('should work with a `start` and `end`', () => { + expect(inRange(1, 1, 5)).toBe(true); + expect(inRange(3, 1, 5)).toBe(true); + expect(inRange(0, 1, 5)).toBe(false); + expect(inRange(5, 1, 5)).toBe(false); + }); + + it('should treat falsey `start` as `0`', () => { + lodashStable.each(falsey, (value, index) => { + if (index) { + expect(inRange(0, value)).toBe(false); + expect(inRange(0, value, 1)).toBe(true); + } else { + expect(inRange(0)).toBe(false); + } + }); + }); + + it('should swap `start` and `end` when `start` > `end`', () => { + expect(inRange(2, 5, 1)).toBe(true); + expect(inRange(-3, -2, -6)).toBe(true); + }); + + it('should work with a floating point `n` value', () => { + expect(inRange(0.5, 5)).toBe(true); + expect(inRange(1.2, 1, 5)).toBe(true); + expect(inRange(5.2, 5)).toBe(false); + expect(inRange(0.5, 1, 5)).toBe(false); + }); + + it('should coerce arguments to finite numbers', () => { + const actual = [ + inRange(0, '1'), + inRange(0, '0', 1), + inRange(0, 0, '1'), + inRange(0, NaN, 1), + inRange(-1, -1, NaN), + ]; + + expect(actual, lodashStable.map(actual).toEqual(stubTrue)); + }); +}); diff --git a/test/includes.js b/test/includes.js deleted file mode 100644 index b9147a80c6..0000000000 --- a/test/includes.js +++ /dev/null @@ -1,95 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, stubFalse } from './utils.js'; -import includes from '../includes.js'; - -describe('includes', function() { - (function() { - lodashStable.each({ - 'an `arguments` object': arguments, - 'an array': [1, 2, 3, 4], - 'an object': { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, - 'a string': '1234' - }, - function(collection, key) { - it('should work with ' + key + ' and return `true` for matched values', function() { - assert.strictEqual(includes(collection, 3), true); - }); - - it('should work with ' + key + ' and return `false` for unmatched values', function() { - assert.strictEqual(includes(collection, 5), false); - }); - - it('should work with ' + key + ' and floor `position` values', function() { - assert.strictEqual(includes(collection, 2, 1.2), true); - }); - - it('should work with ' + key + ' and return an unwrapped value implicitly when chaining', function() { - assert.strictEqual(_(collection).includes(3), true); - }); - - it('should work with ' + key + ' and return a wrapped value when explicitly chaining', function() { - assert.ok(_(collection).chain().includes(3) instanceof _); - }); - }); - - lodashStable.each({ - 'literal': 'abc', - 'object': Object('abc') - }, - function(collection, key) { - it('should work with a string ' + key + ' for `collection`', function() { - assert.strictEqual(includes(collection, 'bc'), true); - assert.strictEqual(includes(collection, 'd'), false); - }); - }); - - it('should return `false` for empty collections', function() { - var expected = lodashStable.map(empties, stubFalse); - - var actual = lodashStable.map(empties, function(value) { - try { - return includes(value); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with a string and a `fromIndex` >= `length`', function() { - var string = '1234', - length = string.length, - indexes = [4, 6, Math.pow(2, 32), Infinity]; - - var expected = lodashStable.map(indexes, function(index) { - return [false, false, index == length]; - }); - - var actual = lodashStable.map(indexes, function(fromIndex) { - return [ - includes(string, 1, fromIndex), - includes(string, undefined, fromIndex), - includes(string, '', fromIndex) - ]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should match `NaN`', function() { - assert.strictEqual(includes([1, NaN, 3], NaN), true); - }); - - it('should match `-0` as `0`', function() { - assert.strictEqual(includes([-0], 0), true); - assert.strictEqual(includes([0], -0), true); - }); - - it('should work as an iteratee for methods like `_.every`', function() { - var array = [2, 3, 1], - values = [1, 2, 3]; - - assert.ok(lodashStable.every(values, lodashStable.partial(includes, array))); - }); - })(1, 2, 3, 4); -}); diff --git a/test/includes.spec.js b/test/includes.spec.js new file mode 100644 index 0000000000..11c7e391c8 --- /dev/null +++ b/test/includes.spec.js @@ -0,0 +1,94 @@ +import lodashStable from 'lodash'; +import { empties, stubFalse } from './utils'; +import includes from '../src/includes'; + +describe('includes', () => { + (function () { + lodashStable.each( + { + 'an `arguments` object': arguments, + 'an array': [1, 2, 3, 4], + 'an object': { a: 1, b: 2, c: 3, d: 4 }, + 'a string': '1234', + }, + (collection, key) => { + it(`should work with ${key} and return \`true\` for matched values`, () => { + expect(includes(collection, 3)).toBe(true); + }); + + it(`should work with ${key} and return \`false\` for unmatched values`, () => { + expect(includes(collection, 5)).toBe(false); + }); + + it(`should work with ${key} and floor \`position\` values`, () => { + expect(includes(collection, 2, 1.2)).toBe(true); + }); + + it(`should work with ${key} and return an unwrapped value implicitly when chaining`, () => { + expect(_(collection).includes(3)).toBe(true); + }); + + it(`should work with ${key} and return a wrapped value when explicitly chaining`, () => { + expect(_(collection).chain().includes(3) instanceof _); + }); + }, + ); + + lodashStable.each( + { + literal: 'abc', + object: Object('abc'), + }, + (collection, key) => { + it(`should work with a string ${key} for \`collection\``, () => { + expect(includes(collection, 'bc')).toBe(true); + expect(includes(collection, 'd')).toBe(false); + }); + }, + ); + + it('should return `false` for empty collections', () => { + const expected = lodashStable.map(empties, stubFalse); + + const actual = lodashStable.map(empties, (value) => { + try { + return includes(value); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should work with a string and a `fromIndex` >= `length`', () => { + const string = '1234'; + const length = string.length; + const indexes = [4, 6, 2 ** 32, Infinity]; + + const expected = lodashStable.map(indexes, (index) => [false, false, index === length]); + + const actual = lodashStable.map(indexes, (fromIndex) => [ + includes(string, 1, fromIndex), + includes(string, undefined, fromIndex), + includes(string, '', fromIndex), + ]); + + expect(actual).toEqual(expected); + }); + + it('should match `NaN`', () => { + expect(includes([1, NaN, 3], NaN)).toBe(true); + }); + + it('should match `-0` as `0`', () => { + expect(includes([-0], 0)).toBe(true); + expect(includes([0], -0)).toBe(true); + }); + + it('should work as an iteratee for methods like `_.every`', () => { + const array = [2, 3, 1]; + const values = [1, 2, 3]; + + expect(lodashStable.every(values, lodashStable.partial(includes, array))); + }); + })(1, 2, 3, 4); +}); diff --git a/test/indexOf-methods.js b/test/indexOf-methods.js deleted file mode 100644 index 05b506f338..0000000000 --- a/test/indexOf-methods.js +++ /dev/null @@ -1,63 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, falsey } from './utils.js'; - -describe('indexOf methods', function() { - lodashStable.each(['indexOf', 'lastIndexOf', 'sortedIndexOf', 'sortedLastIndexOf'], function(methodName) { - var func = _[methodName], - isIndexOf = !/last/i.test(methodName), - isSorted = /^sorted/.test(methodName); - - it('`_.' + methodName + '` should accept a falsey `array`', function() { - var expected = lodashStable.map(falsey, lodashStable.constant(-1)); - - var actual = lodashStable.map(falsey, function(array, index) { - try { - return index ? func(array) : func(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `-1` for an unmatched value', function() { - var array = [1, 2, 3], - empty = []; - - assert.strictEqual(func(array, 4), -1); - assert.strictEqual(func(array, 4, true), -1); - assert.strictEqual(func(array, undefined, true), -1); - - assert.strictEqual(func(empty, undefined), -1); - assert.strictEqual(func(empty, undefined, true), -1); - }); - - it('`_.' + methodName + '` should not match values on empty arrays', function() { - var array = []; - array[-1] = 0; - - assert.strictEqual(func(array, undefined), -1); - assert.strictEqual(func(array, 0, true), -1); - }); - - it('`_.' + methodName + '` should match `NaN`', function() { - var array = isSorted - ? [1, 2, NaN, NaN] - : [1, NaN, 3, NaN, 5, NaN]; - - if (isSorted) { - assert.strictEqual(func(array, NaN, true), isIndexOf ? 2 : 3); - } - else { - assert.strictEqual(func(array, NaN), isIndexOf ? 1 : 5); - assert.strictEqual(func(array, NaN, 2), isIndexOf ? 3 : 1); - assert.strictEqual(func(array, NaN, -2), isIndexOf ? 5 : 3); - } - }); - - it('`_.' + methodName + '` should match `-0` as `0`', function() { - assert.strictEqual(func([-0], 0), 0); - assert.strictEqual(func([0], -0), 0); - }); - }); -}); diff --git a/test/indexOf-methods.spec.js b/test/indexOf-methods.spec.js new file mode 100644 index 0000000000..2c964b7f92 --- /dev/null +++ b/test/indexOf-methods.spec.js @@ -0,0 +1,62 @@ +import lodashStable from 'lodash'; +import { _, falsey } from './utils'; + +describe('indexOf methods', () => { + lodashStable.each( + ['indexOf', 'lastIndexOf', 'sortedIndexOf', 'sortedLastIndexOf'], + (methodName) => { + const func = _[methodName]; + const isIndexOf = !/last/i.test(methodName); + const isSorted = /^sorted/.test(methodName); + + it(`\`_.${methodName}\` should accept a falsey \`array\``, () => { + const expected = lodashStable.map(falsey, lodashStable.constant(-1)); + + const actual = lodashStable.map(falsey, (array, index) => { + try { + return index ? func(array) : func(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`-1\` for an unmatched value`, () => { + const array = [1, 2, 3]; + const empty = []; + + expect(func(array, 4)).toBe(-1); + expect(func(array, 4, true)).toBe(-1); + expect(func(array, undefined, true)).toBe(-1); + + expect(func(empty, undefined)).toBe(-1); + expect(func(empty, undefined, true)).toBe(-1); + }); + + it(`\`_.${methodName}\` should not match values on empty arrays`, () => { + const array = []; + array[-1] = 0; + + expect(func(array, undefined)).toBe(-1); + expect(func(array, 0, true)).toBe(-1); + }); + + it(`\`_.${methodName}\` should match \`NaN\``, () => { + const array = isSorted ? [1, 2, NaN, NaN] : [1, NaN, 3, NaN, 5, NaN]; + + if (isSorted) { + expect(func(array, NaN, true)).toBe(isIndexOf ? 2 : 3); + } else { + expect(func(array, NaN)).toBe(isIndexOf ? 1 : 5); + expect(func(array, NaN, 2)).toBe(isIndexOf ? 3 : 1); + expect(func(array, NaN, -2)).toBe(isIndexOf ? 5 : 3); + } + }); + + it(`\`_.${methodName}\` should match \`-0\` as \`0\``, () => { + expect(func([-0], 0)).toBe(0); + expect(func([0], -0)).toBe(0); + }); + }, + ); +}); diff --git a/test/indexOf.spec.js b/test/indexOf.spec.js new file mode 100644 index 0000000000..a36566e2c0 --- /dev/null +++ b/test/indexOf.spec.js @@ -0,0 +1,53 @@ +import lodashStable from 'lodash'; +import { stubZero, falsey } from './utils'; +import indexOf from '../src/indexOf'; + +describe('indexOf', () => { + const array = [1, 2, 3, 1, 2, 3]; + + it('`_.indexOf` should return the index of the first matched value', () => { + expect(indexOf(array, 3)).toBe(2); + }); + + it('`_.indexOf` should work with a positive `fromIndex`', () => { + expect(indexOf(array, 1, 2)).toBe(3); + }); + + it('`_.indexOf` should work with a `fromIndex` >= `length`', () => { + const values = [6, 8, 2 ** 32, Infinity]; + const expected = lodashStable.map(values, lodashStable.constant([-1, -1, -1])); + + const actual = lodashStable.map(values, (fromIndex) => [ + indexOf(array, undefined, fromIndex), + indexOf(array, 1, fromIndex), + indexOf(array, '', fromIndex), + ]); + + expect(actual).toEqual(expected); + }); + + it('`_.indexOf` should work with a negative `fromIndex`', () => { + expect(indexOf(array, 2, -3)).toBe(4); + }); + + it('`_.indexOf` should work with a negative `fromIndex` <= `-length`', () => { + const values = [-6, -8, -Infinity]; + const expected = lodashStable.map(values, stubZero); + + const actual = lodashStable.map(values, (fromIndex) => indexOf(array, 1, fromIndex)); + + expect(actual).toEqual(expected); + }); + + it('`_.indexOf` should treat falsey `fromIndex` values as `0`', () => { + const expected = lodashStable.map(falsey, stubZero); + + const actual = lodashStable.map(falsey, (fromIndex) => indexOf(array, 1, fromIndex)); + + expect(actual).toEqual(expected); + }); + + it('`_.indexOf` should coerce `fromIndex` to an integer', () => { + expect(indexOf(array, 2, 1.2)).toBe(1); + }); +}); diff --git a/test/indexOf.test.js b/test/indexOf.test.js deleted file mode 100644 index 49fc808618..0000000000 --- a/test/indexOf.test.js +++ /dev/null @@ -1,60 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubZero, falsey } from './utils.js'; -import indexOf from '../indexOf.js'; - -describe('indexOf', function() { - var array = [1, 2, 3, 1, 2, 3]; - - it('`_.indexOf` should return the index of the first matched value', function() { - assert.strictEqual(indexOf(array, 3), 2); - }); - - it('`_.indexOf` should work with a positive `fromIndex`', function() { - assert.strictEqual(indexOf(array, 1, 2), 3); - }); - - it('`_.indexOf` should work with a `fromIndex` >= `length`', function() { - var values = [6, 8, Math.pow(2, 32), Infinity], - expected = lodashStable.map(values, lodashStable.constant([-1, -1, -1])); - - var actual = lodashStable.map(values, function(fromIndex) { - return [ - indexOf(array, undefined, fromIndex), - indexOf(array, 1, fromIndex), - indexOf(array, '', fromIndex) - ]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.indexOf` should work with a negative `fromIndex`', function() { - assert.strictEqual(indexOf(array, 2, -3), 4); - }); - - it('`_.indexOf` should work with a negative `fromIndex` <= `-length`', function() { - var values = [-6, -8, -Infinity], - expected = lodashStable.map(values, stubZero); - - var actual = lodashStable.map(values, function(fromIndex) { - return indexOf(array, 1, fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.indexOf` should treat falsey `fromIndex` values as `0`', function() { - var expected = lodashStable.map(falsey, stubZero); - - var actual = lodashStable.map(falsey, function(fromIndex) { - return indexOf(array, 1, fromIndex); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.indexOf` should coerce `fromIndex` to an integer', function() { - assert.strictEqual(indexOf(array, 2, 1.2), 1); - }); -}); diff --git a/test/initial.js b/test/initial.js deleted file mode 100644 index c2891ca85c..0000000000 --- a/test/initial.js +++ /dev/null @@ -1,61 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubArray, LARGE_ARRAY_SIZE } from './utils.js'; -import initial from '../initial.js'; - -describe('initial', function() { - var array = [1, 2, 3]; - - it('should accept a falsey `array`', function() { - var expected = lodashStable.map(falsey, stubArray); - - var actual = lodashStable.map(falsey, function(array, index) { - try { - return index ? initial(array) : initial(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should exclude last element', function() { - assert.deepStrictEqual(initial(array), [1, 2]); - }); - - it('should return an empty when querying empty arrays', function() { - assert.deepStrictEqual(initial([]), []); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - actual = lodashStable.map(array, initial); - - assert.deepStrictEqual(actual, [[1, 2], [4, 5], [7, 8]]); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - values = []; - - var actual = _(array).initial().filter(function(value) { - values.push(value); - return false; - }) - .value(); - - assert.deepEqual(actual, []); - assert.deepEqual(values, initial(array)); - - values = []; - - actual = _(array).filter(function(value) { - values.push(value); - return isEven(value); - }) - .initial() - .value(); - - assert.deepEqual(actual, initial(lodashStable.filter(array, isEven))); - assert.deepEqual(values, array); - }); -}); diff --git a/test/initial.spec.js b/test/initial.spec.js new file mode 100644 index 0000000000..f704280a68 --- /dev/null +++ b/test/initial.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import { falsey, stubArray } from './utils'; +import initial from '../src/initial'; + +describe('initial', () => { + const array = [1, 2, 3]; + + it('should accept a falsey `array`', () => { + const expected = lodashStable.map(falsey, stubArray); + + const actual = lodashStable.map(falsey, (array, index) => { + try { + return index ? initial(array) : initial(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should exclude last element', () => { + expect(initial(array)).toEqual([1, 2]); + }); + + it('should return an empty when querying empty arrays', () => { + expect(initial([])).toEqual([]); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]; + const actual = lodashStable.map(array, initial); + + expect(actual).toEqual([ + [1, 2], + [4, 5], + [7, 8], + ]); + }); +}); diff --git a/test/intersection-methods.js b/test/intersection-methods.js deleted file mode 100644 index d90104da73..0000000000 --- a/test/intersection-methods.js +++ /dev/null @@ -1,91 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, args, LARGE_ARRAY_SIZE, stubNaN } from './utils.js'; - -describe('intersection methods', function() { - lodashStable.each(['intersection', 'intersectionBy', 'intersectionWith'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should return the intersection of two arrays', function() { - var actual = func([2, 1], [2, 3]); - assert.deepStrictEqual(actual, [2]); - }); - - it('`_.' + methodName + '` should return the intersection of multiple arrays', function() { - var actual = func([2, 1, 2, 3], [3, 4], [3, 2]); - assert.deepStrictEqual(actual, [3]); - }); - - it('`_.' + methodName + '` should return an array of unique values', function() { - var actual = func([1, 1, 3, 2, 2], [5, 2, 2, 1, 4], [2, 1, 1]); - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('`_.' + methodName + '` should work with a single array', function() { - var actual = func([1, 1, 3, 2, 2]); - assert.deepStrictEqual(actual, [1, 3, 2]); - }); - - it('`_.' + methodName + '` should work with `arguments` objects', function() { - var array = [0, 1, null, 3], - expected = [1, 3]; - - assert.deepStrictEqual(func(array, args), expected); - assert.deepStrictEqual(func(args, array), expected); - }); - - it('`_.' + methodName + '` should treat `-0` as `0`', function() { - var values = [-0, 0], - expected = lodashStable.map(values, lodashStable.constant(['0'])); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.map(func(values, [value]), lodashStable.toString); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should match `NaN`', function() { - var actual = func([1, NaN, 3], [NaN, 5, NaN]); - assert.deepStrictEqual(actual, [NaN]); - }); - - it('`_.' + methodName + '` should work with large arrays of `-0` as `0`', function() { - var values = [-0, 0], - expected = lodashStable.map(values, lodashStable.constant(['0'])); - - var actual = lodashStable.map(values, function(value) { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(value)); - return lodashStable.map(func(values, largeArray), lodashStable.toString); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with large arrays of `NaN`', function() { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubNaN); - assert.deepStrictEqual(func([1, NaN, 3], largeArray), [NaN]); - }); - - it('`_.' + methodName + '` should work with large arrays of objects', function() { - var object = {}, - largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(object)); - - assert.deepStrictEqual(func([object], largeArray), [object]); - assert.deepStrictEqual(func(lodashStable.range(LARGE_ARRAY_SIZE), [1]), [1]); - }); - - it('`_.' + methodName + '` should treat values that are not arrays or `arguments` objects as empty', function() { - var array = [0, 1, null, 3]; - assert.deepStrictEqual(func(array, 3, { '0': 1 }, null), []); - assert.deepStrictEqual(func(null, array, null, [2, 3]), []); - assert.deepStrictEqual(func(array, null, args, null), []); - }); - - it('`_.' + methodName + '` should return a wrapped value when chaining', function() { - var wrapped = _([1, 3, 2])[methodName]([5, 2, 1, 4]); - assert.ok(wrapped instanceof _); - assert.deepEqual(wrapped.value(), [1, 2]); - }); - }); -}); diff --git a/test/intersection-methods.spec.js b/test/intersection-methods.spec.js new file mode 100644 index 0000000000..b929fcf59e --- /dev/null +++ b/test/intersection-methods.spec.js @@ -0,0 +1,93 @@ +import lodashStable from 'lodash'; +import { _, args, LARGE_ARRAY_SIZE, stubNaN } from './utils'; + +describe('intersection methods', () => { + lodashStable.each(['intersection', 'intersectionBy', 'intersectionWith'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should return the intersection of two arrays`, () => { + const actual = func([2, 1], [2, 3]); + expect(actual).toEqual([2]); + }); + + it(`\`_.${methodName}\` should return the intersection of multiple arrays`, () => { + const actual = func([2, 1, 2, 3], [3, 4], [3, 2]); + expect(actual).toEqual([3]); + }); + + it(`\`_.${methodName}\` should return an array of unique values`, () => { + const actual = func([1, 1, 3, 2, 2], [5, 2, 2, 1, 4], [2, 1, 1]); + expect(actual, [1).toEqual(2]); + }); + + it(`\`_.${methodName}\` should work with a single array`, () => { + const actual = func([1, 1, 3, 2, 2]); + expect(actual, [1, 3).toEqual(2]); + }); + + it(`\`_.${methodName}\` should work with \`arguments\` objects`, () => { + const array = [0, 1, null, 3]; + const expected = [1, 3]; + + expect(func(array, args)).toEqual(expected); + expect(func(args, array)).toEqual(expected); + }); + + it(`\`_.${methodName}\` should treat \`-0\` as \`0\``, () => { + const values = [-0, 0]; + const expected = lodashStable.map(values, lodashStable.constant(['0'])); + + const actual = lodashStable.map(values, (value) => + lodashStable.map(func(values, [value]), lodashStable.toString), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should match \`NaN\``, () => { + const actual = func([1, NaN, 3], [NaN, 5, NaN]); + expect(actual).toEqual([NaN]); + }); + + it(`\`_.${methodName}\` should work with large arrays of \`-0\` as \`0\``, () => { + const values = [-0, 0]; + const expected = lodashStable.map(values, lodashStable.constant(['0'])); + + const actual = lodashStable.map(values, (value) => { + const largeArray = lodashStable.times( + LARGE_ARRAY_SIZE, + lodashStable.constant(value), + ); + return lodashStable.map(func(values, largeArray), lodashStable.toString); + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with large arrays of \`NaN\``, () => { + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubNaN); + expect(func([1, NaN, 3], largeArray)).toEqual([NaN]); + }); + + it(`\`_.${methodName}\` should work with large arrays of objects`, () => { + const object = {}; + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, lodashStable.constant(object)); + + expect(func([object], largeArray)).toEqual([object]); + expect(func(lodashStable.range(LARGE_ARRAY_SIZE), [1])).toEqual([1]); + }); + + it(`\`_.${methodName}\` should treat values that are not arrays or \`arguments\` objects as empty`, () => { + const array = [0, 1, null, 3]; + expect(func(array, 3, { 0: 1 }, null)).toEqual([]); + expect(func(null, array, null, [2, 3])).toEqual([]); + expect(func(array, null, args, null)).toEqual([]); + }); + + it(`\`_.${methodName}\` should return a wrapped value when chaining`, () => { + const wrapped = _([1, 3, 2])[methodName]([5, 2, 1, 4]); + expect(wrapped instanceof _) + expect(wrapped.value(), [1).toEqual(2]); + }); + }); +}); diff --git a/test/intersectionBy.js b/test/intersectionBy.js deleted file mode 100644 index c2a988f1c1..0000000000 --- a/test/intersectionBy.js +++ /dev/null @@ -1,23 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import intersectionBy from '../intersectionBy.js'; - -describe('intersectionBy', function() { - it('should accept an `iteratee`', function() { - var actual = intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); - assert.deepStrictEqual(actual, [2.1]); - - actual = intersectionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - assert.deepStrictEqual(actual, [{ 'x': 1 }]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - intersectionBy([2.1, 1.2], [2.3, 3.4], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [2.3]); - }); -}); diff --git a/test/intersectionBy.spec.js b/test/intersectionBy.spec.js new file mode 100644 index 0000000000..b036346188 --- /dev/null +++ b/test/intersectionBy.spec.js @@ -0,0 +1,22 @@ +import { slice } from './utils'; +import intersectionBy from '../src/intersectionBy'; + +describe('intersectionBy', () => { + it('should accept an `iteratee`', () => { + let actual = intersectionBy([2.1, 1.2], [2.3, 3.4], Math.floor); + expect(actual).toEqual([2.1]); + + actual = intersectionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x'); + expect(actual).toEqual([{ x: 1 }]); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + intersectionBy([2.1, 1.2], [2.3, 3.4], function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([2.3]); + }); +}); diff --git a/test/intersectionWith.spec.js b/test/intersectionWith.spec.js new file mode 100644 index 0000000000..e56e7609a6 --- /dev/null +++ b/test/intersectionWith.spec.js @@ -0,0 +1,35 @@ +import lodashStable from 'lodash'; +import { LARGE_ARRAY_SIZE, stubZero } from './utils'; +import intersectionWith from '../src/intersectionWith'; + +describe('intersectionWith', () => { + it('should work with a `comparator`', () => { + const objects = [ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + ]; + const others = [ + { x: 1, y: 1 }, + { x: 1, y: 2 }, + ]; + const actual = intersectionWith(objects, others, lodashStable.isEqual); + + expect(actual).toEqual([objects[0]]); + }); + + it('should preserve the sign of `0`', () => { + const array = [-0]; + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubZero); + const others = [[0], largeArray]; + const expected = lodashStable.map(others, lodashStable.constant(['-0'])); + + const actual = lodashStable.map(others, (other) => + lodashStable.map( + intersectionWith(array, other, lodashStable.eq), + lodashStable.toString, + ), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/intersectionWith.test.js b/test/intersectionWith.test.js deleted file mode 100644 index 72b3cbee3d..0000000000 --- a/test/intersectionWith.test.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, stubZero } from './utils.js'; -import intersectionWith from '../intersectionWith.js'; - -describe('intersectionWith', function() { - it('should work with a `comparator`', function() { - var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }], - others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }], - actual = intersectionWith(objects, others, lodashStable.isEqual); - - assert.deepStrictEqual(actual, [objects[0]]); - }); - - it('should preserve the sign of `0`', function() { - var array = [-0], - largeArray = lodashStable.times(LARGE_ARRAY_SIZE, stubZero), - others = [[0], largeArray], - expected = lodashStable.map(others, lodashStable.constant(['-0'])); - - var actual = lodashStable.map(others, function(other) { - return lodashStable.map(intersectionWith(array, other, lodashStable.eq), lodashStable.toString); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/invert.spec.js b/test/invert.spec.js new file mode 100644 index 0000000000..75af20df52 --- /dev/null +++ b/test/invert.spec.js @@ -0,0 +1,21 @@ +import invert from '../src/invert'; + +describe('invert', () => { + it('should invert an object', () => { + const object = { a: 1, b: 2 }; + const actual = invert(object); + + expect(actual).toEqual({ 1: 'a', 2: 'b' }); + expect(invert(actual)).toEqual({ a: '1', b: '2' }); + }); + + it('should work with values that shadow keys on `Object.prototype`', () => { + const object = { a: 'hasOwnProperty', b: 'constructor' }; + expect(invert(object)).toEqual({ hasOwnProperty: 'a', constructor: 'b' }); + }); + + it('should work with an object that has a `length` property', () => { + const object = { 0: 'a', 1: 'b', length: 2 }; + expect(invert(object)).toEqual({ a: '0', b: '1', 2: 'length' }); + }); +}); diff --git a/test/invert.test.js b/test/invert.test.js deleted file mode 100644 index 5fbfd22f24..0000000000 --- a/test/invert.test.js +++ /dev/null @@ -1,30 +0,0 @@ -import assert from 'assert'; -import invert from '../invert.js'; - -describe('invert', function() { - it('should invert an object', function() { - var object = { 'a': 1, 'b': 2 }, - actual = invert(object); - - assert.deepStrictEqual(actual, { '1': 'a', '2': 'b' }); - assert.deepStrictEqual(invert(actual), { 'a': '1', 'b': '2' }); - }); - - it('should work with values that shadow keys on `Object.prototype`', function() { - var object = { 'a': 'hasOwnProperty', 'b': 'constructor' }; - assert.deepStrictEqual(invert(object), { 'hasOwnProperty': 'a', 'constructor': 'b' }); - }); - - it('should work with an object that has a `length` property', function() { - var object = { '0': 'a', '1': 'b', 'length': 2 }; - assert.deepStrictEqual(invert(object), { 'a': '0', 'b': '1', '2': 'length' }); - }); - - it('should return a wrapped value when chaining', function() { - var object = { 'a': 1, 'b': 2 }, - wrapped = _(object).invert(); - - assert.ok(wrapped instanceof _); - assert.deepEqual(wrapped.value(), { '1': 'a', '2': 'b' }); - }); -}); diff --git a/test/invertBy.js b/test/invertBy.js deleted file mode 100644 index 42379222e0..0000000000 --- a/test/invertBy.js +++ /dev/null @@ -1,42 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import invertBy from '../invertBy.js'; - -describe('invertBy', function() { - var object = { 'a': 1, 'b': 2, 'c': 1 }; - - it('should transform keys by `iteratee`', function() { - var expected = { 'group1': ['a', 'c'], 'group2': ['b'] }; - - var actual = invertBy(object, function(value) { - return 'group' + value; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant({ '1': ['a', 'c'], '2': ['b'] })); - - var actual = lodashStable.map(values, function(value, index) { - return index ? invertBy(object, value) : invertBy(object); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should only add multiple values to own, not inherited, properties', function() { - var object = { 'a': 'hasOwnProperty', 'b': 'constructor' }, - expected = { 'hasOwnProperty': ['a'], 'constructor': ['b'] }; - - assert.ok(lodashStable.isEqual(invertBy(object), expected)); - }); - - it('should return a wrapped value when chaining', function() { - var wrapped = _(object).invertBy(); - - assert.ok(wrapped instanceof _); - assert.deepEqual(wrapped.value(), { '1': ['a', 'c'], '2': ['b'] }); - }); -}); diff --git a/test/invertBy.spec.js b/test/invertBy.spec.js new file mode 100644 index 0000000000..c7d752746f --- /dev/null +++ b/test/invertBy.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import invertBy from '../src/invertBy'; + +describe('invertBy', () => { + const object = { a: 1, b: 2, c: 1 }; + + it('should transform keys by `iteratee`', () => { + const expected = { group1: ['a', 'c'], group2: ['b'] }; + + const actual = invertBy(object, (value) => `group${value}`); + + expect(actual).toEqual(expected); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map( + values, + lodashStable.constant({ 1: ['a', 'c'], 2: ['b'] }), + ); + + const actual = lodashStable.map(values, (value, index) => + index ? invertBy(object, value) : invertBy(object), + ); + + expect(actual).toEqual(expected); + }); + + it('should only add multiple values to own, not inherited, properties', () => { + const object = { a: 'hasOwnProperty', b: 'constructor' }; + const expected = { hasOwnProperty: ['a'], constructor: ['b'] }; + + expect(lodashStable.isEqual(invertBy(object), expected)) + }); + + it('should return a wrapped value when chaining', () => { + const wrapped = _(object).invertBy(); + + expect(wrapped instanceof _) + expect(wrapped.value(), { 1: ['a', 'c']).toEqual(2: ['b'] }); + }); +}); diff --git a/test/invoke.js b/test/invoke.js deleted file mode 100644 index 4d046a80e2..0000000000 --- a/test/invoke.js +++ /dev/null @@ -1,71 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop, stubA, stubB, stubOne } from './utils.js'; -import invoke from '../invoke.js'; - -describe('invoke', function() { - it('should invoke a method on `object`', function() { - var object = { 'a': lodashStable.constant('A') }, - actual = invoke(object, 'a'); - - assert.strictEqual(actual, 'A'); - }); - - it('should support invoking with arguments', function() { - var object = { 'a': function(a, b) { return [a, b]; } }, - actual = invoke(object, 'a', 1, 2); - - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('should not error on nullish elements', function() { - var values = [null, undefined], - expected = lodashStable.map(values, noop); - - var actual = lodashStable.map(values, function(value) { - try { - return invoke(value, 'a.b', 1, 2); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should preserve the sign of `0`', function() { - var object = { '-0': stubA, '0': stubB }, - props = [-0, Object(-0), 0, Object(0)]; - - var actual = lodashStable.map(props, function(key) { - return invoke(object, key); - }); - - assert.deepStrictEqual(actual, ['a', 'a', 'b', 'b']); - }); - - it('should support deep paths', function() { - var object = { 'a': { 'b': function(a, b) { return [a, b]; } } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var actual = invoke(object, path, 1, 2); - assert.deepStrictEqual(actual, [1, 2]); - }); - }); - - it('should invoke deep property methods with the correct `this` binding', function() { - var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.deepStrictEqual(invoke(object, path), 1); - }); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - var object = { 'a': stubOne }; - assert.strictEqual(_(object).invoke('a'), 1); - }); - - it('should return a wrapped value when explicitly chaining', function() { - var object = { 'a': stubOne }; - assert.ok(_(object).chain().invoke('a') instanceof _); - }); -}); diff --git a/test/invoke.spec.js b/test/invoke.spec.js new file mode 100644 index 0000000000..87b716c96d --- /dev/null +++ b/test/invoke.spec.js @@ -0,0 +1,87 @@ +import lodashStable from 'lodash'; +import { noop, stubA, stubB, stubOne } from './utils'; +import invoke from '../src/invoke'; + +describe('invoke', () => { + it('should invoke a method on `object`', () => { + const object = { a: lodashStable.constant('A') }; + const actual = invoke(object, 'a'); + + expect(actual).toBe('A'); + }); + + it('should support invoking with arguments', () => { + const object = { + a: function (a, b) { + return [a, b]; + }, + }; + const actual = invoke(object, 'a', [1, 2]); + + expect(actual).toEqual([1, 2]); + }); + + it('should not error on nullish elements', () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, noop); + + const actual = lodashStable.map(values, (value) => { + try { + return invoke(value, 'a.b', [1, 2]); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should preserve the sign of `0`', () => { + const object = { '-0': stubA, 0: stubB }; + const props = [-0, Object(-0), 0, Object(0)]; + + const actual = lodashStable.map(props, (key) => invoke(object, key)); + + expect(actual).toEqual(['a', 'a', 'b', 'b']); + }); + + it('should support deep paths', () => { + const object = { + a: { + b: function (a, b) { + return [a, b]; + }, + }, + }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const actual = invoke(object, path, [1, 2]); + expect(actual).toEqual([1, 2]); + }); + }); + + it('should invoke deep property methods with the correct `this` binding', () => { + const object = { + a: { + b: function () { + return this.c; + }, + c: 1, + }, + }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(invoke(object, path)).toEqual(1); + }); + }); + + // FIXME: Work out a solution for _. + // + // it('should return an unwrapped value when implicitly chaining', () => { + // const object = { a: stubOne }; + // expect(_(object).invoke('a')).toBe(1); + // }); + // + // it('should return a wrapped value when explicitly chaining', () => { + // const object = { a: stubOne }; + // expect(_(object).chain().invoke('a') instanceof _) + // }); +}); diff --git a/test/invokeMap.js b/test/invokeMap.js deleted file mode 100644 index bb3ba944f4..0000000000 --- a/test/invokeMap.js +++ /dev/null @@ -1,105 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, stubOne } from './utils.js'; -import invokeMap from '../invokeMap.js'; - -describe('invokeMap', function() { - it('should invoke a methods on each element of `collection`', function() { - var array = ['a', 'b', 'c'], - actual = invokeMap(array, 'toUpperCase'); - - assert.deepStrictEqual(actual, ['A', 'B', 'C']); - }); - - it('should support invoking with arguments', function() { - var array = [function() { return slice.call(arguments); }], - actual = invokeMap(array, 'call', null, 'a', 'b', 'c'); - - assert.deepStrictEqual(actual, [['a', 'b', 'c']]); - }); - - it('should work with a function for `methodName`', function() { - var array = ['a', 'b', 'c']; - - var actual = invokeMap(array, function(left, right) { - return left + this.toUpperCase() + right; - }, '(', ')'); - - assert.deepStrictEqual(actual, ['(A)', '(B)', '(C)']); - }); - - it('should work with an object for `collection`', function() { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - actual = invokeMap(object, 'toFixed', 1); - - assert.deepStrictEqual(actual, ['1.0', '2.0', '3.0']); - }); - - it('should treat number values for `collection` as empty', function() { - assert.deepStrictEqual(invokeMap(1), []); - }); - - it('should not error on nullish elements', function() { - var array = ['a', null, undefined, 'd']; - - try { - var actual = invokeMap(array, 'toUpperCase'); - } catch (e) {} - - assert.deepStrictEqual(actual, ['A', undefined, undefined, 'D']); - }); - - it('should not error on elements with missing properties', function() { - var objects = lodashStable.map([null, undefined, stubOne], function(value) { - return { 'a': value }; - }); - - var expected = lodashStable.map(objects, function(object) { - return object.a ? object.a() : undefined; - }); - - try { - var actual = invokeMap(objects, 'a'); - } catch (e) {} - - assert.deepStrictEqual(actual, expected); - }); - - it('should invoke deep property methods with the correct `this` binding', function() { - var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.deepStrictEqual(invokeMap([object], path), [1]); - }); - }); - - it('should return a wrapped value when chaining', function() { - var array = ['a', 'b', 'c'], - wrapped = _(array), - actual = wrapped.invokeMap('toUpperCase'); - - assert.ok(actual instanceof _); - assert.deepEqual(actual.valueOf(), ['A', 'B', 'C']); - - actual = wrapped.invokeMap(function(left, right) { - return left + this.toUpperCase() + right; - }, '(', ')'); - - assert.ok(actual instanceof _); - assert.deepEqual(actual.valueOf(), ['(A)', '(B)', '(C)']); - }); - - it('should support shortcut fusion', function() { - var count = 0, - method = function() { count++; return this.index; }; - - var array = lodashStable.times(LARGE_ARRAY_SIZE, function(index) { - return { 'index': index, 'method': method }; - }); - - var actual = _(array).invokeMap('method').take(1).value(); - - assert.strictEqual(count, 1); - assert.deepEqual(actual, [0]); - }); -}); diff --git a/test/invokeMap.spec.js b/test/invokeMap.spec.js new file mode 100644 index 0000000000..34054a0853 --- /dev/null +++ b/test/invokeMap.spec.js @@ -0,0 +1,124 @@ +import lodashStable from 'lodash'; +import { slice, stubOne } from './utils'; +import invokeMap from '../src/invokeMap'; + +describe('invokeMap', () => { + it('should invoke a methods on each element of `collection`', () => { + const array = ['a', 'b', 'c']; + const actual = invokeMap(array, 'toUpperCase'); + + expect(actual, ['A', 'B').toEqual('C']); + }); + + it('should support invoking with arguments', () => { + const array = [ + function () { + return slice.call(arguments); + }, + ]; + const actual = invokeMap(array, 'call', null, 'a', 'b', 'c'); + + expect(actual, [['a', 'b').toEqual('c']]); + }); + + it('should work with a function for `methodName`', () => { + const array = ['a', 'b', 'c']; + + const actual = invokeMap( + array, + function (left, right) { + return left + this.toUpperCase() + right; + }, + '(', + ')', + ); + + expect(actual, ['(A)', '(B)').toEqual('(C)']); + }); + + it('should work with an object for `collection`', () => { + const object = { a: 1, b: 2, c: 3 }; + const actual = invokeMap(object, 'toFixed', 1); + + expect(actual, ['1.0', '2.0').toEqual('3.0']); + }); + + it('should treat number values for `collection` as empty', () => { + expect(invokeMap(1)).toEqual([]); + }); + + it('should not error on nullish elements', () => { + const array = ['a', null, undefined, 'd']; + + try { + var actual = invokeMap(array, 'toUpperCase'); + } catch (e) {} + + expect(actual, ['A', undefined, undefined).toEqual('D']); + }); + + it('should not error on elements with missing properties', () => { + const objects = lodashStable.map([null, undefined, stubOne], (value) => ({ a: value })); + + const expected = lodashStable.map(objects, (object) => (object.a ? object.a() : undefined)); + + try { + var actual = invokeMap(objects, 'a'); + } catch (e) {} + + expect(actual).toEqual(expected); + }); + + it('should invoke deep property methods with the correct `this` binding', () => { + const object = { + a: { + b: function () { + return this.c; + }, + c: 1, + }, + }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(invokeMap([object], path)).toEqual([1]); + }); + }); + + it('should return a wrapped value when chaining', () => { + const array = ['a', 'b', 'c']; + const wrapped = _(array); + let actual = wrapped.invokeMap('toUpperCase'); + + expect(actual instanceof _) + expect(actual.valueOf(), ['A', 'B').toEqual('C']); + + actual = wrapped.invokeMap( + function (left, right) { + return left + this.toUpperCase() + right; + }, + '(', + ')', + ); + + expect(actual instanceof _) + expect(actual.valueOf(), ['(A)', '(B)').toEqual('(C)']); + }); + + it('should support shortcut fusion', () => { + let count = 0; + const method = function () { + count++; + return this.index; + }; + + const array = lodashStable.times(LARGE_ARRAY_SIZE, (index) => ({ + index: index, + method: method, + })); + + const actual = _(array).invokeMap('method').take(1).value(); + + expect(count).toBe(1); + expect(actual).toEqual([0]); + }); +}); diff --git a/test/isArguments.spec.js b/test/isArguments.spec.js new file mode 100644 index 0000000000..077ebef469 --- /dev/null +++ b/test/isArguments.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { args, strictArgs, falsey, stubFalse, slice, noop, symbol, realm } from './utils'; +import isArguments from '../src/isArguments'; + +describe('isArguments', () => { + it('should return `true` for `arguments` objects', () => { + expect(isArguments(args)).toBe(true); + expect(isArguments(strictArgs)).toBe(true); + }); + + it('should return `false` for non `arguments` objects', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isArguments(value) : isArguments(), + ); + + expect(actual).toEqual(expected); + + expect(isArguments([1, 2, 3])).toBe(false); + expect(isArguments(true)).toBe(false); + expect(isArguments(new Date())).toBe(false); + expect(isArguments(new Error())).toBe(false); + expect(isArguments(slice)).toBe(false); + expect(isArguments({ 0: 1, callee: noop, length: 1 })).toBe(false); + expect(isArguments(1)).toBe(false); + expect(isArguments(/x/)).toBe(false); + expect(isArguments('a')).toBe(false); + expect(isArguments(symbol)).toBe(false); + }); + + it('should work with an `arguments` object from another realm', () => { + if (realm.arguments) { + expect(isArguments(realm.arguments)).toBe(true); + } + }); +}); diff --git a/test/isArguments.test.js b/test/isArguments.test.js deleted file mode 100644 index 87c62a9804..0000000000 --- a/test/isArguments.test.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, strictArgs, falsey, stubFalse, slice, noop, symbol, realm } from './utils.js'; -import isArguments from '../isArguments.js'; - -describe('isArguments', function() { - it('should return `true` for `arguments` objects', function() { - assert.strictEqual(isArguments(args), true); - assert.strictEqual(isArguments(strictArgs), true); - }); - - it('should return `false` for non `arguments` objects', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isArguments(value) : isArguments(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isArguments([1, 2, 3]), false); - assert.strictEqual(isArguments(true), false); - assert.strictEqual(isArguments(new Date), false); - assert.strictEqual(isArguments(new Error), false); - assert.strictEqual(isArguments(_), false); - assert.strictEqual(isArguments(slice), false); - assert.strictEqual(isArguments({ '0': 1, 'callee': noop, 'length': 1 }), false); - assert.strictEqual(isArguments(1), false); - assert.strictEqual(isArguments(/x/), false); - assert.strictEqual(isArguments('a'), false); - assert.strictEqual(isArguments(symbol), false); - }); - - it('should work with an `arguments` object from another realm', function() { - if (realm.arguments) { - assert.strictEqual(isArguments(realm.arguments), true); - } - }); -}); diff --git a/test/isArray.js b/test/isArray.js deleted file mode 100644 index 552796755a..0000000000 --- a/test/isArray.js +++ /dev/null @@ -1,38 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubFalse, args, slice, symbol, realm } from './utils.js'; -import isArray from '../isArray.js'; - -describe('isArray', function() { - it('should return `true` for arrays', function() { - assert.strictEqual(isArray([1, 2, 3]), true); - }); - - it('should return `false` for non-arrays', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isArray(value) : isArray(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isArray(args), false); - assert.strictEqual(isArray(true), false); - assert.strictEqual(isArray(new Date), false); - assert.strictEqual(isArray(new Error), false); - assert.strictEqual(isArray(_), false); - assert.strictEqual(isArray(slice), false); - assert.strictEqual(isArray({ '0': 1, 'length': 1 }), false); - assert.strictEqual(isArray(1), false); - assert.strictEqual(isArray(/x/), false); - assert.strictEqual(isArray('a'), false); - assert.strictEqual(isArray(symbol), false); - }); - - it('should work with an array from another realm', function() { - if (realm.array) { - assert.strictEqual(isArray(realm.array), true); - } - }); -}); diff --git a/test/isArray.spec.js b/test/isArray.spec.js new file mode 100644 index 0000000000..4def8d7db5 --- /dev/null +++ b/test/isArray.spec.js @@ -0,0 +1,35 @@ +import lodashStable, { isArray } from 'lodash'; +import { falsey, stubFalse, args, slice, symbol, realm } from './utils'; + +describe('isArray', () => { + it('should return `true` for arrays', () => { + expect(isArray([1, 2, 3])).toBe(true); + }); + + it('should return `false` for non-arrays', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isArray(value) : isArray(), + ); + + expect(actual).toEqual(expected); + + expect(isArray(args)).toBe(false); + expect(isArray(true)).toBe(false); + expect(isArray(new Date())).toBe(false); + expect(isArray(new Error())).toBe(false); + expect(isArray(slice)).toBe(false); + expect(isArray({ 0: 1, length: 1 })).toBe(false); + expect(isArray(1)).toBe(false); + expect(isArray(/x/)).toBe(false); + expect(isArray('a')).toBe(false); + expect(isArray(symbol)).toBe(false); + }); + + it('should work with an array from another realm', () => { + if (realm.array) { + expect(isArray(realm.array)).toBe(true); + } + }); +}); diff --git a/test/isArrayBuffer.spec.js b/test/isArrayBuffer.spec.js new file mode 100644 index 0000000000..629632be80 --- /dev/null +++ b/test/isArrayBuffer.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { arrayBuffer, falsey, stubFalse, args, slice, symbol, realm } from './utils'; +import isArrayBuffer from '../src/isArrayBuffer'; + +describe('isArrayBuffer', () => { + it('should return `true` for array buffers', () => { + if (ArrayBuffer) { + expect(isArrayBuffer(arrayBuffer)).toBe(true); + } + }); + + it('should return `false` for non array buffers', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isArrayBuffer(value) : isArrayBuffer(), + ); + + expect(actual).toEqual(expected); + + expect(isArrayBuffer(args)).toBe(false); + expect(isArrayBuffer([1])).toBe(false); + expect(isArrayBuffer(true)).toBe(false); + expect(isArrayBuffer(new Date())).toBe(false); + expect(isArrayBuffer(new Error())).toBe(false); + expect(isArrayBuffer(slice)).toBe(false); + expect(isArrayBuffer({ a: 1 })).toBe(false); + expect(isArrayBuffer(1)).toBe(false); + expect(isArrayBuffer(/x/)).toBe(false); + expect(isArrayBuffer('a')).toBe(false); + expect(isArrayBuffer(symbol)).toBe(false); + }); + + it('should work with array buffers from another realm', () => { + if (realm.arrayBuffer) { + expect(isArrayBuffer(realm.arrayBuffer)).toBe(true); + } + }); +}); diff --git a/test/isArrayBuffer.test.js b/test/isArrayBuffer.test.js deleted file mode 100644 index 7ac1bf0984..0000000000 --- a/test/isArrayBuffer.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { arrayBuffer, falsey, stubFalse, args, slice, symbol, realm } from './utils.js'; -import isArrayBuffer from '../isArrayBuffer.js'; - -describe('isArrayBuffer', function() { - it('should return `true` for array buffers', function() { - if (ArrayBuffer) { - assert.strictEqual(isArrayBuffer(arrayBuffer), true); - } - }); - - it('should return `false` for non array buffers', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isArrayBuffer(value) : isArrayBuffer(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isArrayBuffer(args), false); - assert.strictEqual(isArrayBuffer([1]), false); - assert.strictEqual(isArrayBuffer(true), false); - assert.strictEqual(isArrayBuffer(new Date), false); - assert.strictEqual(isArrayBuffer(new Error), false); - assert.strictEqual(isArrayBuffer(_), false); - assert.strictEqual(isArrayBuffer(slice), false); - assert.strictEqual(isArrayBuffer({ 'a': 1 }), false); - assert.strictEqual(isArrayBuffer(1), false); - assert.strictEqual(isArrayBuffer(/x/), false); - assert.strictEqual(isArrayBuffer('a'), false); - assert.strictEqual(isArrayBuffer(symbol), false); - }); - - it('should work with array buffers from another realm', function() { - if (realm.arrayBuffer) { - assert.strictEqual(isArrayBuffer(realm.arrayBuffer), true); - } - }); -}); diff --git a/test/isArrayLike.spec.js b/test/isArrayLike.spec.js new file mode 100644 index 0000000000..cdf02b8203 --- /dev/null +++ b/test/isArrayLike.spec.js @@ -0,0 +1,44 @@ +import lodashStable from 'lodash'; +import { args, stubTrue, falsey, asyncFunc, genFunc, slice, symbol, realm } from './utils'; +import isArrayLike from '../src/isArrayLike'; + +describe('isArrayLike', () => { + it('should return `true` for array-like values', () => { + const values = [args, [1, 2, 3], { 0: 'a', length: 1 }, 'a']; + const expected = lodashStable.map(values, stubTrue); + const actual = lodashStable.map(values, isArrayLike); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-arrays', () => { + const expected = lodashStable.map(falsey, (value) => value === ''); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isArrayLike(value) : isArrayLike(), + ); + + expect(actual).toEqual(expected); + + expect(isArrayLike(true)).toBe(false); + expect(isArrayLike(new Date())).toBe(false); + expect(isArrayLike(new Error())).toBe(false); + expect(isArrayLike(asyncFunc)).toBe(false); + expect(isArrayLike(genFunc)).toBe(false); + expect(isArrayLike(slice)).toBe(false); + expect(isArrayLike({ a: 1 })).toBe(false); + expect(isArrayLike(1)).toBe(false); + expect(isArrayLike(/x/)).toBe(false); + expect(isArrayLike(symbol)).toBe(false); + }); + + it('should work with an array from another realm', () => { + if (realm.object) { + const values = [realm.arguments, realm.array, realm.string]; + const expected = lodashStable.map(values, stubTrue); + const actual = lodashStable.map(values, isArrayLike); + + expect(actual).toEqual(expected); + } + }); +}); diff --git a/test/isArrayLike.test.js b/test/isArrayLike.test.js deleted file mode 100644 index 6e332dd2d3..0000000000 --- a/test/isArrayLike.test.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, stubTrue, falsey, asyncFunc, genFunc, slice, symbol, realm } from './utils.js'; -import isArrayLike from '../isArrayLike.js'; - -describe('isArrayLike', function() { - it('should return `true` for array-like values', function() { - var values = [args, [1, 2, 3], { '0': 'a', 'length': 1 }, 'a'], - expected = lodashStable.map(values, stubTrue), - actual = lodashStable.map(values, isArrayLike); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-arrays', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === ''; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isArrayLike(value) : isArrayLike(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isArrayLike(true), false); - assert.strictEqual(isArrayLike(new Date), false); - assert.strictEqual(isArrayLike(new Error), false); - assert.strictEqual(isArrayLike(_), false); - assert.strictEqual(isArrayLike(asyncFunc), false); - assert.strictEqual(isArrayLike(genFunc), false); - assert.strictEqual(isArrayLike(slice), false); - assert.strictEqual(isArrayLike({ 'a': 1 }), false); - assert.strictEqual(isArrayLike(1), false); - assert.strictEqual(isArrayLike(/x/), false); - assert.strictEqual(isArrayLike(symbol), false); - }); - - it('should work with an array from another realm', function() { - if (realm.object) { - var values = [realm.arguments, realm.array, realm.string], - expected = lodashStable.map(values, stubTrue), - actual = lodashStable.map(values, isArrayLike); - - assert.deepStrictEqual(actual, expected); - } - }); -}); diff --git a/test/isBoolean.spec.js b/test/isBoolean.spec.js new file mode 100644 index 0000000000..b5d6f7814f --- /dev/null +++ b/test/isBoolean.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isBoolean from '../src/isBoolean'; + +describe('isBoolean', () => { + it('should return `true` for booleans', () => { + expect(isBoolean(true)).toBe(true); + expect(isBoolean(false)).toBe(true); + expect(isBoolean(Object(true))).toBe(true); + expect(isBoolean(Object(false))).toBe(true); + }); + + it('should return `false` for non-booleans', () => { + const expected = lodashStable.map(falsey, (value) => value === false); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isBoolean(value) : isBoolean(), + ); + + expect(actual).toEqual(expected); + + expect(isBoolean(args)).toBe(false); + expect(isBoolean([1, 2, 3])).toBe(false); + expect(isBoolean(new Date())).toBe(false); + expect(isBoolean(new Error())).toBe(false); + expect(isBoolean(slice)).toBe(false); + expect(isBoolean({ a: 1 })).toBe(false); + expect(isBoolean(1)).toBe(false); + expect(isBoolean(/x/)).toBe(false); + expect(isBoolean('a')).toBe(false); + expect(isBoolean(symbol)).toBe(false); + }); + + it('should work with a boolean from another realm', () => { + if (realm.boolean) { + expect(isBoolean(realm.boolean)).toBe(true); + } + }); +}); diff --git a/test/isBoolean.test.js b/test/isBoolean.test.js deleted file mode 100644 index 6f6c2e3d41..0000000000 --- a/test/isBoolean.test.js +++ /dev/null @@ -1,43 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isBoolean from '../isBoolean.js'; - -describe('isBoolean', function() { - it('should return `true` for booleans', function() { - assert.strictEqual(isBoolean(true), true); - assert.strictEqual(isBoolean(false), true); - assert.strictEqual(isBoolean(Object(true)), true); - assert.strictEqual(isBoolean(Object(false)), true); - }); - - it('should return `false` for non-booleans', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === false; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isBoolean(value) : isBoolean(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isBoolean(args), false); - assert.strictEqual(isBoolean([1, 2, 3]), false); - assert.strictEqual(isBoolean(new Date), false); - assert.strictEqual(isBoolean(new Error), false); - assert.strictEqual(isBoolean(_), false); - assert.strictEqual(isBoolean(slice), false); - assert.strictEqual(isBoolean({ 'a': 1 }), false); - assert.strictEqual(isBoolean(1), false); - assert.strictEqual(isBoolean(/x/), false); - assert.strictEqual(isBoolean('a'), false); - assert.strictEqual(isBoolean(symbol), false); - }); - - it('should work with a boolean from another realm', function() { - if (realm.boolean) { - assert.strictEqual(isBoolean(realm.boolean), true); - } - }); -}); diff --git a/test/isBuffer.spec.js b/test/isBuffer.spec.js new file mode 100644 index 0000000000..c102ab6dca --- /dev/null +++ b/test/isBuffer.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { falsey, stubFalse, args, slice, symbol, isStrict, lodashBizarro } from './utils'; +import isBuffer from '../src/isBuffer'; + +describe('isBuffer', () => { + it('should return `true` for buffers', () => { + if (Buffer) { + expect(isBuffer(Buffer.alloc(2))).toBe(true); + } + }); + + it('should return `false` for non-buffers', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isBuffer(value) : isBuffer(), + ); + + expect(actual).toEqual(expected); + + expect(isBuffer(args)).toBe(false); + expect(isBuffer([1])).toBe(false); + expect(isBuffer(true)).toBe(false); + expect(isBuffer(new Date())).toBe(false); + expect(isBuffer(new Error())).toBe(false); + expect(isBuffer(slice)).toBe(false); + expect(isBuffer({ a: 1 })).toBe(false); + expect(isBuffer(1)).toBe(false); + expect(isBuffer(/x/)).toBe(false); + expect(isBuffer('a')).toBe(false); + expect(isBuffer(symbol)).toBe(false); + }); + + it('should return `false` if `Buffer` is not defined', () => { + if (!isStrict && Buffer && lodashBizarro) { + expect(lodashBizarro.isBuffer(Buffer.alloc(2))).toBe(false); + } + }); +}); diff --git a/test/isBuffer.test.js b/test/isBuffer.test.js deleted file mode 100644 index a81af76f83..0000000000 --- a/test/isBuffer.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubFalse, args, slice, symbol, isStrict, lodashBizarro } from './utils.js'; -import isBuffer from '../isBuffer.js'; - -describe('isBuffer', function() { - it('should return `true` for buffers', function() { - if (Buffer) { - assert.strictEqual(isBuffer(new Buffer(2)), true); - } - }); - - it('should return `false` for non-buffers', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isBuffer(value) : isBuffer(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isBuffer(args), false); - assert.strictEqual(isBuffer([1]), false); - assert.strictEqual(isBuffer(true), false); - assert.strictEqual(isBuffer(new Date), false); - assert.strictEqual(isBuffer(new Error), false); - assert.strictEqual(isBuffer(_), false); - assert.strictEqual(isBuffer(slice), false); - assert.strictEqual(isBuffer({ 'a': 1 }), false); - assert.strictEqual(isBuffer(1), false); - assert.strictEqual(isBuffer(/x/), false); - assert.strictEqual(isBuffer('a'), false); - assert.strictEqual(isBuffer(symbol), false); - }); - - it('should return `false` if `Buffer` is not defined', function() { - if (!isStrict && Buffer && lodashBizarro) { - assert.strictEqual(lodashBizarro.isBuffer(new Buffer(2)), false); - } - }); -}); diff --git a/test/isDate.spec.js b/test/isDate.spec.js new file mode 100644 index 0000000000..b0c7967a0f --- /dev/null +++ b/test/isDate.spec.js @@ -0,0 +1,36 @@ +import lodashStable from 'lodash'; +import { falsey, stubFalse, args, slice, symbol, realm } from './utils'; +import isDate from '../src/isDate'; + +describe('isDate', () => { + it('should return `true` for dates', () => { + expect(isDate(new Date())).toBe(true); + }); + + it('should return `false` for non-dates', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isDate(value) : isDate(), + ); + + expect(actual).toEqual(expected); + + expect(isDate(args)).toBe(false); + expect(isDate([1, 2, 3])).toBe(false); + expect(isDate(true)).toBe(false); + expect(isDate(new Error())).toBe(false); + expect(isDate(slice)).toBe(false); + expect(isDate({ a: 1 })).toBe(false); + expect(isDate(1)).toBe(false); + expect(isDate(/x/)).toBe(false); + expect(isDate('a')).toBe(false); + expect(isDate(symbol)).toBe(false); + }); + + it('should work with a date object from another realm', () => { + if (realm.date) { + expect(isDate(realm.date)).toBe(true); + } + }); +}); diff --git a/test/isDate.test.js b/test/isDate.test.js deleted file mode 100644 index 8da8064c27..0000000000 --- a/test/isDate.test.js +++ /dev/null @@ -1,38 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubFalse, args, slice, symbol, realm } from './utils.js'; -import isDate from '../isDate.js'; - -describe('isDate', function() { - it('should return `true` for dates', function() { - assert.strictEqual(isDate(new Date), true); - }); - - it('should return `false` for non-dates', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isDate(value) : isDate(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isDate(args), false); - assert.strictEqual(isDate([1, 2, 3]), false); - assert.strictEqual(isDate(true), false); - assert.strictEqual(isDate(new Error), false); - assert.strictEqual(isDate(_), false); - assert.strictEqual(isDate(slice), false); - assert.strictEqual(isDate({ 'a': 1 }), false); - assert.strictEqual(isDate(1), false); - assert.strictEqual(isDate(/x/), false); - assert.strictEqual(isDate('a'), false); - assert.strictEqual(isDate(symbol), false); - }); - - it('should work with a date object from another realm', function() { - if (realm.date) { - assert.strictEqual(isDate(realm.date), true); - } - }); -}); diff --git a/test/isElement.spec.js b/test/isElement.spec.js new file mode 100644 index 0000000000..911c649f51 --- /dev/null +++ b/test/isElement.spec.js @@ -0,0 +1,56 @@ +import lodashStable from 'lodash'; +import { document, body, falsey, stubFalse, args, slice, symbol, realm } from './utils'; +import isElement from '../src/isElement'; + +describe('isElement', () => { + it('should return `true` for elements', () => { + if (document) { + expect(isElement(body)).toBe(true); + } + }); + + it('should return `true` for non-plain objects', () => { + function Foo() { + this.nodeType = 1; + } + + expect(isElement(new Foo())).toBe(true); + }); + + it('should return `false` for non DOM elements', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isElement(value) : isElement(), + ); + + expect(actual).toEqual(expected); + + expect(isElement(args)).toBe(false); + expect(isElement([1, 2, 3])).toBe(false); + expect(isElement(true)).toBe(false); + expect(isElement(new Date())).toBe(false); + expect(isElement(new Error())).toBe(false); + expect(isElement(slice)).toBe(false); + expect(isElement({ a: 1 })).toBe(false); + expect(isElement(1)).toBe(false); + expect(isElement(/x/)).toBe(false); + expect(isElement('a')).toBe(false); + expect(isElement(symbol)).toBe(false); + }); + + it('should return `false` for plain objects', () => { + expect(isElement({ nodeType: 1 })).toBe(false); + expect(isElement({ nodeType: Object(1) })).toBe(false); + expect(isElement({ nodeType: true })).toBe(false); + expect(isElement({ nodeType: [1] })).toBe(false); + expect(isElement({ nodeType: '1' })).toBe(false); + expect(isElement({ nodeType: '001' })).toBe(false); + }); + + it('should work with a DOM element from another realm', () => { + if (realm.element) { + expect(isElement(realm.element)).toBe(true); + } + }); +}); diff --git a/test/isElement.test.js b/test/isElement.test.js deleted file mode 100644 index c5e8833d7f..0000000000 --- a/test/isElement.test.js +++ /dev/null @@ -1,58 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { document, body, falsey, stubFalse, args, slice, symbol, realm } from './utils.js'; -import isElement from '../isElement.js'; - -describe('isElement', function() { - it('should return `true` for elements', function() { - if (document) { - assert.strictEqual(isElement(body), true); - } - }); - - it('should return `true` for non-plain objects', function() { - function Foo() { - this.nodeType = 1; - } - - assert.strictEqual(isElement(new Foo), true); - }); - - it('should return `false` for non DOM elements', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isElement(value) : isElement(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isElement(args), false); - assert.strictEqual(isElement([1, 2, 3]), false); - assert.strictEqual(isElement(true), false); - assert.strictEqual(isElement(new Date), false); - assert.strictEqual(isElement(new Error), false); - assert.strictEqual(isElement(_), false); - assert.strictEqual(isElement(slice), false); - assert.strictEqual(isElement({ 'a': 1 }), false); - assert.strictEqual(isElement(1), false); - assert.strictEqual(isElement(/x/), false); - assert.strictEqual(isElement('a'), false); - assert.strictEqual(isElement(symbol), false); - }); - - it('should return `false` for plain objects', function() { - assert.strictEqual(isElement({ 'nodeType': 1 }), false); - assert.strictEqual(isElement({ 'nodeType': Object(1) }), false); - assert.strictEqual(isElement({ 'nodeType': true }), false); - assert.strictEqual(isElement({ 'nodeType': [1] }), false); - assert.strictEqual(isElement({ 'nodeType': '1' }), false); - assert.strictEqual(isElement({ 'nodeType': '001' }), false); - }); - - it('should work with a DOM element from another realm', function() { - if (realm.element) { - assert.strictEqual(isElement(realm.element), true); - } - }); -}); diff --git a/test/isEmpty.js b/test/isEmpty.js deleted file mode 100644 index 05c8770abe..0000000000 --- a/test/isEmpty.js +++ /dev/null @@ -1,119 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - empties, - stubTrue, - slice, - symbol, - args, - push, - arrayProto, - realm, - MAX_SAFE_INTEGER, -} from './utils.js'; - -import isEmpty from '../isEmpty.js'; - -describe('isEmpty', function() { - it('should return `true` for empty values', function() { - var expected = lodashStable.map(empties, stubTrue), - actual = lodashStable.map(empties, isEmpty); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isEmpty(true), true); - assert.strictEqual(isEmpty(slice), true); - assert.strictEqual(isEmpty(1), true); - assert.strictEqual(isEmpty(NaN), true); - assert.strictEqual(isEmpty(/x/), true); - assert.strictEqual(isEmpty(symbol), true); - assert.strictEqual(isEmpty(), true); - - if (Buffer) { - assert.strictEqual(isEmpty(new Buffer(0)), true); - assert.strictEqual(isEmpty(new Buffer(1)), false); - } - }); - - it('should return `false` for non-empty values', function() { - assert.strictEqual(isEmpty([0]), false); - assert.strictEqual(isEmpty({ 'a': 0 }), false); - assert.strictEqual(isEmpty('a'), false); - }); - - it('should work with an object that has a `length` property', function() { - assert.strictEqual(isEmpty({ 'length': 0 }), false); - }); - - it('should work with `arguments` objects', function() { - assert.strictEqual(isEmpty(args), false); - }); - - it('should work with prototype objects', function() { - function Foo() {} - Foo.prototype = { 'constructor': Foo }; - - assert.strictEqual(isEmpty(Foo.prototype), true); - - Foo.prototype.a = 1; - assert.strictEqual(isEmpty(Foo.prototype), false); - }); - - it('should work with jQuery/MooTools DOM query collections', function() { - function Foo(elements) { - push.apply(this, elements); - } - Foo.prototype = { 'length': 0, 'splice': arrayProto.splice }; - - assert.strictEqual(isEmpty(new Foo([])), true); - }); - - it('should work with maps', function() { - if (Map) { - lodashStable.each([new Map, realm.map], function(map) { - assert.strictEqual(isEmpty(map), true); - map.set('a', 1); - assert.strictEqual(isEmpty(map), false); - map.clear(); - }); - } - }); - - it('should work with sets', function() { - if (Set) { - lodashStable.each([new Set, realm.set], function(set) { - assert.strictEqual(isEmpty(set), true); - set.add(1); - assert.strictEqual(isEmpty(set), false); - set.clear(); - }); - } - }); - - it('should not treat objects with negative lengths as array-like', function() { - function Foo() {} - Foo.prototype.length = -1; - - assert.strictEqual(isEmpty(new Foo), true); - }); - - it('should not treat objects with lengths larger than `MAX_SAFE_INTEGER` as array-like', function() { - function Foo() {} - Foo.prototype.length = MAX_SAFE_INTEGER + 1; - - assert.strictEqual(isEmpty(new Foo), true); - }); - - it('should not treat objects with non-number lengths as array-like', function() { - assert.strictEqual(isEmpty({ 'length': '0' }), false); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - assert.strictEqual(_({}).isEmpty(), true); - }); - - it('should return a wrapped value when explicitly chaining', function() { - assert.ok(_({}).chain().isEmpty() instanceof _); - }); -}); diff --git a/test/isEmpty.spec.js b/test/isEmpty.spec.js new file mode 100644 index 0000000000..5a60a448f5 --- /dev/null +++ b/test/isEmpty.spec.js @@ -0,0 +1,118 @@ +import lodashStable from 'lodash'; + +import { + empties, + stubTrue, + slice, + symbol, + args, + push, + arrayProto, + realm, + MAX_SAFE_INTEGER, +} from './utils'; + +import isEmpty from '../src/isEmpty'; + +describe('isEmpty', () => { + it('should return `true` for empty values', () => { + const expected = lodashStable.map(empties, stubTrue); + const actual = lodashStable.map(empties, isEmpty); + + expect(actual).toEqual(expected); + + expect(isEmpty(true)).toBe(true); + expect(isEmpty(slice)).toBe(true); + expect(isEmpty(1)).toBe(true); + expect(isEmpty(NaN)).toBe(true); + expect(isEmpty(/x/)).toBe(true); + expect(isEmpty(symbol)).toBe(true); + expect(isEmpty()).toBe(true); + + if (Buffer) { + expect(isEmpty(Buffer.alloc(0))).toBe(true); + expect(isEmpty(Buffer.alloc(1))).toBe(false); + } + }); + + it('should return `false` for non-empty values', () => { + expect(isEmpty([0])).toBe(false); + expect(isEmpty({ a: 0 })).toBe(false); + expect(isEmpty('a')).toBe(false); + }); + + it('should work with an object that has a `length` property', () => { + expect(isEmpty({ length: 0 })).toBe(false); + }); + + it('should work with `arguments` objects', () => { + expect(isEmpty(args)).toBe(false); + }); + + it('should work with prototype objects', () => { + function Foo() {} + Foo.prototype = { constructor: Foo }; + + expect(isEmpty(Foo.prototype)).toBe(true); + + Foo.prototype.a = 1; + expect(isEmpty(Foo.prototype)).toBe(false); + }); + + it('should work with jQuery/MooTools DOM query collections', () => { + function Foo(elements) { + push.apply(this, elements); + } + Foo.prototype = { length: 0, splice: arrayProto.splice }; + + expect(isEmpty(new Foo([]))).toBe(true); + }); + + it('should work with maps', () => { + if (Map) { + lodashStable.each([new Map(), realm.map], (map) => { + expect(isEmpty(map)).toBe(true); + map.set('a', 1); + expect(isEmpty(map)).toBe(false); + map.clear(); + }); + } + }); + + it('should work with sets', () => { + if (Set) { + lodashStable.each([new Set(), realm.set], (set) => { + expect(isEmpty(set)).toBe(true); + set.add(1); + expect(isEmpty(set)).toBe(false); + set.clear(); + }); + } + }); + + it('should not treat objects with negative lengths as array-like', () => { + function Foo() {} + Foo.prototype.length = -1; + + expect(isEmpty(new Foo())).toBe(true); + }); + + it('should not treat objects with lengths larger than `MAX_SAFE_INTEGER` as array-like', () => { + function Foo() {} + Foo.prototype.length = MAX_SAFE_INTEGER + 1; + + expect(isEmpty(new Foo())).toBe(true); + }); + + it('should not treat objects with non-number lengths as array-like', () => { + expect(isEmpty({ length: '0' })).toBe(false); + }); + + it('should return an unwrapped value when implicitly chaining', () => { + expect(_({}).isEmpty()).toBe(true); + }); + + it('should return a wrapped value when explicitly chaining', () => { + expect(_({}).chain().isEmpty() instanceof _); + }); +}); diff --git a/test/isEqual.js b/test/isEqual.js deleted file mode 100644 index 66056d7e56..0000000000 --- a/test/isEqual.js +++ /dev/null @@ -1,700 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - noop, - create, - args, - realm, - arrayViews, - map, - promise, - set, - defineProperty, - document, - stubFalse, -} from './utils.js'; - -import isEqual from '../isEqual.js'; - -describe('isEqual', function() { - var symbol1 = Symbol ? Symbol('a') : true, - symbol2 = Symbol ? Symbol('b') : false; - - it('should compare primitives', function() { - var pairs = [ - [1, 1, true], [1, Object(1), true], [1, '1', false], [1, 2, false], - [-0, -0, true], [0, 0, true], [0, Object(0), true], [Object(0), Object(0), true], [-0, 0, true], [0, '0', false], [0, null, false], - [NaN, NaN, true], [NaN, Object(NaN), true], [Object(NaN), Object(NaN), true], [NaN, 'a', false], [NaN, Infinity, false], - ['a', 'a', true], ['a', Object('a'), true], [Object('a'), Object('a'), true], ['a', 'b', false], ['a', ['a'], false], - [true, true, true], [true, Object(true), true], [Object(true), Object(true), true], [true, 1, false], [true, 'a', false], - [false, false, true], [false, Object(false), true], [Object(false), Object(false), true], [false, 0, false], [false, '', false], - [symbol1, symbol1, true], [symbol1, Object(symbol1), true], [Object(symbol1), Object(symbol1), true], [symbol1, symbol2, false], - [null, null, true], [null, undefined, false], [null, {}, false], [null, '', false], - [undefined, undefined, true], [undefined, null, false], [undefined, '', false] - ]; - - var expected = lodashStable.map(pairs, function(pair) { - return pair[2]; - }); - - var actual = lodashStable.map(pairs, function(pair) { - return isEqual(pair[0], pair[1]); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should compare arrays', function() { - var array1 = [true, null, 1, 'a', undefined], - array2 = [true, null, 1, 'a', undefined]; - - assert.strictEqual(isEqual(array1, array2), true); - - array1 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { 'e': 1 }]; - array2 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { 'e': 1 }]; - - assert.strictEqual(isEqual(array1, array2), true); - - array1 = [1]; - array1[2] = 3; - - array2 = [1]; - array2[1] = undefined; - array2[2] = 3; - - assert.strictEqual(isEqual(array1, array2), true); - - array1 = [Object(1), false, Object('a'), /x/, new Date(2012, 4, 23), ['a', 'b', [Object('c')]], { 'a': 1 }]; - array2 = [1, Object(false), 'a', /x/, new Date(2012, 4, 23), ['a', Object('b'), ['c']], { 'a': 1 }]; - - assert.strictEqual(isEqual(array1, array2), true); - - array1 = [1, 2, 3]; - array2 = [3, 2, 1]; - - assert.strictEqual(isEqual(array1, array2), false); - - array1 = [1, 2]; - array2 = [1, 2, 3]; - - assert.strictEqual(isEqual(array1, array2), false); - }); - - it('should treat arrays with identical values but different non-index properties as equal', function() { - var array1 = [1, 2, 3], - array2 = [1, 2, 3]; - - array1.every = array1.filter = array1.forEach = - array1.indexOf = array1.lastIndexOf = array1.map = - array1.some = array1.reduce = array1.reduceRight = null; - - array2.concat = array2.join = array2.pop = - array2.reverse = array2.shift = array2.slice = - array2.sort = array2.splice = array2.unshift = null; - - assert.strictEqual(isEqual(array1, array2), true); - - array1 = [1, 2, 3]; - array1.a = 1; - - array2 = [1, 2, 3]; - array2.b = 1; - - assert.strictEqual(isEqual(array1, array2), true); - - array1 = /c/.exec('abcde'); - array2 = ['c']; - - assert.strictEqual(isEqual(array1, array2), true); - }); - - it('should compare sparse arrays', function() { - var array = Array(1); - - assert.strictEqual(isEqual(array, Array(1)), true); - assert.strictEqual(isEqual(array, [undefined]), true); - assert.strictEqual(isEqual(array, Array(2)), false); - }); - - it('should compare plain objects', function() { - var object1 = { 'a': true, 'b': null, 'c': 1, 'd': 'a', 'e': undefined }, - object2 = { 'a': true, 'b': null, 'c': 1, 'd': 'a', 'e': undefined }; - - assert.strictEqual(isEqual(object1, object2), true); - - object1 = { 'a': [1, 2, 3], 'b': new Date(2012, 4, 23), 'c': /x/, 'd': { 'e': 1 } }; - object2 = { 'a': [1, 2, 3], 'b': new Date(2012, 4, 23), 'c': /x/, 'd': { 'e': 1 } }; - - assert.strictEqual(isEqual(object1, object2), true); - - object1 = { 'a': 1, 'b': 2, 'c': 3 }; - object2 = { 'a': 3, 'b': 2, 'c': 1 }; - - assert.strictEqual(isEqual(object1, object2), false); - - object1 = { 'a': 1, 'b': 2, 'c': 3 }; - object2 = { 'd': 1, 'e': 2, 'f': 3 }; - - assert.strictEqual(isEqual(object1, object2), false); - - object1 = { 'a': 1, 'b': 2 }; - object2 = { 'a': 1, 'b': 2, 'c': 3 }; - - assert.strictEqual(isEqual(object1, object2), false); - }); - - it('should compare objects regardless of key order', function() { - var object1 = { 'a': 1, 'b': 2, 'c': 3 }, - object2 = { 'c': 3, 'a': 1, 'b': 2 }; - - assert.strictEqual(isEqual(object1, object2), true); - }); - - it('should compare nested objects', function() { - var object1 = { - 'a': [1, 2, 3], - 'b': true, - 'c': Object(1), - 'd': 'a', - 'e': { - 'f': ['a', Object('b'), 'c'], - 'g': Object(false), - 'h': new Date(2012, 4, 23), - 'i': noop, - 'j': 'a' - } - }; - - var object2 = { - 'a': [1, Object(2), 3], - 'b': Object(true), - 'c': 1, - 'd': Object('a'), - 'e': { - 'f': ['a', 'b', 'c'], - 'g': false, - 'h': new Date(2012, 4, 23), - 'i': noop, - 'j': 'a' - } - }; - - assert.strictEqual(isEqual(object1, object2), true); - }); - - it('should compare object instances', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.a = 1; - - function Bar() { - this.a = 1; - } - Bar.prototype.a = 2; - - assert.strictEqual(isEqual(new Foo, new Foo), true); - assert.strictEqual(isEqual(new Foo, new Bar), false); - assert.strictEqual(isEqual({ 'a': 1 }, new Foo), false); - assert.strictEqual(isEqual({ 'a': 2 }, new Bar), false); - }); - - it('should compare objects with constructor properties', function() { - assert.strictEqual(isEqual({ 'constructor': 1 }, { 'constructor': 1 }), true); - assert.strictEqual(isEqual({ 'constructor': 1 }, { 'constructor': '1' }), false); - assert.strictEqual(isEqual({ 'constructor': [1] }, { 'constructor': [1] }), true); - assert.strictEqual(isEqual({ 'constructor': [1] }, { 'constructor': ['1'] }), false); - assert.strictEqual(isEqual({ 'constructor': Object }, {}), false); - }); - - it('should compare arrays with circular references', function() { - var array1 = [], - array2 = []; - - array1.push(array1); - array2.push(array2); - - assert.strictEqual(isEqual(array1, array2), true); - - array1.push('b'); - array2.push('b'); - - assert.strictEqual(isEqual(array1, array2), true); - - array1.push('c'); - array2.push('d'); - - assert.strictEqual(isEqual(array1, array2), false); - - array1 = ['a', 'b', 'c']; - array1[1] = array1; - array2 = ['a', ['a', 'b', 'c'], 'c']; - - assert.strictEqual(isEqual(array1, array2), false); - }); - - it('should have transitive equivalence for circular references of arrays', function() { - var array1 = [], - array2 = [array1], - array3 = [array2]; - - array1[0] = array1; - - assert.strictEqual(isEqual(array1, array2), true); - assert.strictEqual(isEqual(array2, array3), true); - assert.strictEqual(isEqual(array1, array3), true); - }); - - it('should compare objects with circular references', function() { - var object1 = {}, - object2 = {}; - - object1.a = object1; - object2.a = object2; - - assert.strictEqual(isEqual(object1, object2), true); - - object1.b = 0; - object2.b = Object(0); - - assert.strictEqual(isEqual(object1, object2), true); - - object1.c = Object(1); - object2.c = Object(2); - - assert.strictEqual(isEqual(object1, object2), false); - - object1 = { 'a': 1, 'b': 2, 'c': 3 }; - object1.b = object1; - object2 = { 'a': 1, 'b': { 'a': 1, 'b': 2, 'c': 3 }, 'c': 3 }; - - assert.strictEqual(isEqual(object1, object2), false); - }); - - it('should have transitive equivalence for circular references of objects', function() { - var object1 = {}, - object2 = { 'a': object1 }, - object3 = { 'a': object2 }; - - object1.a = object1; - - assert.strictEqual(isEqual(object1, object2), true); - assert.strictEqual(isEqual(object2, object3), true); - assert.strictEqual(isEqual(object1, object3), true); - }); - - it('should compare objects with multiple circular references', function() { - var array1 = [{}], - array2 = [{}]; - - (array1[0].a = array1).push(array1); - (array2[0].a = array2).push(array2); - - assert.strictEqual(isEqual(array1, array2), true); - - array1[0].b = 0; - array2[0].b = Object(0); - - assert.strictEqual(isEqual(array1, array2), true); - - array1[0].c = Object(1); - array2[0].c = Object(2); - - assert.strictEqual(isEqual(array1, array2), false); - }); - - it('should compare objects with complex circular references', function() { - var object1 = { - 'foo': { 'b': { 'c': { 'd': {} } } }, - 'bar': { 'a': 2 } - }; - - var object2 = { - 'foo': { 'b': { 'c': { 'd': {} } } }, - 'bar': { 'a': 2 } - }; - - object1.foo.b.c.d = object1; - object1.bar.b = object1.foo.b; - - object2.foo.b.c.d = object2; - object2.bar.b = object2.foo.b; - - assert.strictEqual(isEqual(object1, object2), true); - }); - - it('should compare objects with shared property values', function() { - var object1 = { - 'a': [1, 2] - }; - - var object2 = { - 'a': [1, 2], - 'b': [1, 2] - }; - - object1.b = object1.a; - - assert.strictEqual(isEqual(object1, object2), true); - }); - - it('should treat objects created by `Object.create(null)` like plain objects', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.constructor = null; - - var object1 = create(null); - object1.a = 1; - - var object2 = { 'a': 1 }; - - assert.strictEqual(isEqual(object1, object2), true); - assert.strictEqual(isEqual(new Foo, object2), false); - }); - - it('should avoid common type coercions', function() { - assert.strictEqual(isEqual(true, Object(false)), false); - assert.strictEqual(isEqual(Object(false), Object(0)), false); - assert.strictEqual(isEqual(false, Object('')), false); - assert.strictEqual(isEqual(Object(36), Object('36')), false); - assert.strictEqual(isEqual(0, ''), false); - assert.strictEqual(isEqual(1, true), false); - assert.strictEqual(isEqual(1337756400000, new Date(2012, 4, 23)), false); - assert.strictEqual(isEqual('36', 36), false); - assert.strictEqual(isEqual(36, '36'), false); - }); - - it('should compare `arguments` objects', function() { - var args1 = (function() { return arguments; }()), - args2 = (function() { return arguments; }()), - args3 = (function() { return arguments; }(1, 2)); - - assert.strictEqual(isEqual(args1, args2), true); - assert.strictEqual(isEqual(args1, args3), false); - }); - - it('should treat `arguments` objects like `Object` objects', function() { - var object = { '0': 1, '1': 2, '2': 3 }; - - function Foo() {} - Foo.prototype = object; - - assert.strictEqual(isEqual(args, object), true); - assert.strictEqual(isEqual(object, args), true); - assert.strictEqual(isEqual(args, new Foo), false); - assert.strictEqual(isEqual(new Foo, args), false); - }); - - it('should compare array buffers', function() { - if (ArrayBuffer) { - var buffer = new Int8Array([-1]).buffer; - - assert.strictEqual(isEqual(buffer, new Uint8Array([255]).buffer), true); - assert.strictEqual(isEqual(buffer, new ArrayBuffer(1)), false); - } - }); - - it('should compare array views', function() { - lodashStable.times(2, function(index) { - var ns = index ? realm : root; - - var pairs = lodashStable.map(arrayViews, function(type, viewIndex) { - var otherType = arrayViews[(viewIndex + 1) % arrayViews.length], - CtorA = ns[type] || function(n) { this.n = n; }, - CtorB = ns[otherType] || function(n) { this.n = n; }, - bufferA = ns[type] ? new ns.ArrayBuffer(8) : 8, - bufferB = ns[otherType] ? new ns.ArrayBuffer(8) : 8, - bufferC = ns[otherType] ? new ns.ArrayBuffer(16) : 16; - - return [new CtorA(bufferA), new CtorA(bufferA), new CtorB(bufferB), new CtorB(bufferC)]; - }); - - var expected = lodashStable.map(pairs, lodashStable.constant([true, false, false])); - - var actual = lodashStable.map(pairs, function(pair) { - return [isEqual(pair[0], pair[1]), isEqual(pair[0], pair[2]), isEqual(pair[2], pair[3])]; - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should compare buffers', function() { - if (Buffer) { - var buffer = new Buffer([1]); - - assert.strictEqual(isEqual(buffer, new Buffer([1])), true); - assert.strictEqual(isEqual(buffer, new Buffer([2])), false); - assert.strictEqual(isEqual(buffer, new Uint8Array([1])), false); - } - }); - - it('should compare date objects', function() { - var date = new Date(2012, 4, 23); - - assert.strictEqual(isEqual(date, new Date(2012, 4, 23)), true); - assert.strictEqual(isEqual(new Date('a'), new Date('b')), true); - assert.strictEqual(isEqual(date, new Date(2013, 3, 25)), false); - assert.strictEqual(isEqual(date, { 'getTime': lodashStable.constant(+date) }), false); - }); - - it('should compare error objects', function() { - var pairs = lodashStable.map([ - 'Error', - 'EvalError', - 'RangeError', - 'ReferenceError', - 'SyntaxError', - 'TypeError', - 'URIError' - ], function(type, index, errorTypes) { - var otherType = errorTypes[++index % errorTypes.length], - CtorA = root[type], - CtorB = root[otherType]; - - return [new CtorA('a'), new CtorA('a'), new CtorB('a'), new CtorB('b')]; - }); - - var expected = lodashStable.map(pairs, lodashStable.constant([true, false, false])); - - var actual = lodashStable.map(pairs, function(pair) { - return [isEqual(pair[0], pair[1]), isEqual(pair[0], pair[2]), isEqual(pair[2], pair[3])]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should compare functions', function() { - function a() { return 1 + 2; } - function b() { return 1 + 2; } - - assert.strictEqual(isEqual(a, a), true); - assert.strictEqual(isEqual(a, b), false); - }); - - it('should compare maps', function() { - if (Map) { - lodashStable.each([[map, new Map], [map, realm.map]], function(maps) { - var map1 = maps[0], - map2 = maps[1]; - - map1.set('a', 1); - map2.set('b', 2); - assert.strictEqual(isEqual(map1, map2), false); - - map1.set('b', 2); - map2.set('a', 1); - assert.strictEqual(isEqual(map1, map2), true); - - map1.delete('a'); - map1.set('a', 1); - assert.strictEqual(isEqual(map1, map2), true); - - map2.delete('a'); - assert.strictEqual(isEqual(map1, map2), false); - - map1.clear(); - map2.clear(); - }); - } - }); - - it('should compare maps with circular references', function() { - if (Map) { - var map1 = new Map, - map2 = new Map; - - map1.set('a', map1); - map2.set('a', map2); - assert.strictEqual(isEqual(map1, map2), true); - - map1.set('b', 1); - map2.set('b', 2); - assert.strictEqual(isEqual(map1, map2), false); - } - }); - - it('should compare promises by reference', function() { - if (promise) { - lodashStable.each([[promise, Promise.resolve(1)], [promise, realm.promise]], function(promises) { - var promise1 = promises[0], - promise2 = promises[1]; - - assert.strictEqual(isEqual(promise1, promise2), false); - assert.strictEqual(isEqual(promise1, promise1), true); - }); - } - }); - - it('should compare regexes', function() { - assert.strictEqual(isEqual(/x/gim, /x/gim), true); - assert.strictEqual(isEqual(/x/gim, /x/mgi), true); - assert.strictEqual(isEqual(/x/gi, /x/g), false); - assert.strictEqual(isEqual(/x/, /y/), false); - assert.strictEqual(isEqual(/x/g, { 'global': true, 'ignoreCase': false, 'multiline': false, 'source': 'x' }), false); - }); - - it('should compare sets', function() { - if (Set) { - lodashStable.each([[set, new Set], [set, realm.set]], function(sets) { - var set1 = sets[0], - set2 = sets[1]; - - set1.add(1); - set2.add(2); - assert.strictEqual(isEqual(set1, set2), false); - - set1.add(2); - set2.add(1); - assert.strictEqual(isEqual(set1, set2), true); - - set1.delete(1); - set1.add(1); - assert.strictEqual(isEqual(set1, set2), true); - - set2.delete(1); - assert.strictEqual(isEqual(set1, set2), false); - - set1.clear(); - set2.clear(); - }); - } - }); - - it('should compare sets with circular references', function() { - if (Set) { - var set1 = new Set, - set2 = new Set; - - set1.add(set1); - set2.add(set2); - assert.strictEqual(isEqual(set1, set2), true); - - set1.add(1); - set2.add(2); - assert.strictEqual(isEqual(set1, set2), false); - } - }); - - it('should compare symbol properties', function() { - if (Symbol) { - var object1 = { 'a': 1 }, - object2 = { 'a': 1 }; - - object1[symbol1] = { 'a': { 'b': 2 } }; - object2[symbol1] = { 'a': { 'b': 2 } }; - - defineProperty(object2, symbol2, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': 2 - }); - - assert.strictEqual(isEqual(object1, object2), true); - - object2[symbol1] = { 'a': 1 }; - assert.strictEqual(isEqual(object1, object2), false); - - delete object2[symbol1]; - object2[Symbol('a')] = { 'a': { 'b': 2 } }; - assert.strictEqual(isEqual(object1, object2), false); - } - }); - - it('should compare wrapped values', function() { - var stamp = +new Date; - - var values = [ - [[1, 2], [1, 2], [1, 2, 3]], - [true, true, false], - [new Date(stamp), new Date(stamp), new Date(stamp - 100)], - [{ 'a': 1, 'b': 2 }, { 'a': 1, 'b': 2 }, { 'a': 1, 'b': 1 }], - [1, 1, 2], - [NaN, NaN, Infinity], - [/x/, /x/, /x/i], - ['a', 'a', 'A'] - ]; - - lodashStable.each(values, function(vals) { - var wrapped1 = _(vals[0]), - wrapped2 = _(vals[1]), - actual = wrapped1.isEqual(wrapped2); - - assert.strictEqual(actual, true); - assert.strictEqual(isEqual(_(actual), _(true)), true); - - wrapped1 = _(vals[0]); - wrapped2 = _(vals[2]); - - actual = wrapped1.isEqual(wrapped2); - assert.strictEqual(actual, false); - assert.strictEqual(isEqual(_(actual), _(false)), true); - }); - }); - - it('should compare wrapped and non-wrapped values', function() { - var object1 = _({ 'a': 1, 'b': 2 }), - object2 = { 'a': 1, 'b': 2 }; - - assert.strictEqual(object1.isEqual(object2), true); - assert.strictEqual(isEqual(object1, object2), true); - - object1 = _({ 'a': 1, 'b': 2 }); - object2 = { 'a': 1, 'b': 1 }; - - assert.strictEqual(object1.isEqual(object2), false); - assert.strictEqual(isEqual(object1, object2), false); - }); - - it('should work as an iteratee for `_.every`', function() { - var actual = lodashStable.every([1, 1, 1], lodashStable.partial(isEqual, 1)); - assert.ok(actual); - }); - - it('should not error on DOM elements', function() { - if (document) { - var element1 = document.createElement('div'), - element2 = element1.cloneNode(true); - - try { - assert.strictEqual(isEqual(element1, element2), false); - } catch (e) { - assert.ok(false, e.message); - } - } - }); - - it('should return `true` for like-objects from different documents', function() { - if (realm.object) { - assert.strictEqual(isEqual([1], realm.array), true); - assert.strictEqual(isEqual([2], realm.array), false); - assert.strictEqual(isEqual({ 'a': 1 }, realm.object), true); - assert.strictEqual(isEqual({ 'a': 2 }, realm.object), false); - } - }); - - it('should return `false` for objects with custom `toString` methods', function() { - var primitive, - object = { 'toString': function() { return primitive; } }, - values = [true, null, 1, 'a', undefined], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value) { - primitive = value; - return isEqual(object, value); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - assert.strictEqual(_('a').isEqual('a'), true); - }); - - it('should return a wrapped value when explicitly chaining', function() { - assert.ok(_('a').chain().isEqual('a') instanceof _); - }); -}); diff --git a/test/isEqual.spec.js b/test/isEqual.spec.js new file mode 100644 index 0000000000..d312ab9bfe --- /dev/null +++ b/test/isEqual.spec.js @@ -0,0 +1,821 @@ +import lodashStable from 'lodash'; + +import { + noop, + create, + args, + realm, + arrayViews, + map, + promise, + set, + defineProperty, + document, + stubFalse, +} from './utils'; + +import isEqual from '../src/isEqual'; + +describe('isEqual', () => { + const symbol1 = Symbol ? Symbol('a') : true; + const symbol2 = Symbol ? Symbol('b') : false; + + it('should compare primitives', () => { + const pairs = [ + [1, 1, true], + [1, Object(1), true], + [1, '1', false], + [1, 2, false], + [-0, -0, true], + [0, 0, true], + [0, Object(0), true], + [Object(0), Object(0), true], + [-0, 0, true], + [0, '0', false], + [0, null, false], + [NaN, NaN, true], + [NaN, Object(NaN), true], + [Object(NaN), Object(NaN), true], + [NaN, 'a', false], + [NaN, Infinity, false], + ['a', 'a', true], + ['a', Object('a'), true], + [Object('a'), Object('a'), true], + ['a', 'b', false], + ['a', ['a'], false], + [true, true, true], + [true, Object(true), true], + [Object(true), Object(true), true], + [true, 1, false], + [true, 'a', false], + [false, false, true], + [false, Object(false), true], + [Object(false), Object(false), true], + [false, 0, false], + [false, '', false], + [symbol1, symbol1, true], + [symbol1, Object(symbol1), true], + [Object(symbol1), Object(symbol1), true], + [symbol1, symbol2, false], + [null, null, true], + [null, undefined, false], + [null, {}, false], + [null, '', false], + [undefined, undefined, true], + [undefined, null, false], + [undefined, '', false], + ]; + + const expected = lodashStable.map(pairs, (pair) => pair[2]); + + const actual = lodashStable.map(pairs, (pair) => isEqual(pair[0], pair[1])); + + expect(actual).toEqual(expected); + }); + + it('should compare arrays', () => { + let array1 = [true, null, 1, 'a', undefined]; + let array2 = [true, null, 1, 'a', undefined]; + + expect(isEqual(array1, array2)).toBe(true); + + array1 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { e: 1 }]; + array2 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { e: 1 }]; + + expect(isEqual(array1, array2)).toBe(true); + + array1 = [1]; + array1[2] = 3; + + array2 = [1]; + array2[1] = undefined; + array2[2] = 3; + + expect(isEqual(array1, array2)).toBe(true); + + array1 = [ + Object(1), + false, + Object('a'), + /x/, + new Date(2012, 4, 23), + ['a', 'b', [Object('c')]], + { a: 1 }, + ]; + array2 = [ + 1, + Object(false), + 'a', + /x/, + new Date(2012, 4, 23), + ['a', Object('b'), ['c']], + { a: 1 }, + ]; + + expect(isEqual(array1, array2)).toBe(true); + + array1 = [1, 2, 3]; + array2 = [3, 2, 1]; + + expect(isEqual(array1, array2)).toBe(false); + + array1 = [1, 2]; + array2 = [1, 2, 3]; + + expect(isEqual(array1, array2)).toBe(false); + }); + + it('should treat arrays with identical values but different non-index properties as equal', () => { + let array1 = [1, 2, 3]; + let array2 = [1, 2, 3]; + + array1.every = + array1.filter = + array1.forEach = + array1.indexOf = + array1.lastIndexOf = + array1.map = + array1.some = + array1.reduce = + array1.reduceRight = + null; + + array2.concat = + array2.join = + array2.pop = + array2.reverse = + array2.shift = + array2.slice = + array2.sort = + array2.splice = + array2.unshift = + null; + + expect(isEqual(array1, array2)).toBe(true); + + array1 = [1, 2, 3]; + array1.a = 1; + + array2 = [1, 2, 3]; + array2.b = 1; + + expect(isEqual(array1, array2)).toBe(true); + + array1 = /c/.exec('abcde'); + array2 = ['c']; + + expect(isEqual(array1, array2)).toBe(true); + }); + + it('should compare sparse arrays', () => { + const array = Array(1); + + expect(isEqual(array, Array(1))).toBe(true); + expect(isEqual(array, [undefined])).toBe(true); + expect(isEqual(array, Array(2))).toBe(false); + }); + + it('should compare plain objects', () => { + let object1 = { a: true, b: null, c: 1, d: 'a', e: undefined }; + let object2 = { a: true, b: null, c: 1, d: 'a', e: undefined }; + + expect(isEqual(object1, object2)).toBe(true); + + object1 = { a: [1, 2, 3], b: new Date(2012, 4, 23), c: /x/, d: { e: 1 } }; + object2 = { a: [1, 2, 3], b: new Date(2012, 4, 23), c: /x/, d: { e: 1 } }; + + expect(isEqual(object1, object2)).toBe(true); + + object1 = { a: 1, b: 2, c: 3 }; + object2 = { a: 3, b: 2, c: 1 }; + + expect(isEqual(object1, object2)).toBe(false); + + object1 = { a: 1, b: 2, c: 3 }; + object2 = { d: 1, e: 2, f: 3 }; + + expect(isEqual(object1, object2)).toBe(false); + + object1 = { a: 1, b: 2 }; + object2 = { a: 1, b: 2, c: 3 }; + + expect(isEqual(object1, object2)).toBe(false); + }); + + it('should compare objects regardless of key order', () => { + const object1 = { a: 1, b: 2, c: 3 }; + const object2 = { c: 3, a: 1, b: 2 }; + + expect(isEqual(object1, object2)).toBe(true); + }); + + it('should compare nested objects', () => { + const object1 = { + a: [1, 2, 3], + b: true, + c: Object(1), + d: 'a', + e: { + f: ['a', Object('b'), 'c'], + g: Object(false), + h: new Date(2012, 4, 23), + i: noop, + j: 'a', + }, + }; + + const object2 = { + a: [1, Object(2), 3], + b: Object(true), + c: 1, + d: Object('a'), + e: { + f: ['a', 'b', 'c'], + g: false, + h: new Date(2012, 4, 23), + i: noop, + j: 'a', + }, + }; + + expect(isEqual(object1, object2)).toBe(true); + }); + + it('should compare object instances', () => { + function Foo() { + this.a = 1; + } + Foo.prototype.a = 1; + + function Bar() { + this.a = 1; + } + Bar.prototype.a = 2; + + expect(isEqual(new Foo(), new Foo())).toBe(true); + expect(isEqual(new Foo(), new Bar())).toBe(false); + expect(isEqual({ a: 1 }, new Foo())).toBe(false); + expect(isEqual({ a: 2 }, new Bar())).toBe(false); + }); + + it('should compare objects with constructor properties', () => { + expect(isEqual({ constructor: 1 }, { constructor: 1 })).toBe(true); + expect(isEqual({ constructor: 1 }, { constructor: '1' })).toBe(false); + expect(isEqual({ constructor: [1] }, { constructor: [1] })).toBe(true); + expect(isEqual({ constructor: [1] }, { constructor: ['1'] })).toBe(false); + expect(isEqual({ constructor: Object }, {})).toBe(false); + }); + + it('should compare arrays with circular references', () => { + let array1 = []; + let array2 = []; + + array1.push(array1); + array2.push(array2); + + expect(isEqual(array1, array2)).toBe(true); + + array1.push('b'); + array2.push('b'); + + expect(isEqual(array1, array2)).toBe(true); + + array1.push('c'); + array2.push('d'); + + expect(isEqual(array1, array2)).toBe(false); + + array1 = ['a', 'b', 'c']; + array1[1] = array1; + array2 = ['a', ['a', 'b', 'c'], 'c']; + + expect(isEqual(array1, array2)).toBe(false); + }); + + it('should have transitive equivalence for circular references of arrays', () => { + const array1 = []; + const array2 = [array1]; + const array3 = [array2]; + + array1[0] = array1; + + expect(isEqual(array1, array2)).toBe(true); + expect(isEqual(array2, array3)).toBe(true); + expect(isEqual(array1, array3)).toBe(true); + }); + + it('should compare objects with circular references', () => { + let object1 = {}; + let object2 = {}; + + object1.a = object1; + object2.a = object2; + + expect(isEqual(object1, object2)).toBe(true); + + object1.b = 0; + object2.b = Object(0); + + expect(isEqual(object1, object2)).toBe(true); + + object1.c = Object(1); + object2.c = Object(2); + + expect(isEqual(object1, object2)).toBe(false); + + object1 = { a: 1, b: 2, c: 3 }; + object1.b = object1; + object2 = { a: 1, b: { a: 1, b: 2, c: 3 }, c: 3 }; + + expect(isEqual(object1, object2)).toBe(false); + }); + + it('should have transitive equivalence for circular references of objects', () => { + const object1 = {}; + const object2 = { a: object1 }; + const object3 = { a: object2 }; + + object1.a = object1; + + expect(isEqual(object1, object2)).toBe(true); + expect(isEqual(object2, object3)).toBe(true); + expect(isEqual(object1, object3)).toBe(true); + }); + + it('should compare objects with multiple circular references', () => { + const array1 = [{}]; + const array2 = [{}]; + + (array1[0].a = array1).push(array1); + (array2[0].a = array2).push(array2); + + expect(isEqual(array1, array2)).toBe(true); + + array1[0].b = 0; + array2[0].b = Object(0); + + expect(isEqual(array1, array2)).toBe(true); + + array1[0].c = Object(1); + array2[0].c = Object(2); + + expect(isEqual(array1, array2)).toBe(false); + }); + + it('should compare objects with complex circular references', () => { + const object1 = { + foo: { b: { c: { d: {} } } }, + bar: { a: 2 }, + }; + + const object2 = { + foo: { b: { c: { d: {} } } }, + bar: { a: 2 }, + }; + + object1.foo.b.c.d = object1; + object1.bar.b = object1.foo.b; + + object2.foo.b.c.d = object2; + object2.bar.b = object2.foo.b; + + expect(isEqual(object1, object2)).toBe(true); + }); + + it('should compare objects with shared property values', () => { + const object1 = { + a: [1, 2], + }; + + const object2 = { + a: [1, 2], + b: [1, 2], + }; + + object1.b = object1.a; + + expect(isEqual(object1, object2)).toBe(true); + }); + + it('should treat objects created by `Object.create(null)` like plain objects', () => { + function Foo() { + this.a = 1; + } + Foo.prototype.constructor = null; + + const object1 = create(null); + object1.a = 1; + + const object2 = { a: 1 }; + + expect(isEqual(object1, object2)).toBe(true); + expect(isEqual(new Foo(), object2)).toBe(false); + }); + + it('should avoid common type coercions', () => { + expect(isEqual(true, Object(false))).toBe(false); + expect(isEqual(Object(false), Object(0))).toBe(false); + expect(isEqual(false, Object(''))).toBe(false); + expect(isEqual(Object(36), Object('36'))).toBe(false); + expect(isEqual(0, '')).toBe(false); + expect(isEqual(1, true)).toBe(false); + expect(isEqual(1337756400000, new Date(2012, 4, 23))).toBe(false); + expect(isEqual('36', 36)).toBe(false); + expect(isEqual(36, '36')).toBe(false); + }); + + it('should compare `arguments` objects', () => { + const args1 = (function () { + return arguments; + })(); + const args2 = (function () { + return arguments; + })(); + const args3 = (function () { + return arguments; + })(1, 2); + + expect(isEqual(args1, args2)).toBe(true); + expect(isEqual(args1, args3)).toBe(false); + }); + + it('should treat `arguments` objects like `Object` objects', () => { + const object = { 0: 1, 1: 2, 2: 3 }; + + function Foo() {} + Foo.prototype = object; + + expect(isEqual(args, object)).toBe(true); + expect(isEqual(object, args)).toBe(true); + expect(isEqual(args, new Foo())).toBe(false); + expect(isEqual(new Foo(), args)).toBe(false); + }); + + it('should compare array buffers', () => { + if (ArrayBuffer) { + const buffer = new Int8Array([-1]).buffer; + + expect(isEqual(buffer, new Uint8Array([255]).buffer)).toBe(true); + expect(isEqual(buffer, new ArrayBuffer(1))).toBe(false); + } + }); + + it('should compare array views', () => { + lodashStable.times(2, (index) => { + const ns = index ? realm : root; + + const pairs = lodashStable.map(arrayViews, (type, viewIndex) => { + const otherType = arrayViews[(viewIndex + 1) % arrayViews.length]; + const CtorA = + ns[type] || + function (n) { + this.n = n; + }; + const CtorB = + ns[otherType] || + function (n) { + this.n = n; + }; + const bufferA = ns[type] ? new ns.ArrayBuffer(8) : 8; + const bufferB = ns[otherType] ? new ns.ArrayBuffer(8) : 8; + const bufferC = ns[otherType] ? new ns.ArrayBuffer(16) : 16; + + return [ + new CtorA(bufferA), + new CtorA(bufferA), + new CtorB(bufferB), + new CtorB(bufferC), + ]; + }); + + const expected = lodashStable.map(pairs, lodashStable.constant([true, false, false])); + + const actual = lodashStable.map(pairs, (pair) => [ + isEqual(pair[0], pair[1]), + isEqual(pair[0], pair[2]), + isEqual(pair[2], pair[3]), + ]); + + expect(actual).toEqual(expected); + }); + }); + + it('should compare buffers', () => { + if (Buffer) { + const buffer = Buffer.alloc([1]); + + expect(isEqual(buffer, Buffer.alloc([1]))).toBe(true); + expect(isEqual(buffer, Buffer.alloc([2]))).toBe(false); + expect(isEqual(buffer, new Uint8Array([1]))).toBe(false); + } + }); + + it('should compare date objects', () => { + const date = new Date(2012, 4, 23); + + expect(isEqual(date, new Date(2012, 4, 23))).toBe(true); + expect(isEqual(new Date('a'), new Date('b'))).toBe(true); + expect(isEqual(date, new Date(2013, 3, 25))).toBe(false); + expect(isEqual(date, { getTime: lodashStable.constant(+date) })).toBe(false); + }); + + it('should compare error objects', () => { + const pairs = lodashStable.map( + [ + 'Error', + 'EvalError', + 'RangeError', + 'ReferenceError', + 'SyntaxError', + 'TypeError', + 'URIError', + ], + (type, index, errorTypes) => { + const otherType = errorTypes[++index % errorTypes.length]; + const CtorA = root[type]; + const CtorB = root[otherType]; + + return [new CtorA('a'), new CtorA('a'), new CtorB('a'), new CtorB('b')]; + }, + ); + + const expected = lodashStable.map(pairs, lodashStable.constant([true, false, false])); + + const actual = lodashStable.map(pairs, (pair) => [ + isEqual(pair[0], pair[1]), + isEqual(pair[0], pair[2]), + isEqual(pair[2], pair[3]), + ]); + + expect(actual).toEqual(expected); + }); + + it('should compare functions', () => { + function a() { + return 1 + 2; + } + function b() { + return 1 + 2; + } + + expect(isEqual(a, a)).toBe(true); + expect(isEqual(a, b)).toBe(false); + }); + + it('should compare maps', () => { + if (Map) { + lodashStable.each( + [ + [map, new Map()], + [map, realm.map], + ], + (maps) => { + const map1 = maps[0]; + const map2 = maps[1]; + + map1.set('a', 1); + map2.set('b', 2); + expect(isEqual(map1, map2)).toBe(false); + + map1.set('b', 2); + map2.set('a', 1); + expect(isEqual(map1, map2)).toBe(true); + + map1.delete('a'); + map1.set('a', 1); + expect(isEqual(map1, map2)).toBe(true); + + map2.delete('a'); + expect(isEqual(map1, map2)).toBe(false); + + map1.clear(); + map2.clear(); + }, + ); + } + }); + + it('should compare maps with circular references', () => { + if (Map) { + const map1 = new Map(); + const map2 = new Map(); + + map1.set('a', map1); + map2.set('a', map2); + expect(isEqual(map1, map2)).toBe(true); + + map1.set('b', 1); + map2.set('b', 2); + expect(isEqual(map1, map2)).toBe(false); + } + }); + + it('should compare promises by reference', () => { + if (promise) { + lodashStable.each( + [ + [promise, Promise.resolve(1)], + [promise, realm.promise], + ], + (promises) => { + const promise1 = promises[0]; + const promise2 = promises[1]; + + expect(isEqual(promise1, promise2)).toBe(false); + expect(isEqual(promise1, promise1)).toBe(true); + }, + ); + } + }); + + it('should compare regexes', () => { + expect(isEqual(/x/gim, /x/gim)).toBe(true); + expect(isEqual(/x/gim, /x/gim)).toBe(true); + expect(isEqual(/x/gi, /x/g)).toBe(false); + expect(isEqual(/x/, /y/)).toBe(false); + assert.strictEqual( + isEqual(/x/g, { global: true, ignoreCase: false, multiline: false, source: 'x' }), + false, + ); + }); + + it('should compare sets', () => { + if (Set) { + lodashStable.each( + [ + [set, new Set()], + [set, realm.set], + ], + (sets) => { + const set1 = sets[0]; + const set2 = sets[1]; + + set1.add(1); + set2.add(2); + expect(isEqual(set1, set2)).toBe(false); + + set1.add(2); + set2.add(1); + expect(isEqual(set1, set2)).toBe(true); + + set1.delete(1); + set1.add(1); + expect(isEqual(set1, set2)).toBe(true); + + set2.delete(1); + expect(isEqual(set1, set2)).toBe(false); + + set1.clear(); + set2.clear(); + }, + ); + } + }); + + it('should compare sets with circular references', () => { + if (Set) { + const set1 = new Set(); + const set2 = new Set(); + + set1.add(set1); + set2.add(set2); + expect(isEqual(set1, set2)).toBe(true); + + set1.add(1); + set2.add(2); + expect(isEqual(set1, set2)).toBe(false); + } + }); + + it('should compare symbol properties', () => { + if (Symbol) { + const object1 = { a: 1 }; + const object2 = { a: 1 }; + + object1[symbol1] = { a: { b: 2 } }; + object2[symbol1] = { a: { b: 2 } }; + + defineProperty(object2, symbol2, { + configurable: true, + enumerable: false, + writable: true, + value: 2, + }); + + expect(isEqual(object1, object2)).toBe(true); + + object2[symbol1] = { a: 1 }; + expect(isEqual(object1, object2)).toBe(false); + + delete object2[symbol1]; + object2[Symbol('a')] = { a: { b: 2 } }; + expect(isEqual(object1, object2)).toBe(false); + } + }); + + it('should compare wrapped values', () => { + const stamp = +new Date(); + + const values = [ + [ + [1, 2], + [1, 2], + [1, 2, 3], + ], + [true, true, false], + [new Date(stamp), new Date(stamp), new Date(stamp - 100)], + [ + { a: 1, b: 2 }, + { a: 1, b: 2 }, + { a: 1, b: 1 }, + ], + [1, 1, 2], + [NaN, NaN, Infinity], + [/x/, /x/, /x/i], + ['a', 'a', 'A'], + ]; + + lodashStable.each(values, (vals) => { + let wrapped1 = _(vals[0]); + let wrapped2 = _(vals[1]); + let actual = wrapped1.isEqual(wrapped2); + + expect(actual).toBe(true); + expect(isEqual(_(actual), _(true))).toBe(true); + + wrapped1 = _(vals[0]); + wrapped2 = _(vals[2]); + + actual = wrapped1.isEqual(wrapped2); + expect(actual).toBe(false); + expect(isEqual(_(actual), _(false))).toBe(true); + }); + }); + + it('should compare wrapped and non-wrapped values', () => { + let object1 = _({ a: 1, b: 2 }); + let object2 = { a: 1, b: 2 }; + + expect(object1.isEqual(object2)).toBe(true); + expect(isEqual(object1, object2)).toBe(true); + + object1 = _({ a: 1, b: 2 }); + object2 = { a: 1, b: 1 }; + + expect(object1.isEqual(object2)).toBe(false); + expect(isEqual(object1, object2)).toBe(false); + }); + + it('should work as an iteratee for `_.every`', () => { + const actual = lodashStable.every([1, 1, 1], lodashStable.partial(isEqual, 1)); + expect(actual); + }); + + it('should not error on DOM elements', () => { + if (document) { + const element1 = document.createElement('div'); + const element2 = element1.cloneNode(true); + + try { + expect(isEqual(element1, element2)).toBe(false); + } catch (e) { + expect(false, e.message); + } + } + }); + + it('should return `true` for like-objects from different documents', () => { + if (realm.object) { + expect(isEqual([1], realm.array)).toBe(true); + expect(isEqual([2], realm.array)).toBe(false); + expect(isEqual({ a: 1 }, realm.object)).toBe(true); + expect(isEqual({ a: 2 }, realm.object)).toBe(false); + } + }); + + it('should return `false` for objects with custom `toString` methods', () => { + let primitive; + const object = { + toString: function () { + return primitive; + }, + }; + const values = [true, null, 1, 'a', undefined]; + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value) => { + primitive = value; + return isEqual(object, value); + }); + + expect(actual).toEqual(expected); + }); + + it('should return an unwrapped value when implicitly chaining', () => { + expect(_('a').isEqual('a')).toBe(true); + }); + + it('should return a wrapped value when explicitly chaining', () => { + expect(_('a').chain().isEqual('a') instanceof _); + }); +}); diff --git a/test/isEqualWith.js b/test/isEqualWith.js deleted file mode 100644 index 312e3e4857..0000000000 --- a/test/isEqualWith.js +++ /dev/null @@ -1,128 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, noop, stubC, falsey, stubFalse } from './utils.js'; -import isEqualWith from '../isEqualWith.js'; -import isString from '../isString.js'; -import without from '../without.js'; -import partial from '../partial.js'; - -describe('isEqualWith', function() { - it('should provide correct `customizer` arguments', function() { - var argsList = [], - object1 = { 'a': [1, 2], 'b': null }, - object2 = { 'a': [1, 2], 'b': null }; - - object1.b = object2; - object2.b = object1; - - var expected = [ - [object1, object2], - [object1.a, object2.a, 'a', object1, object2], - [object1.a[0], object2.a[0], 0, object1.a, object2.a], - [object1.a[1], object2.a[1], 1, object1.a, object2.a], - [object1.b, object2.b, 'b', object1.b, object2.b] - ]; - - isEqualWith(object1, object2, function() { - var length = arguments.length, - args = slice.call(arguments, 0, length - (length > 2 ? 1 : 0)); - - argsList.push(args); - }); - - assert.deepStrictEqual(argsList, expected); - }); - - it('should handle comparisons when `customizer` returns `undefined`', function() { - assert.strictEqual(isEqualWith('a', 'a', noop), true); - assert.strictEqual(isEqualWith(['a'], ['a'], noop), true); - assert.strictEqual(isEqualWith({ '0': 'a' }, { '0': 'a' }, noop), true); - }); - - it('should not handle comparisons when `customizer` returns `true`', function() { - var customizer = function(value) { - return isString(value) || undefined; - }; - - assert.strictEqual(isEqualWith('a', 'b', customizer), true); - assert.strictEqual(isEqualWith(['a'], ['b'], customizer), true); - assert.strictEqual(isEqualWith({ '0': 'a' }, { '0': 'b' }, customizer), true); - }); - - it('should not handle comparisons when `customizer` returns `false`', function() { - var customizer = function(value) { - return isString(value) ? false : undefined; - }; - - assert.strictEqual(isEqualWith('a', 'a', customizer), false); - assert.strictEqual(isEqualWith(['a'], ['a'], customizer), false); - assert.strictEqual(isEqualWith({ '0': 'a' }, { '0': 'a' }, customizer), false); - }); - - it('should return a boolean value even when `customizer` does not', function() { - var actual = isEqualWith('a', 'b', stubC); - assert.strictEqual(actual, true); - - var values = without(falsey, undefined), - expected = lodashStable.map(values, stubFalse); - - actual = []; - lodashStable.each(values, function(value) { - actual.push(isEqualWith('a', 'a', lodashStable.constant(value))); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should ensure `customizer` is a function', function() { - var array = [1, 2, 3], - eq = partial(isEqualWith, array), - actual = lodashStable.map([array, [1, 0, 3]], eq); - - assert.deepStrictEqual(actual, [true, false]); - }); - - it('should call `customizer` for values maps and sets', function() { - var value = { 'a': { 'b': 2 } }; - - if (Map) { - var map1 = new Map; - map1.set('a', value); - - var map2 = new Map; - map2.set('a', value); - } - if (Set) { - var set1 = new Set; - set1.add(value); - - var set2 = new Set; - set2.add(value); - } - lodashStable.each([[map1, map2], [set1, set2]], function(pair, index) { - if (pair[0]) { - var argsList = [], - array = lodashStable.toArray(pair[0]); - - var expected = [ - [pair[0], pair[1]], - [array[0], array[0], 0, array, array], - [array[0][0], array[0][0], 0, array[0], array[0]], - [array[0][1], array[0][1], 1, array[0], array[0]] - ]; - - if (index) { - expected.length = 2; - } - isEqualWith(pair[0], pair[1], function() { - var length = arguments.length, - args = slice.call(arguments, 0, length - (length > 2 ? 1 : 0)); - - argsList.push(args); - }); - - assert.deepStrictEqual(argsList, expected, index ? 'Set' : 'Map'); - } - }); - }); -}); diff --git a/test/isEqualWith.spec.js b/test/isEqualWith.spec.js new file mode 100644 index 0000000000..296c77986c --- /dev/null +++ b/test/isEqualWith.spec.js @@ -0,0 +1,133 @@ +import lodashStable from 'lodash'; +import { slice, noop, stubC, falsey, stubFalse } from './utils'; +import isEqualWith from '../src/isEqualWith'; +import isString from '../src/isString'; +import without from '../src/without'; +import partial from '../src/partial'; + +describe('isEqualWith', () => { + it('should provide correct `customizer` arguments', () => { + const argsList = []; + const object1 = { a: [1, 2], b: null }; + const object2 = { a: [1, 2], b: null }; + + object1.b = object2; + object2.b = object1; + + const expected = [ + [object1, object2], + [object1.a, object2.a, 'a', object1, object2], + [object1.a[0], object2.a[0], 0, object1.a, object2.a], + [object1.a[1], object2.a[1], 1, object1.a, object2.a], + [object1.b, object2.b, 'b', object1.b, object2.b], + ]; + + isEqualWith(object1, object2, function () { + const length = arguments.length; + const args = slice.call(arguments, 0, length - (length > 2 ? 1 : 0)); + + argsList.push(args); + }); + + expect(argsList).toEqual(expected); + }); + + it('should handle comparisons when `customizer` returns `undefined`', () => { + expect(isEqualWith('a', 'a', noop)).toBe(true); + expect(isEqualWith(['a'], ['a'], noop)).toBe(true); + expect(isEqualWith({ 0: 'a' }, { 0: 'a' }, noop)).toBe(true); + }); + + it('should not handle comparisons when `customizer` returns `true`', () => { + const customizer = function (value) { + return isString(value) || undefined; + }; + + expect(isEqualWith('a', 'b', customizer)).toBe(true); + expect(isEqualWith(['a'], ['b'], customizer)).toBe(true); + expect(isEqualWith({ 0: 'a' }, { 0: 'b' }, customizer)).toBe(true); + }); + + it('should not handle comparisons when `customizer` returns `false`', () => { + const customizer = function (value) { + return isString(value) ? false : undefined; + }; + + expect(isEqualWith('a', 'a', customizer)).toBe(false); + expect(isEqualWith(['a'], ['a'], customizer)).toBe(false); + expect(isEqualWith({ 0: 'a' }, { 0: 'a' }, customizer)).toBe(false); + }); + + it('should return a boolean value even when `customizer` does not', () => { + let actual = isEqualWith('a', 'b', stubC); + expect(actual).toBe(true); + + const values = without(falsey, undefined); + const expected = lodashStable.map(values, stubFalse); + + actual = []; + lodashStable.each(values, (value) => { + actual.push(isEqualWith('a', 'a', lodashStable.constant(value))); + }); + + expect(actual).toEqual(expected); + }); + + it('should ensure `customizer` is a function', () => { + const array = [1, 2, 3]; + const eq = partial(isEqualWith, array); + const actual = lodashStable.map([array, [1, 0, 3]], eq); + + expect(actual, [true).toEqual(false]); + }); + + it('should call `customizer` for values maps and sets', () => { + const value = { a: { b: 2 } }; + + if (Map) { + var map1 = new Map(); + map1.set('a', value); + + var map2 = new Map(); + map2.set('a', value); + } + if (Set) { + var set1 = new Set(); + set1.add(value); + + var set2 = new Set(); + set2.add(value); + } + lodashStable.each( + [ + [map1, map2], + [set1, set2], + ], + (pair, index) => { + if (pair[0]) { + const argsList = []; + const array = lodashStable.toArray(pair[0]); + + const expected = [ + [pair[0], pair[1]], + [array[0], array[0], 0, array, array], + [array[0][0], array[0][0], 0, array[0], array[0]], + [array[0][1], array[0][1], 1, array[0], array[0]], + ]; + + if (index) { + expected.length = 2; + } + isEqualWith(pair[0], pair[1], function () { + const length = arguments.length; + const args = slice.call(arguments, 0, length - (length > 2 ? 1 : 0)); + + argsList.push(args); + }); + + expect(argsList, expected).toEqual(index ? 'Set' : 'Map'); + } + }, + ); + }); +}); diff --git a/test/isError.spec.js b/test/isError.spec.js new file mode 100644 index 0000000000..018d2c9abd --- /dev/null +++ b/test/isError.spec.js @@ -0,0 +1,64 @@ +import lodashStable from 'lodash'; + +import { + errors, + stubTrue, + CustomError, + falsey, + stubFalse, + args, + slice, + symbol, + realm, +} from './utils'; + +import isError from '../src/isError'; + +describe('isError', () => { + it('should return `true` for error objects', () => { + const expected = lodashStable.map(errors, stubTrue); + + const actual = lodashStable.map(errors, (error) => isError(error) === true); + + expect(actual).toEqual(expected); + }); + + it('should return `true` for subclassed values', () => { + expect(isError(new CustomError('x'))).toBe(true); + }); + + it('should return `false` for non error objects', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isError(value) : isError(), + ); + + expect(actual).toEqual(expected); + + expect(isError(args)).toBe(false); + expect(isError([1, 2, 3])).toBe(false); + expect(isError(true)).toBe(false); + expect(isError(new Date())).toBe(false); + expect(isError(slice)).toBe(false); + expect(isError({ a: 1 })).toBe(false); + expect(isError(1)).toBe(false); + expect(isError(/x/)).toBe(false); + expect(isError('a')).toBe(false); + expect(isError(symbol)).toBe(false); + }); + + it('should return `false` for plain objects', () => { + expect(isError({ name: 'Error', message: '' })).toBe(false); + }); + + it('should work with an error object from another realm', () => { + if (realm.errors) { + const expected = lodashStable.map(realm.errors, stubTrue); + + const actual = lodashStable.map(realm.errors, (error) => isError(error) === true); + + expect(actual).toEqual(expected); + } + }); +}); diff --git a/test/isError.test.js b/test/isError.test.js deleted file mode 100644 index 0805b588e7..0000000000 --- a/test/isError.test.js +++ /dev/null @@ -1,70 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - errors, - stubTrue, - CustomError, - falsey, - stubFalse, - args, - slice, - symbol, - realm, -} from './utils.js'; - -import isError from '../isError.js'; - -describe('isError', function() { - it('should return `true` for error objects', function() { - var expected = lodashStable.map(errors, stubTrue); - - var actual = lodashStable.map(errors, function(error) { - return isError(error) === true; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `true` for subclassed values', function() { - assert.strictEqual(isError(new CustomError('x')), true); - }); - - it('should return `false` for non error objects', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isError(value) : isError(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isError(args), false); - assert.strictEqual(isError([1, 2, 3]), false); - assert.strictEqual(isError(true), false); - assert.strictEqual(isError(new Date), false); - assert.strictEqual(isError(_), false); - assert.strictEqual(isError(slice), false); - assert.strictEqual(isError({ 'a': 1 }), false); - assert.strictEqual(isError(1), false); - assert.strictEqual(isError(/x/), false); - assert.strictEqual(isError('a'), false); - assert.strictEqual(isError(symbol), false); - }); - - it('should return `false` for plain objects', function() { - assert.strictEqual(isError({ 'name': 'Error', 'message': '' }), false); - }); - - it('should work with an error object from another realm', function() { - if (realm.errors) { - var expected = lodashStable.map(realm.errors, stubTrue); - - var actual = lodashStable.map(realm.errors, function(error) { - return isError(error) === true; - }); - - assert.deepStrictEqual(actual, expected); - } - }); -}); diff --git a/test/isFinite.js b/test/isFinite.js deleted file mode 100644 index f5c8ec6881..0000000000 --- a/test/isFinite.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubTrue, stubFalse, args, symbol } from './utils.js'; -import isFinite from '../isFinite.js'; - -describe('isFinite', function() { - it('should return `true` for finite values', function() { - var values = [0, 1, 3.14, -1], - expected = lodashStable.map(values, stubTrue), - actual = lodashStable.map(values, isFinite); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-finite values', function() { - var values = [NaN, Infinity, -Infinity, Object(1)], - expected = lodashStable.map(values, stubFalse), - actual = lodashStable.map(values, isFinite); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-numeric values', function() { - var values = [undefined, [], true, '', ' ', '2px'], - expected = lodashStable.map(values, stubFalse), - actual = lodashStable.map(values, isFinite); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isFinite(args), false); - assert.strictEqual(isFinite([1, 2, 3]), false); - assert.strictEqual(isFinite(true), false); - assert.strictEqual(isFinite(new Date), false); - assert.strictEqual(isFinite(new Error), false); - assert.strictEqual(isFinite({ 'a': 1 }), false); - assert.strictEqual(isFinite(/x/), false); - assert.strictEqual(isFinite('a'), false); - assert.strictEqual(isFinite(symbol), false); - }); - - it('should return `false` for numeric string values', function() { - var values = ['2', '0', '08'], - expected = lodashStable.map(values, stubFalse), - actual = lodashStable.map(values, isFinite); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/isFinite.spec.js b/test/isFinite.spec.js new file mode 100644 index 0000000000..a8eab01695 --- /dev/null +++ b/test/isFinite.spec.js @@ -0,0 +1,47 @@ +import lodashStable from 'lodash'; +import { stubTrue, stubFalse, args, symbol } from './utils'; +import isFinite from '../src/isFinite'; + +describe('isFinite', () => { + it('should return `true` for finite values', () => { + const values = [0, 1, 3.14, -1]; + const expected = lodashStable.map(values, stubTrue); + const actual = lodashStable.map(values, isFinite); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-finite values', () => { + const values = [NaN, Infinity, -Infinity, Object(1)]; + const expected = lodashStable.map(values, stubFalse); + const actual = lodashStable.map(values, isFinite); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-numeric values', () => { + const values = [undefined, [], true, '', ' ', '2px']; + const expected = lodashStable.map(values, stubFalse); + const actual = lodashStable.map(values, isFinite); + + expect(actual).toEqual(expected); + + expect(isFinite(args)).toBe(false); + expect(isFinite([1, 2, 3])).toBe(false); + expect(isFinite(true)).toBe(false); + expect(isFinite(new Date())).toBe(false); + expect(isFinite(new Error())).toBe(false); + expect(isFinite({ a: 1 })).toBe(false); + expect(isFinite(/x/)).toBe(false); + expect(isFinite('a')).toBe(false); + expect(isFinite(symbol)).toBe(false); + }); + + it('should return `false` for numeric string values', () => { + const values = ['2', '0', '08']; + const expected = lodashStable.map(values, stubFalse); + const actual = lodashStable.map(values, isFinite); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/isFunction.js b/test/isFunction.js deleted file mode 100644 index be051d63c4..0000000000 --- a/test/isFunction.js +++ /dev/null @@ -1,83 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - slice, - asyncFunc, - genFunc, - arrayViews, - objToString, - funcTag, - falsey, - stubFalse, - args, - symbol, - document, - realm, -} from './utils.js'; - -import isFunction from '../isFunction.js'; - -describe('isFunction', function() { - it('should return `true` for functions', function() { - assert.strictEqual(isFunction(_), true); - assert.strictEqual(isFunction(slice), true); - }); - - it('should return `true` for async functions', function() { - assert.strictEqual(isFunction(asyncFunc), typeof asyncFunc === 'function'); - }); - - it('should return `true` for generator functions', function() { - assert.strictEqual(isFunction(genFunc), typeof genFunc === 'function'); - }); - - it('should return `true` for the `Proxy` constructor', function() { - if (Proxy) { - assert.strictEqual(isFunction(Proxy), true); - } - }); - - it('should return `true` for array view constructors', function() { - var expected = lodashStable.map(arrayViews, function(type) { - return objToString.call(root[type]) == funcTag; - }); - - var actual = lodashStable.map(arrayViews, function(type) { - return isFunction(root[type]); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-functions', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isFunction(value) : isFunction(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isFunction(args), false); - assert.strictEqual(isFunction([1, 2, 3]), false); - assert.strictEqual(isFunction(true), false); - assert.strictEqual(isFunction(new Date), false); - assert.strictEqual(isFunction(new Error), false); - assert.strictEqual(isFunction({ 'a': 1 }), false); - assert.strictEqual(isFunction(1), false); - assert.strictEqual(isFunction(/x/), false); - assert.strictEqual(isFunction('a'), false); - assert.strictEqual(isFunction(symbol), false); - - if (document) { - assert.strictEqual(isFunction(document.getElementsByTagName('body')), false); - } - }); - - it('should work with a function from another realm', function() { - if (realm.function) { - assert.strictEqual(isFunction(realm.function), true); - } - }); -}); diff --git a/test/isFunction.spec.js b/test/isFunction.spec.js new file mode 100644 index 0000000000..61af3df827 --- /dev/null +++ b/test/isFunction.spec.js @@ -0,0 +1,81 @@ +import lodashStable from 'lodash'; + +import { + slice, + asyncFunc, + genFunc, + arrayViews, + objToString, + funcTag, + falsey, + stubFalse, + args, + symbol, + document, + realm, + root, +} from './utils'; + +import isFunction from '../src/isFunction'; + +describe('isFunction', () => { + it('should return `true` for functions', () => { + expect(isFunction(slice)).toBe(true); + }); + + it('should return `true` for async functions', () => { + expect(isFunction(asyncFunc)).toBe(typeof asyncFunc === 'function'); + }); + + it('should return `true` for generator functions', () => { + expect(isFunction(genFunc)).toBe(typeof genFunc === 'function'); + }); + + it('should return `true` for the `Proxy` constructor', () => { + if (Proxy) { + expect(isFunction(Proxy)).toBe(true); + } + }); + + it('should return `true` for array view constructors', () => { + const expected = lodashStable.map( + arrayViews, + (type) => objToString.call(root[type]) === funcTag, + ); + + const actual = lodashStable.map(arrayViews, (type) => isFunction(root[type])); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-functions', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isFunction(value) : isFunction(), + ); + + expect(actual).toEqual(expected); + + expect(isFunction(args)).toBe(false); + expect(isFunction([1, 2, 3])).toBe(false); + expect(isFunction(true)).toBe(false); + expect(isFunction(new Date())).toBe(false); + expect(isFunction(new Error())).toBe(false); + expect(isFunction({ a: 1 })).toBe(false); + expect(isFunction(1)).toBe(false); + expect(isFunction(/x/)).toBe(false); + expect(isFunction('a')).toBe(false); + expect(isFunction(symbol)).toBe(false); + + if (document) { + expect(isFunction(document.getElementsByTagName('body'))).toBe(false); + } + }); + + it('should work with a function from another realm', () => { + if (realm.function) { + expect(isFunction(realm.function)).toBe(true); + } + }); +}); diff --git a/test/isIndex.spec.js b/test/isIndex.spec.js new file mode 100644 index 0000000000..2c9f009b70 --- /dev/null +++ b/test/isIndex.spec.js @@ -0,0 +1,29 @@ +import lodashStable from 'lodash'; +import { MAX_SAFE_INTEGER, stubTrue, stubFalse } from './utils'; +import _isIndex from '../src/.internal/isIndex'; + +describe('isIndex', () => { + const func = _isIndex; + + it('should return `true` for indexes', () => { + if (func) { + const values = [[0], ['0'], ['1'], [3, 4], [MAX_SAFE_INTEGER - 1]]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (args) => func.apply(undefined, args)); + + expect(actual).toEqual(expected); + } + }); + + it('should return `false` for non-indexes', () => { + if (func) { + const values = [['1abc'], ['07'], ['0001'], [-1], [3, 3], [1.1], [MAX_SAFE_INTEGER]]; + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (args) => func.apply(undefined, args)); + + expect(actual).toEqual(expected); + } + }); +}); diff --git a/test/isIndex.test.js b/test/isIndex.test.js deleted file mode 100644 index 486eef29e1..0000000000 --- a/test/isIndex.test.js +++ /dev/null @@ -1,34 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { MAX_SAFE_INTEGER, stubTrue, stubFalse } from './utils.js'; -import _isIndex from '../.internal/isIndex.js'; - -describe('isIndex', function() { - var func = _isIndex; - - it('should return `true` for indexes', function() { - if (func) { - var values = [[0], ['0'], ['1'], [3, 4], [MAX_SAFE_INTEGER - 1]], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(args) { - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, expected); - } - }); - - it('should return `false` for non-indexes', function() { - if (func) { - var values = [['1abc'], ['07'], ['0001'], [-1], [3, 3], [1.1], [MAX_SAFE_INTEGER]], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(args) { - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, expected); - } - }); -}); diff --git a/test/isInteger-methods.js b/test/isInteger-methods.js deleted file mode 100644 index 9c12c68404..0000000000 --- a/test/isInteger-methods.js +++ /dev/null @@ -1,55 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, stubTrue, MAX_INTEGER, stubFalse, falsey, args, symbol } from './utils.js'; - -describe('isInteger methods', function() { - lodashStable.each(['isInteger', 'isSafeInteger'], function(methodName) { - var func = _[methodName], - isSafe = methodName == 'isSafeInteger'; - - it('`_.' + methodName + '` should return `true` for integer values', function() { - var values = [-1, 0, 1], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return func(value); - }); - - assert.deepStrictEqual(actual, expected); - assert.strictEqual(func(MAX_INTEGER), !isSafe); - }); - - it('should return `false` for non-integer number values', function() { - var values = [NaN, Infinity, -Infinity, Object(1), 3.14], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value) { - return func(value); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-numeric values', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === 0; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? func(value) : func(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(func(args), false); - assert.strictEqual(func([1, 2, 3]), false); - assert.strictEqual(func(true), false); - assert.strictEqual(func(new Date), false); - assert.strictEqual(func(new Error), false); - assert.strictEqual(func({ 'a': 1 }), false); - assert.strictEqual(func(/x/), false); - assert.strictEqual(func('a'), false); - assert.strictEqual(func(symbol), false); - }); - }); -}); diff --git a/test/isInteger-methods.spec.js b/test/isInteger-methods.spec.js new file mode 100644 index 0000000000..5d9114d275 --- /dev/null +++ b/test/isInteger-methods.spec.js @@ -0,0 +1,48 @@ +import lodashStable from 'lodash'; +import { _, stubTrue, MAX_INTEGER, stubFalse, falsey, args, symbol } from './utils'; + +describe('isInteger methods', () => { + lodashStable.each(['isInteger', 'isSafeInteger'], (methodName) => { + const func = _[methodName]; + const isSafe = methodName === 'isSafeInteger'; + + it(`\`_.${methodName}\` should return \`true\` for integer values`, () => { + const values = [-1, 0, 1]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => func(value)); + + expect(actual).toEqual(expected); + expect(func(MAX_INTEGER)).toBe(!isSafe); + }); + + it('should return `false` for non-integer number values', () => { + const values = [NaN, Infinity, -Infinity, Object(1), 3.14]; + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value) => func(value)); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-numeric values', () => { + const expected = lodashStable.map(falsey, (value) => value === 0); + + const actual = lodashStable.map(falsey, (value, index) => + index ? func(value) : func(), + ); + + expect(actual).toEqual(expected); + + expect(func(args)).toBe(false); + expect(func([1, 2, 3])).toBe(false); + expect(func(true)).toBe(false); + expect(func(new Date())).toBe(false); + expect(func(new Error())).toBe(false); + expect(func({ a: 1 })).toBe(false); + expect(func(/x/)).toBe(false); + expect(func('a')).toBe(false); + expect(func(symbol)).toBe(false); + }); + }); +}); diff --git a/test/isIterateeCall.js b/test/isIterateeCall.js deleted file mode 100644 index 86b4de524e..0000000000 --- a/test/isIterateeCall.js +++ /dev/null @@ -1,47 +0,0 @@ -import assert from 'assert'; -import { MAX_SAFE_INTEGER } from './utils.js'; -import _isIterateeCall from '../.internal/isIterateeCall.js'; - -describe('isIterateeCall', function() { - var array = [1], - func = _isIterateeCall, - object = { 'a': 1 }; - - it('should return `true` for iteratee calls', function() { - function Foo() {} - Foo.prototype.a = 1; - - if (func) { - assert.strictEqual(func(1, 0, array), true); - assert.strictEqual(func(1, 'a', object), true); - assert.strictEqual(func(1, 'a', new Foo), true); - } - }); - - it('should return `false` for non-iteratee calls', function() { - if (func) { - assert.strictEqual(func(2, 0, array), false); - assert.strictEqual(func(1, 1.1, array), false); - assert.strictEqual(func(1, 0, { 'length': MAX_SAFE_INTEGER + 1 }), false); - assert.strictEqual(func(1, 'b', object), false); - } - }); - - it('should work with `NaN` values', function() { - if (func) { - assert.strictEqual(func(NaN, 0, [NaN]), true); - assert.strictEqual(func(NaN, 'a', { 'a': NaN }), true); - } - }); - - it('should not error when `index` is an object without a `toString` method', function() { - if (func) { - try { - var actual = func(1, { 'toString': null }, [1]); - } catch (e) { - var message = e.message; - } - assert.strictEqual(actual, false, message || ''); - } - }); -}); diff --git a/test/isIterateeCall.spec.js b/test/isIterateeCall.spec.js new file mode 100644 index 0000000000..6437233743 --- /dev/null +++ b/test/isIterateeCall.spec.js @@ -0,0 +1,46 @@ +import { MAX_SAFE_INTEGER } from './utils'; +import _isIterateeCall from '../.internal/isIterateeCall'; + +describe('isIterateeCall', () => { + const array = [1]; + const func = _isIterateeCall; + const object = { a: 1 }; + + it('should return `true` for iteratee calls', () => { + function Foo() {} + Foo.prototype.a = 1; + + if (func) { + expect(func(1, 0, array)).toBe(true); + expect(func(1, 'a', object)).toBe(true); + expect(func(1, 'a', new Foo())).toBe(true); + } + }); + + it('should return `false` for non-iteratee calls', () => { + if (func) { + expect(func(2, 0, array)).toBe(false); + expect(func(1, 1.1, array)).toBe(false); + expect(func(1, 0, { length: MAX_SAFE_INTEGER + 1 })).toBe(false); + expect(func(1, 'b', object)).toBe(false); + } + }); + + it('should work with `NaN` values', () => { + if (func) { + expect(func(NaN, 0, [NaN])).toBe(true); + expect(func(NaN, 'a', { a: NaN })).toBe(true); + } + }); + + it('should not error when `index` is an object without a `toString` method', () => { + if (func) { + try { + var actual = func(1, { toString: null }, [1]); + } catch (e) { + var message = e.message; + } + expect(actual, false).toBe(message || ''); + } + }); +}); diff --git a/test/isLength.spec.js b/test/isLength.spec.js new file mode 100644 index 0000000000..5d054aac71 --- /dev/null +++ b/test/isLength.spec.js @@ -0,0 +1,21 @@ +import lodashStable from 'lodash'; +import { MAX_SAFE_INTEGER, stubTrue, stubFalse } from './utils'; +import isLength from '../src/isLength'; + +describe('isLength', () => { + it('should return `true` for lengths', () => { + const values = [0, 3, MAX_SAFE_INTEGER]; + const expected = lodashStable.map(values, stubTrue); + const actual = lodashStable.map(values, isLength); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-lengths', () => { + const values = [-1, '1', 1.1, MAX_SAFE_INTEGER + 1]; + const expected = lodashStable.map(values, stubFalse); + const actual = lodashStable.map(values, isLength); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/isLength.test.js b/test/isLength.test.js deleted file mode 100644 index 3978c0cb5c..0000000000 --- a/test/isLength.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { MAX_SAFE_INTEGER, stubTrue, stubFalse } from './utils.js'; -import isLength from '../isLength.js'; - -describe('isLength', function() { - it('should return `true` for lengths', function() { - var values = [0, 3, MAX_SAFE_INTEGER], - expected = lodashStable.map(values, stubTrue), - actual = lodashStable.map(values, isLength); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-lengths', function() { - var values = [-1, '1', 1.1, MAX_SAFE_INTEGER + 1], - expected = lodashStable.map(values, stubFalse), - actual = lodashStable.map(values, isLength); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/isMap.spec.js b/test/isMap.spec.js new file mode 100644 index 0000000000..ef61b0de19 --- /dev/null +++ b/test/isMap.spec.js @@ -0,0 +1,47 @@ +import lodashStable from 'lodash'; +import { map, falsey, stubFalse, args, slice, symbol, weakMap, realm } from './utils'; +import isMap from '../src/isMap'; + +describe('isMap', () => { + it('should return `true` for maps', () => { + if (Map) { + expect(isMap(map)).toBe(true); + } + }); + + it('should return `false` for non-maps', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => (index ? isMap(value) : isMap())); + + expect(actual).toEqual(expected); + + expect(isMap(args)).toBe(false); + expect(isMap([1, 2, 3])).toBe(false); + expect(isMap(true)).toBe(false); + expect(isMap(new Date())).toBe(false); + expect(isMap(new Error())).toBe(false); + expect(isMap(slice)).toBe(false); + expect(isMap({ a: 1 })).toBe(false); + expect(isMap(1)).toBe(false); + expect(isMap(/x/)).toBe(false); + expect(isMap('a')).toBe(false); + expect(isMap(symbol)).toBe(false); + expect(isMap(weakMap)).toBe(false); + }); + + it('should work for objects with a non-function `constructor` (test in IE 11)', () => { + const values = [false, true]; + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value) => isMap({ constructor: value })); + + expect(actual).toEqual(expected); + }); + + it('should work with maps from another realm', () => { + if (realm.map) { + expect(isMap(realm.map)).toBe(true); + } + }); +}); diff --git a/test/isMap.test.js b/test/isMap.test.js deleted file mode 100644 index 32fb80fffc..0000000000 --- a/test/isMap.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { map, falsey, stubFalse, args, slice, symbol, weakMap, realm } from './utils.js'; -import isMap from '../isMap.js'; - -describe('isMap', function() { - it('should return `true` for maps', function() { - if (Map) { - assert.strictEqual(isMap(map), true); - } - }); - - it('should return `false` for non-maps', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isMap(value) : isMap(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isMap(args), false); - assert.strictEqual(isMap([1, 2, 3]), false); - assert.strictEqual(isMap(true), false); - assert.strictEqual(isMap(new Date), false); - assert.strictEqual(isMap(new Error), false); - assert.strictEqual(isMap(_), false); - assert.strictEqual(isMap(slice), false); - assert.strictEqual(isMap({ 'a': 1 }), false); - assert.strictEqual(isMap(1), false); - assert.strictEqual(isMap(/x/), false); - assert.strictEqual(isMap('a'), false); - assert.strictEqual(isMap(symbol), false); - assert.strictEqual(isMap(weakMap), false); - }); - - it('should work for objects with a non-function `constructor` (test in IE 11)', function() { - var values = [false, true], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value) { - return isMap({ 'constructor': value }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with maps from another realm', function() { - if (realm.map) { - assert.strictEqual(isMap(realm.map), true); - } - }); -}); diff --git a/test/isMatchWith.js b/test/isMatchWith.js deleted file mode 100644 index a7941d6589..0000000000 --- a/test/isMatchWith.js +++ /dev/null @@ -1,137 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, noop, stubA, falsey, stubFalse, isNpm, mapCaches } from './utils.js'; -import isMatchWith from '../isMatchWith.js'; -import isString from '../isString.js'; -import last from '../last.js'; -import partial from '../partial.js'; - -describe('isMatchWith', function() { - it('should provide correct `customizer` arguments', function() { - var argsList = [], - object1 = { 'a': [1, 2], 'b': null }, - object2 = { 'a': [1, 2], 'b': null }; - - object1.b = object2; - object2.b = object1; - - var expected = [ - [object1.a, object2.a, 'a', object1, object2], - [object1.a[0], object2.a[0], 0, object1.a, object2.a], - [object1.a[1], object2.a[1], 1, object1.a, object2.a], - [object1.b, object2.b, 'b', object1, object2], - [object1.b.a, object2.b.a, 'a', object1.b, object2.b], - [object1.b.a[0], object2.b.a[0], 0, object1.b.a, object2.b.a], - [object1.b.a[1], object2.b.a[1], 1, object1.b.a, object2.b.a], - [object1.b.b, object2.b.b, 'b', object1.b, object2.b] - ]; - - isMatchWith(object1, object2, function() { - argsList.push(slice.call(arguments, 0, -1)); - }); - - assert.deepStrictEqual(argsList, expected); - }); - - it('should handle comparisons when `customizer` returns `undefined`', function() { - assert.strictEqual(isMatchWith({ 'a': 1 }, { 'a': 1 }, noop), true); - }); - - it('should not handle comparisons when `customizer` returns `true`', function() { - var customizer = function(value) { - return isString(value) || undefined; - }; - - assert.strictEqual(isMatchWith(['a'], ['b'], customizer), true); - assert.strictEqual(isMatchWith({ '0': 'a' }, { '0': 'b' }, customizer), true); - }); - - it('should not handle comparisons when `customizer` returns `false`', function() { - var customizer = function(value) { - return isString(value) ? false : undefined; - }; - - assert.strictEqual(isMatchWith(['a'], ['a'], customizer), false); - assert.strictEqual(isMatchWith({ '0': 'a' }, { '0': 'a' }, customizer), false); - }); - - it('should return a boolean value even when `customizer` does not', function() { - var object = { 'a': 1 }, - actual = isMatchWith(object, { 'a': 1 }, stubA); - - assert.strictEqual(actual, true); - - var expected = lodashStable.map(falsey, stubFalse); - - actual = []; - lodashStable.each(falsey, function(value) { - actual.push(isMatchWith(object, { 'a': 2 }, lodashStable.constant(value))); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should provide `stack` to `customizer`', function() { - var actual; - - isMatchWith({ 'a': 1 }, { 'a': 1 }, function() { - actual = last(arguments); - }); - - assert.ok(isNpm - ? actual.constructor.name == 'Stack' - : actual instanceof mapCaches.Stack - ); - }); - - it('should ensure `customizer` is a function', function() { - var object = { 'a': 1 }, - matches = partial(isMatchWith, object), - actual = lodashStable.map([object, { 'a': 2 }], matches); - - assert.deepStrictEqual(actual, [true, false]); - }); - - it('should call `customizer` for values maps and sets', function() { - var value = { 'a': { 'b': 2 } }; - - if (Map) { - var map1 = new Map; - map1.set('a', value); - - var map2 = new Map; - map2.set('a', value); - } - if (Set) { - var set1 = new Set; - set1.add(value); - - var set2 = new Set; - set2.add(value); - } - lodashStable.each([[map1, map2], [set1, set2]], function(pair, index) { - if (pair[0]) { - var argsList = [], - array = lodashStable.toArray(pair[0]), - object1 = { 'a': pair[0] }, - object2 = { 'a': pair[1] }; - - var expected = [ - [pair[0], pair[1], 'a', object1, object2], - [array[0], array[0], 0, array, array], - [array[0][0], array[0][0], 0, array[0], array[0]], - [array[0][1], array[0][1], 1, array[0], array[0]] - ]; - - if (index) { - expected.length = 2; - } - isMatchWith({ 'a': pair[0] }, { 'a': pair[1] }, function() { - argsList.push(slice.call(arguments, 0, -1)); - }); - - assert.deepStrictEqual(argsList, expected, index ? 'Set' : 'Map'); - } - }); - }); -}); diff --git a/test/isMatchWith.spec.js b/test/isMatchWith.spec.js new file mode 100644 index 0000000000..98b62780e1 --- /dev/null +++ b/test/isMatchWith.spec.js @@ -0,0 +1,139 @@ +import lodashStable from 'lodash'; +import { slice, noop, stubA, falsey, stubFalse, isNpm, mapCaches } from './utils'; +import isMatchWith from '../src/isMatchWith'; +import isString from '../src/isString'; +import last from '../src/last'; +import partial from '../src/partial'; + +describe('isMatchWith', () => { + it('should provide correct `customizer` arguments', () => { + const argsList = []; + const object1 = { a: [1, 2], b: null }; + const object2 = { a: [1, 2], b: null }; + + object1.b = object2; + object2.b = object1; + + const expected = [ + [object1.a, object2.a, 'a', object1, object2], + [object1.a[0], object2.a[0], 0, object1.a, object2.a], + [object1.a[1], object2.a[1], 1, object1.a, object2.a], + [object1.b, object2.b, 'b', object1, object2], + [object1.b.a, object2.b.a, 'a', object1.b, object2.b], + [object1.b.a[0], object2.b.a[0], 0, object1.b.a, object2.b.a], + [object1.b.a[1], object2.b.a[1], 1, object1.b.a, object2.b.a], + [object1.b.b, object2.b.b, 'b', object1.b, object2.b], + ]; + + isMatchWith(object1, object2, function () { + argsList.push(slice.call(arguments, 0, -1)); + }); + + expect(argsList).toEqual(expected); + }); + + it('should handle comparisons when `customizer` returns `undefined`', () => { + expect(isMatchWith({ a: 1 }, { a: 1 }, noop)).toBe(true); + }); + + it('should not handle comparisons when `customizer` returns `true`', () => { + const customizer = function (value) { + return isString(value) || undefined; + }; + + expect(isMatchWith(['a'], ['b'], customizer)).toBe(true); + expect(isMatchWith({ 0: 'a' }, { 0: 'b' }, customizer)).toBe(true); + }); + + it('should not handle comparisons when `customizer` returns `false`', () => { + const customizer = function (value) { + return isString(value) ? false : undefined; + }; + + expect(isMatchWith(['a'], ['a'], customizer)).toBe(false); + expect(isMatchWith({ 0: 'a' }, { 0: 'a' }, customizer)).toBe(false); + }); + + it('should return a boolean value even when `customizer` does not', () => { + const object = { a: 1 }; + let actual = isMatchWith(object, { a: 1 }, stubA); + + expect(actual).toBe(true); + + const expected = lodashStable.map(falsey, stubFalse); + + actual = []; + lodashStable.each(falsey, (value) => { + actual.push(isMatchWith(object, { a: 2 }, lodashStable.constant(value))); + }); + + expect(actual).toEqual(expected); + }); + + it('should provide `stack` to `customizer`', () => { + let actual; + + isMatchWith({ a: 1 }, { a: 1 }, function () { + actual = last(arguments); + }); + + expect(isNpm ? actual.constructor.name === 'Stack' : actual instanceof mapCaches.Stack) + }); + + it('should ensure `customizer` is a function', () => { + const object = { a: 1 }; + const matches = partial(isMatchWith, object); + const actual = lodashStable.map([object, { a: 2 }], matches); + + expect(actual, [true).toEqual(false]); + }); + + it('should call `customizer` for values maps and sets', () => { + const value = { a: { b: 2 } }; + + if (Map) { + var map1 = new Map(); + map1.set('a', value); + + var map2 = new Map(); + map2.set('a', value); + } + if (Set) { + var set1 = new Set(); + set1.add(value); + + var set2 = new Set(); + set2.add(value); + } + lodashStable.each( + [ + [map1, map2], + [set1, set2], + ], + (pair, index) => { + if (pair[0]) { + const argsList = []; + const array = lodashStable.toArray(pair[0]); + const object1 = { a: pair[0] }; + const object2 = { a: pair[1] }; + + const expected = [ + [pair[0], pair[1], 'a', object1, object2], + [array[0], array[0], 0, array, array], + [array[0][0], array[0][0], 0, array[0], array[0]], + [array[0][1], array[0][1], 1, array[0], array[0]], + ]; + + if (index) { + expected.length = 2; + } + isMatchWith({ a: pair[0] }, { a: pair[1] }, function () { + argsList.push(slice.call(arguments, 0, -1)); + }); + + expect(argsList, expected).toEqual(index ? 'Set' : 'Map'); + } + }, + ); + }); +}); diff --git a/test/isNaN.js b/test/isNaN.js deleted file mode 100644 index 27529eac30..0000000000 --- a/test/isNaN.js +++ /dev/null @@ -1,43 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isNaN from '../isNaN.js'; - -describe('isNaN', function() { - it('should return `true` for NaNs', function() { - assert.strictEqual(isNaN(NaN), true); - assert.strictEqual(isNaN(Object(NaN)), true); - }); - - it('should return `false` for non-NaNs', function() { - var expected = lodashStable.map(falsey, function(value) { - return value !== value; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isNaN(value) : isNaN(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isNaN(args), false); - assert.strictEqual(isNaN([1, 2, 3]), false); - assert.strictEqual(isNaN(true), false); - assert.strictEqual(isNaN(new Date), false); - assert.strictEqual(isNaN(new Error), false); - assert.strictEqual(isNaN(_), false); - assert.strictEqual(isNaN(slice), false); - assert.strictEqual(isNaN({ 'a': 1 }), false); - assert.strictEqual(isNaN(1), false); - assert.strictEqual(isNaN(Object(1)), false); - assert.strictEqual(isNaN(/x/), false); - assert.strictEqual(isNaN('a'), false); - assert.strictEqual(isNaN(symbol), false); - }); - - it('should work with `NaN` from another realm', function() { - if (realm.object) { - assert.strictEqual(isNaN(realm.nan), true); - } - }); -}); diff --git a/test/isNaN.spec.js b/test/isNaN.spec.js new file mode 100644 index 0000000000..540a942efb --- /dev/null +++ b/test/isNaN.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isNaN from '../src/isNaN'; + +describe('isNaN', () => { + it('should return `true` for NaNs', () => { + expect(isNaN(NaN)).toBe(true); + expect(isNaN(Object(NaN))).toBe(true); + }); + + it('should return `false` for non-NaNs', () => { + const expected = lodashStable.map(falsey, (value) => value !== value); + + const actual = lodashStable.map(falsey, (value, index) => (index ? isNaN(value) : isNaN())); + + expect(actual).toEqual(expected); + + expect(isNaN(args)).toBe(false); + expect(isNaN([1, 2, 3])).toBe(false); + expect(isNaN(true)).toBe(false); + expect(isNaN(new Date())).toBe(false); + expect(isNaN(new Error())).toBe(false); + expect(isNaN(slice)).toBe(false); + expect(isNaN({ a: 1 })).toBe(false); + expect(isNaN(1)).toBe(false); + expect(isNaN(Object(1))).toBe(false); + expect(isNaN(/x/)).toBe(false); + expect(isNaN('a')).toBe(false); + expect(isNaN(symbol)).toBe(false); + }); + + it('should work with `NaN` from another realm', () => { + if (realm.object) { + expect(isNaN(realm.nan)).toBe(true); + } + }); +}); diff --git a/test/isNative.js b/test/isNative.js deleted file mode 100644 index c5f9994858..0000000000 --- a/test/isNative.js +++ /dev/null @@ -1,92 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - body, - create, - slice, - falsey, - stubFalse, - args, - symbol, - realm, - amd, - filePath, - emptyObject, - interopRequire, -} from './utils.js'; - -import isNative from '../isNative.js'; -import runInContext from '../runInContext.js'; -import _baseEach from '../.internal/baseEach.js'; - -describe('isNative', function() { - it('should return `true` for native methods', function() { - var values = [Array, body && body.cloneNode, create, root.encodeURI, Promise, slice, Uint8Array], - expected = lodashStable.map(values, Boolean), - actual = lodashStable.map(values, isNative); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non-native methods', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isNative(value) : isNative(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isNative(args), false); - assert.strictEqual(isNative([1, 2, 3]), false); - assert.strictEqual(isNative(true), false); - assert.strictEqual(isNative(new Date), false); - assert.strictEqual(isNative(new Error), false); - assert.strictEqual(isNative(_), false); - assert.strictEqual(isNative({ 'a': 1 }), false); - assert.strictEqual(isNative(1), false); - assert.strictEqual(isNative(/x/), false); - assert.strictEqual(isNative('a'), false); - assert.strictEqual(isNative(symbol), false); - }); - - it('should work with native functions from another realm', function() { - if (realm.element) { - assert.strictEqual(isNative(realm.element.cloneNode), true); - } - if (realm.object) { - assert.strictEqual(isNative(realm.object.valueOf), true); - } - }); - - it('should throw an error if core-js is detected', function() { - var lodash = runInContext({ - '__core-js_shared__': {} - }); - - assert.raises(function() { lodash.isNative(noop); }); - }); - - it('should detect methods masquerading as native (test in Node.js)', function() { - if (!amd && _baseEach) { - var path = require('path'), - basePath = path.dirname(filePath), - uid = 'e0gvgyrad1jor', - coreKey = '__core-js_shared__', - fakeSrcKey = 'Symbol(src)_1.' + uid; - - root[coreKey] = { 'keys': { 'IE_PROTO': 'Symbol(IE_PROTO)_3.' + uid } }; - emptyObject(require.cache); - - var baseIsNative = interopRequire(path.join(basePath, '_baseIsNative')); - assert.strictEqual(baseIsNative(slice), true); - - slice[fakeSrcKey] = slice + ''; - assert.strictEqual(baseIsNative(slice), false); - - delete slice[fakeSrcKey]; - delete root[coreKey]; - } - }); -}); diff --git a/test/isNative.spec.js b/test/isNative.spec.js new file mode 100644 index 0000000000..2d4f10d589 --- /dev/null +++ b/test/isNative.spec.js @@ -0,0 +1,99 @@ +import lodashStable from 'lodash'; + +import { + body, + create, + slice, + falsey, + stubFalse, + args, + symbol, + realm, + amd, + filePath, + emptyObject, + interopRequire, +} from './utils'; + +import isNative from '../src/isNative'; +import _baseEach from '../.internal/baseEach'; + +describe('isNative', () => { + it('should return `true` for native methods', () => { + const values = [ + Array, + body && body.cloneNode, + create, + root.encodeURI, + Promise, + slice, + Uint8Array, + ]; + const expected = lodashStable.map(values, Boolean); + const actual = lodashStable.map(values, isNative); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non-native methods', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isNative(value) : isNative(), + ); + + expect(actual).toEqual(expected); + + expect(isNative(args)).toBe(false); + expect(isNative([1, 2, 3])).toBe(false); + expect(isNative(true)).toBe(false); + expect(isNative(new Date())).toBe(false); + expect(isNative(new Error())).toBe(false); + expect(isNative({ a: 1 })).toBe(false); + expect(isNative(1)).toBe(false); + expect(isNative(/x/)).toBe(false); + expect(isNative('a')).toBe(false); + expect(isNative(symbol)).toBe(false); + }); + + it('should work with native functions from another realm', () => { + if (realm.element) { + expect(isNative(realm.element.cloneNode)).toBe(true); + } + if (realm.object) { + expect(isNative(realm.object.valueOf)).toBe(true); + } + }); + + xit('should throw an error if core-js is detected', () => { + const lodash = runInContext({ + '__core-js_shared__': {}, + }); + + expect(() => { + lodash.isNative(noop); + }).toThrow(); + }); + + it('should detect methods masquerading as native (test in Node.js)', () => { + if (!amd && _baseEach) { + const path = require('path'); + const basePath = path.dirname(filePath); + const uid = 'e0gvgyrad1jor'; + const coreKey = '__core-js_shared__'; + const fakeSrcKey = `Symbol(src)_1.${uid}`; + + root[coreKey] = { keys: { IE_PROTO: `Symbol(IE_PROTO)_3.${uid}` } }; + emptyObject(require.cache); + + const baseIsNative = interopRequire(path.join(basePath, '_baseIsNative')); + expect(baseIsNative(slice)).toBe(true); + + slice[fakeSrcKey] = `${slice}`; + expect(baseIsNative(slice)).toBe(false); + + delete slice[fakeSrcKey]; + delete root[coreKey]; + } + }); +}); diff --git a/test/isNil.spec.js b/test/isNil.spec.js new file mode 100644 index 0000000000..33fb979d14 --- /dev/null +++ b/test/isNil.spec.js @@ -0,0 +1,41 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isNil from '../src/isNil'; + +describe('isNil', () => { + it('should return `true` for nullish values', () => { + expect(isNil(null)).toBe(true); + expect(isNil()).toBe(true); + expect(isNil(undefined)).toBe(true); + }); + + it('should return `false` for non-nullish values', () => { + const expected = lodashStable.map(falsey, (value) => value == null); + + const actual = lodashStable.map(falsey, (value, index) => (index ? isNil(value) : isNil())); + + expect(actual).toEqual(expected); + + expect(isNil(args)).toBe(false); + expect(isNil([1, 2, 3])).toBe(false); + expect(isNil(true)).toBe(false); + expect(isNil(new Date())).toBe(false); + expect(isNil(new Error())).toBe(false); + expect(isNil(slice)).toBe(false); + expect(isNil({ a: 1 })).toBe(false); + expect(isNil(1)).toBe(false); + expect(isNil(/x/)).toBe(false); + expect(isNil('a')).toBe(false); + + if (Symbol) { + expect(isNil(symbol)).toBe(false); + } + }); + + it('should work with nils from another realm', () => { + if (realm.object) { + expect(isNil(realm.null)).toBe(true); + expect(isNil(realm.undefined)).toBe(true); + } + }); +}); diff --git a/test/isNil.test.js b/test/isNil.test.js deleted file mode 100644 index 3bd067be27..0000000000 --- a/test/isNil.test.js +++ /dev/null @@ -1,47 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isNil from '../isNil.js'; - -describe('isNil', function() { - it('should return `true` for nullish values', function() { - assert.strictEqual(isNil(null), true); - assert.strictEqual(isNil(), true); - assert.strictEqual(isNil(undefined), true); - }); - - it('should return `false` for non-nullish values', function() { - var expected = lodashStable.map(falsey, function(value) { - return value == null; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isNil(value) : isNil(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isNil(args), false); - assert.strictEqual(isNil([1, 2, 3]), false); - assert.strictEqual(isNil(true), false); - assert.strictEqual(isNil(new Date), false); - assert.strictEqual(isNil(new Error), false); - assert.strictEqual(isNil(_), false); - assert.strictEqual(isNil(slice), false); - assert.strictEqual(isNil({ 'a': 1 }), false); - assert.strictEqual(isNil(1), false); - assert.strictEqual(isNil(/x/), false); - assert.strictEqual(isNil('a'), false); - - if (Symbol) { - assert.strictEqual(isNil(symbol), false); - } - }); - - it('should work with nils from another realm', function() { - if (realm.object) { - assert.strictEqual(isNil(realm.null), true); - assert.strictEqual(isNil(realm.undefined), true); - } - }); -}); diff --git a/test/isNull.spec.js b/test/isNull.spec.js new file mode 100644 index 0000000000..9ba2a166ce --- /dev/null +++ b/test/isNull.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isNull from '../src/isNull'; + +describe('isNull', () => { + it('should return `true` for `null` values', () => { + expect(isNull(null)).toBe(true); + }); + + it('should return `false` for non `null` values', () => { + const expected = lodashStable.map(falsey, (value) => value === null); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isNull(value) : isNull(), + ); + + expect(actual).toEqual(expected); + + expect(isNull(args)).toBe(false); + expect(isNull([1, 2, 3])).toBe(false); + expect(isNull(true)).toBe(false); + expect(isNull(new Date())).toBe(false); + expect(isNull(new Error())).toBe(false); + expect(isNull(slice)).toBe(false); + expect(isNull({ a: 1 })).toBe(false); + expect(isNull(1)).toBe(false); + expect(isNull(/x/)).toBe(false); + expect(isNull('a')).toBe(false); + expect(isNull(symbol)).toBe(false); + }); + + it('should work with nulls from another realm', () => { + if (realm.object) { + expect(isNull(realm.null)).toBe(true); + } + }); +}); diff --git a/test/isNull.test.js b/test/isNull.test.js deleted file mode 100644 index 239e4c826b..0000000000 --- a/test/isNull.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isNull from '../isNull.js'; - -describe('isNull', function() { - it('should return `true` for `null` values', function() { - assert.strictEqual(isNull(null), true); - }); - - it('should return `false` for non `null` values', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === null; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isNull(value) : isNull(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isNull(args), false); - assert.strictEqual(isNull([1, 2, 3]), false); - assert.strictEqual(isNull(true), false); - assert.strictEqual(isNull(new Date), false); - assert.strictEqual(isNull(new Error), false); - assert.strictEqual(isNull(_), false); - assert.strictEqual(isNull(slice), false); - assert.strictEqual(isNull({ 'a': 1 }), false); - assert.strictEqual(isNull(1), false); - assert.strictEqual(isNull(/x/), false); - assert.strictEqual(isNull('a'), false); - assert.strictEqual(isNull(symbol), false); - }); - - it('should work with nulls from another realm', function() { - if (realm.object) { - assert.strictEqual(isNull(realm.null), true); - } - }); -}); diff --git a/test/isNumber.spec.js b/test/isNumber.spec.js new file mode 100644 index 0000000000..c5b2decae4 --- /dev/null +++ b/test/isNumber.spec.js @@ -0,0 +1,38 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isNumber from '../src/isNumber'; + +describe('isNumber', () => { + it('should return `true` for numbers', () => { + expect(isNumber(0)).toBe(true); + expect(isNumber(Object(0))).toBe(true); + expect(isNumber(NaN)).toBe(true); + }); + + it('should return `false` for non-numbers', () => { + const expected = lodashStable.map(falsey, (value) => typeof value === 'number'); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isNumber(value) : isNumber(), + ); + + expect(actual).toEqual(expected); + + expect(isNumber(args)).toBe(false); + expect(isNumber([1, 2, 3])).toBe(false); + expect(isNumber(true)).toBe(false); + expect(isNumber(new Date())).toBe(false); + expect(isNumber(new Error())).toBe(false); + expect(isNumber(slice)).toBe(false); + expect(isNumber({ a: 1 })).toBe(false); + expect(isNumber(/x/)).toBe(false); + expect(isNumber('a')).toBe(false); + expect(isNumber(symbol)).toBe(false); + }); + + it('should work with numbers from another realm', () => { + if (realm.number) { + expect(isNumber(realm.number)).toBe(true); + } + }); +}); diff --git a/test/isNumber.test.js b/test/isNumber.test.js deleted file mode 100644 index fd9ff6bb39..0000000000 --- a/test/isNumber.test.js +++ /dev/null @@ -1,42 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isNumber from '../isNumber.js'; - -describe('isNumber', function() { - it('should return `true` for numbers', function() { - assert.strictEqual(isNumber(0), true); - assert.strictEqual(isNumber(Object(0)), true); - assert.strictEqual(isNumber(NaN), true); - }); - - it('should return `false` for non-numbers', function() { - var expected = lodashStable.map(falsey, function(value) { - return typeof value === 'number'; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isNumber(value) : isNumber(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isNumber(args), false); - assert.strictEqual(isNumber([1, 2, 3]), false); - assert.strictEqual(isNumber(true), false); - assert.strictEqual(isNumber(new Date), false); - assert.strictEqual(isNumber(new Error), false); - assert.strictEqual(isNumber(_), false); - assert.strictEqual(isNumber(slice), false); - assert.strictEqual(isNumber({ 'a': 1 }), false); - assert.strictEqual(isNumber(/x/), false); - assert.strictEqual(isNumber('a'), false); - assert.strictEqual(isNumber(symbol), false); - }); - - it('should work with numbers from another realm', function() { - if (realm.number) { - assert.strictEqual(isNumber(realm.number), true); - } - }); -}); diff --git a/test/isObject.spec.js b/test/isObject.spec.js new file mode 100644 index 0000000000..ec8604271b --- /dev/null +++ b/test/isObject.spec.js @@ -0,0 +1,51 @@ +import lodashStable from 'lodash'; +import { args, slice, document, body, symbol, falsey, stubFalse, realm } from './utils'; +import isObject from '../src/isObject'; + +describe('isObject', () => { + it('should return `true` for objects', () => { + expect(isObject(args)).toBe(true); + expect(isObject([1, 2, 3])).toBe(true); + expect(isObject(Object(false))).toBe(true); + expect(isObject(new Date())).toBe(true); + expect(isObject(new Error())).toBe(true); + expect(isObject(slice)).toBe(true); + expect(isObject({ a: 1 })).toBe(true); + expect(isObject(Object(0))).toBe(true); + expect(isObject(/x/)).toBe(true); + expect(isObject(Object('a'))).toBe(true); + + if (document) { + expect(isObject(body)).toBe(true); + } + if (Symbol) { + expect(isObject(Object(symbol))).toBe(true); + } + }); + + it('should return `false` for non-objects', () => { + const values = falsey.concat(true, 1, 'a', symbol); + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value, index) => + index ? isObject(value) : isObject(), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with objects from another realm', () => { + if (realm.element) { + expect(isObject(realm.element)).toBe(true); + } + if (realm.object) { + expect(isObject(realm.boolean)).toBe(true); + expect(isObject(realm.date)).toBe(true); + expect(isObject(realm.function)).toBe(true); + expect(isObject(realm.number)).toBe(true); + expect(isObject(realm.object)).toBe(true); + expect(isObject(realm.regexp)).toBe(true); + expect(isObject(realm.string)).toBe(true); + } + }); +}); diff --git a/test/isObject.test.js b/test/isObject.test.js deleted file mode 100644 index 8e24f532a7..0000000000 --- a/test/isObject.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, slice, document, body, symbol, falsey, stubFalse, realm } from './utils.js'; -import isObject from '../isObject.js'; - -describe('isObject', function() { - it('should return `true` for objects', function() { - assert.strictEqual(isObject(args), true); - assert.strictEqual(isObject([1, 2, 3]), true); - assert.strictEqual(isObject(Object(false)), true); - assert.strictEqual(isObject(new Date), true); - assert.strictEqual(isObject(new Error), true); - assert.strictEqual(isObject(_), true); - assert.strictEqual(isObject(slice), true); - assert.strictEqual(isObject({ 'a': 1 }), true); - assert.strictEqual(isObject(Object(0)), true); - assert.strictEqual(isObject(/x/), true); - assert.strictEqual(isObject(Object('a')), true); - - if (document) { - assert.strictEqual(isObject(body), true); - } - if (Symbol) { - assert.strictEqual(isObject(Object(symbol)), true); - } - }); - - it('should return `false` for non-objects', function() { - var values = falsey.concat(true, 1, 'a', symbol), - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value, index) { - return index ? isObject(value) : isObject(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with objects from another realm', function() { - if (realm.element) { - assert.strictEqual(isObject(realm.element), true); - } - if (realm.object) { - assert.strictEqual(isObject(realm.boolean), true); - assert.strictEqual(isObject(realm.date), true); - assert.strictEqual(isObject(realm.function), true); - assert.strictEqual(isObject(realm.number), true); - assert.strictEqual(isObject(realm.object), true); - assert.strictEqual(isObject(realm.regexp), true); - assert.strictEqual(isObject(realm.string), true); - } - }); -}); diff --git a/test/isObjectLike.spec.js b/test/isObjectLike.spec.js new file mode 100644 index 0000000000..e3f28d95e8 --- /dev/null +++ b/test/isObjectLike.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { args, falsey, slice, symbol, stubFalse, realm } from './utils'; +import isObjectLike from '../src/isObjectLike'; + +describe('isObjectLike', () => { + it('should return `true` for objects', () => { + expect(isObjectLike(args)).toBe(true); + expect(isObjectLike([1, 2, 3])).toBe(true); + expect(isObjectLike(Object(false))).toBe(true); + expect(isObjectLike(new Date())).toBe(true); + expect(isObjectLike(new Error())).toBe(true); + expect(isObjectLike({ a: 1 })).toBe(true); + expect(isObjectLike(Object(0))).toBe(true); + expect(isObjectLike(/x/)).toBe(true); + expect(isObjectLike(Object('a'))).toBe(true); + }); + + it('should return `false` for non-objects', () => { + const values = falsey.concat(true, _, slice, 1, 'a', symbol); + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value, index) => + index ? isObjectLike(value) : isObjectLike(), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with objects from another realm', () => { + if (realm.object) { + expect(isObjectLike(realm.boolean)).toBe(true); + expect(isObjectLike(realm.date)).toBe(true); + expect(isObjectLike(realm.number)).toBe(true); + expect(isObjectLike(realm.object)).toBe(true); + expect(isObjectLike(realm.regexp)).toBe(true); + expect(isObjectLike(realm.string)).toBe(true); + } + }); +}); diff --git a/test/isObjectLike.test.js b/test/isObjectLike.test.js deleted file mode 100644 index 516d87b23f..0000000000 --- a/test/isObjectLike.test.js +++ /dev/null @@ -1,40 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, falsey, slice, symbol, stubFalse, realm } from './utils.js'; -import isObjectLike from '../isObjectLike.js'; - -describe('isObjectLike', function() { - it('should return `true` for objects', function() { - assert.strictEqual(isObjectLike(args), true); - assert.strictEqual(isObjectLike([1, 2, 3]), true); - assert.strictEqual(isObjectLike(Object(false)), true); - assert.strictEqual(isObjectLike(new Date), true); - assert.strictEqual(isObjectLike(new Error), true); - assert.strictEqual(isObjectLike({ 'a': 1 }), true); - assert.strictEqual(isObjectLike(Object(0)), true); - assert.strictEqual(isObjectLike(/x/), true); - assert.strictEqual(isObjectLike(Object('a')), true); - }); - - it('should return `false` for non-objects', function() { - var values = falsey.concat(true, _, slice, 1, 'a', symbol), - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value, index) { - return index ? isObjectLike(value) : isObjectLike(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with objects from another realm', function() { - if (realm.object) { - assert.strictEqual(isObjectLike(realm.boolean), true); - assert.strictEqual(isObjectLike(realm.date), true); - assert.strictEqual(isObjectLike(realm.number), true); - assert.strictEqual(isObjectLike(realm.object), true); - assert.strictEqual(isObjectLike(realm.regexp), true); - assert.strictEqual(isObjectLike(realm.string), true); - } - }); -}); diff --git a/test/isPlainObject.js b/test/isPlainObject.js deleted file mode 100644 index a50ba7b987..0000000000 --- a/test/isPlainObject.js +++ /dev/null @@ -1,114 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - document, - create, - objectProto, - falsey, - stubFalse, - symbol, - defineProperty, - realm, -} from './utils.js'; - -import isPlainObject from '../isPlainObject.js'; - -describe('isPlainObject', function() { - var element = document && document.createElement('div'); - - it('should detect plain objects', function() { - function Foo(a) { - this.a = 1; - } - - assert.strictEqual(isPlainObject({}), true); - assert.strictEqual(isPlainObject({ 'a': 1 }), true); - assert.strictEqual(isPlainObject({ 'constructor': Foo }), true); - assert.strictEqual(isPlainObject([1, 2, 3]), false); - assert.strictEqual(isPlainObject(new Foo(1)), false); - }); - - it('should return `true` for objects with a `[[Prototype]]` of `null`', function() { - var object = create(null); - assert.strictEqual(isPlainObject(object), true); - - object.constructor = objectProto.constructor; - assert.strictEqual(isPlainObject(object), true); - }); - - it('should return `true` for objects with a `valueOf` property', function() { - assert.strictEqual(isPlainObject({ 'valueOf': 0 }), true); - }); - - it('should return `true` for objects with a writable `Symbol.toStringTag` property', function() { - if (Symbol && Symbol.toStringTag) { - var object = {}; - object[Symbol.toStringTag] = 'X'; - - assert.deepStrictEqual(isPlainObject(object), true); - } - }); - - it('should return `false` for objects with a custom `[[Prototype]]`', function() { - var object = create({ 'a': 1 }); - assert.strictEqual(isPlainObject(object), false); - }); - - it('should return `false` for DOM elements', function() { - if (element) { - assert.strictEqual(isPlainObject(element), false); - } - }); - - it('should return `false` for non-Object objects', function() { - assert.strictEqual(isPlainObject(arguments), false); - assert.strictEqual(isPlainObject(Error), false); - assert.strictEqual(isPlainObject(Math), false); - }); - - it('should return `false` for non-objects', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isPlainObject(value) : isPlainObject(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isPlainObject(true), false); - assert.strictEqual(isPlainObject('a'), false); - assert.strictEqual(isPlainObject(symbol), false); - }); - - it('should return `false` for objects with a read-only `Symbol.toStringTag` property', function() { - if (Symbol && Symbol.toStringTag) { - var object = {}; - defineProperty(object, Symbol.toStringTag, { - 'configurable': true, - 'enumerable': false, - 'writable': false, - 'value': 'X' - }); - - assert.deepStrictEqual(isPlainObject(object), false); - } - }); - - it('should not mutate `value`', function() { - if (Symbol && Symbol.toStringTag) { - var proto = {}; - proto[Symbol.toStringTag] = undefined; - var object = create(proto); - - assert.strictEqual(isPlainObject(object), false); - assert.ok(!lodashStable.has(object, Symbol.toStringTag)); - } - }); - - it('should work with objects from another realm', function() { - if (realm.object) { - assert.strictEqual(isPlainObject(realm.object), true); - } - }); -}); diff --git a/test/isPlainObject.spec.js b/test/isPlainObject.spec.js new file mode 100644 index 0000000000..d15c75c0ff --- /dev/null +++ b/test/isPlainObject.spec.js @@ -0,0 +1,113 @@ +import lodashStable from 'lodash'; + +import { + document, + create, + objectProto, + falsey, + stubFalse, + symbol, + defineProperty, + realm, +} from './utils'; + +import isPlainObject from '../src/isPlainObject'; + +describe('isPlainObject', () => { + const element = document && document.createElement('div'); + + it('should detect plain objects', () => { + function Foo(a) { + this.a = 1; + } + + expect(isPlainObject({})).toBe(true); + expect(isPlainObject({ a: 1 })).toBe(true); + expect(isPlainObject({ constructor: Foo })).toBe(true); + expect(isPlainObject([1, 2, 3])).toBe(false); + expect(isPlainObject(new Foo(1))).toBe(false); + }); + + it('should return `true` for objects with a `[[Prototype]]` of `null`', () => { + const object = create(null); + expect(isPlainObject(object)).toBe(true); + + object.constructor = objectProto.constructor; + expect(isPlainObject(object)).toBe(true); + }); + + it('should return `true` for objects with a `valueOf` property', () => { + expect(isPlainObject({ valueOf: 0 })).toBe(true); + }); + + it('should return `true` for objects with a writable `Symbol.toStringTag` property', () => { + if (Symbol && Symbol.toStringTag) { + const object = {}; + object[Symbol.toStringTag] = 'X'; + + expect(isPlainObject(object)).toEqual(true); + } + }); + + it('should return `false` for objects with a custom `[[Prototype]]`', () => { + const object = create({ a: 1 }); + expect(isPlainObject(object)).toBe(false); + }); + + it('should return `false` for DOM elements', () => { + if (element) { + expect(isPlainObject(element)).toBe(false); + } + }); + + it('should return `false` for non-Object objects', function () { + expect(isPlainObject(arguments)).toBe(false); + expect(isPlainObject(Error)).toBe(false); + expect(isPlainObject(Math)).toBe(false); + }); + + it('should return `false` for non-objects', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isPlainObject(value) : isPlainObject(), + ); + + expect(actual).toEqual(expected); + + expect(isPlainObject(true)).toBe(false); + expect(isPlainObject('a')).toBe(false); + expect(isPlainObject(symbol)).toBe(false); + }); + + it('should return `false` for objects with a read-only `Symbol.toStringTag` property', () => { + if (Symbol && Symbol.toStringTag) { + const object = {}; + defineProperty(object, Symbol.toStringTag, { + configurable: true, + enumerable: false, + writable: false, + value: 'X', + }); + + expect(isPlainObject(object)).toEqual(false); + } + }); + + it('should not mutate `value`', () => { + if (Symbol && Symbol.toStringTag) { + const proto = {}; + proto[Symbol.toStringTag] = undefined; + const object = create(proto); + + expect(isPlainObject(object)).toBe(false); + expect(lodashStable.has(object, Symbol.toStringTag)).toBe(false); + } + }); + + it('should work with objects from another realm', () => { + if (realm.object) { + expect(isPlainObject(realm.object)).toBe(true); + } + }); +}); diff --git a/test/isRegExp.spec.js b/test/isRegExp.spec.js new file mode 100644 index 0000000000..037431b580 --- /dev/null +++ b/test/isRegExp.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { falsey, stubFalse, args, slice, symbol, realm } from './utils'; +import isRegExp from '../src/isRegExp'; + +describe('isRegExp', () => { + it('should return `true` for regexes', () => { + expect(isRegExp(/x/)).toBe(true); + expect(isRegExp(RegExp('x'))).toBe(true); + }); + + it('should return `false` for non-regexes', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isRegExp(value) : isRegExp(), + ); + + expect(actual).toEqual(expected); + + expect(isRegExp(args)).toBe(false); + expect(isRegExp([1, 2, 3])).toBe(false); + expect(isRegExp(true)).toBe(false); + expect(isRegExp(new Date())).toBe(false); + expect(isRegExp(new Error())).toBe(false); + expect(isRegExp(slice)).toBe(false); + expect(isRegExp({ a: 1 })).toBe(false); + expect(isRegExp(1)).toBe(false); + expect(isRegExp('a')).toBe(false); + expect(isRegExp(symbol)).toBe(false); + }); + + it('should work with regexes from another realm', () => { + if (realm.regexp) { + expect(isRegExp(realm.regexp)).toBe(true); + } + }); +}); diff --git a/test/isRegExp.test.js b/test/isRegExp.test.js deleted file mode 100644 index 507cdd474a..0000000000 --- a/test/isRegExp.test.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubFalse, args, slice, symbol, realm } from './utils.js'; -import isRegExp from '../isRegExp.js'; - -describe('isRegExp', function() { - it('should return `true` for regexes', function() { - assert.strictEqual(isRegExp(/x/), true); - assert.strictEqual(isRegExp(RegExp('x')), true); - }); - - it('should return `false` for non-regexes', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isRegExp(value) : isRegExp(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isRegExp(args), false); - assert.strictEqual(isRegExp([1, 2, 3]), false); - assert.strictEqual(isRegExp(true), false); - assert.strictEqual(isRegExp(new Date), false); - assert.strictEqual(isRegExp(new Error), false); - assert.strictEqual(isRegExp(_), false); - assert.strictEqual(isRegExp(slice), false); - assert.strictEqual(isRegExp({ 'a': 1 }), false); - assert.strictEqual(isRegExp(1), false); - assert.strictEqual(isRegExp('a'), false); - assert.strictEqual(isRegExp(symbol), false); - }); - - it('should work with regexes from another realm', function() { - if (realm.regexp) { - assert.strictEqual(isRegExp(realm.regexp), true); - } - }); -}); diff --git a/test/isSet.spec.js b/test/isSet.spec.js new file mode 100644 index 0000000000..711a61b4c2 --- /dev/null +++ b/test/isSet.spec.js @@ -0,0 +1,47 @@ +import lodashStable from 'lodash'; +import { set, falsey, stubFalse, args, slice, symbol, weakSet, realm } from './utils'; +import isSet from '../src/isSet'; + +describe('isSet', () => { + it('should return `true` for sets', () => { + if (Set) { + expect(isSet(set)).toBe(true); + } + }); + + it('should return `false` for non-sets', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => (index ? isSet(value) : isSet())); + + expect(actual).toEqual(expected); + + expect(isSet(args)).toBe(false); + expect(isSet([1, 2, 3])).toBe(false); + expect(isSet(true)).toBe(false); + expect(isSet(new Date())).toBe(false); + expect(isSet(new Error())).toBe(false); + expect(isSet(slice)).toBe(false); + expect(isSet({ a: 1 })).toBe(false); + expect(isSet(1)).toBe(false); + expect(isSet(/x/)).toBe(false); + expect(isSet('a')).toBe(false); + expect(isSet(symbol)).toBe(false); + expect(isSet(weakSet)).toBe(false); + }); + + it('should work for objects with a non-function `constructor` (test in IE 11)', () => { + const values = [false, true]; + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value) => isSet({ constructor: value })); + + expect(actual).toEqual(expected); + }); + + it('should work with weak sets from another realm', () => { + if (realm.set) { + expect(isSet(realm.set)).toBe(true); + } + }); +}); diff --git a/test/isSet.test.js b/test/isSet.test.js deleted file mode 100644 index 49d9d9065e..0000000000 --- a/test/isSet.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { set, falsey, stubFalse, args, slice, symbol, weakSet, realm } from './utils.js'; -import isSet from '../isSet.js'; - -describe('isSet', function() { - it('should return `true` for sets', function() { - if (Set) { - assert.strictEqual(isSet(set), true); - } - }); - - it('should return `false` for non-sets', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isSet(value) : isSet(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isSet(args), false); - assert.strictEqual(isSet([1, 2, 3]), false); - assert.strictEqual(isSet(true), false); - assert.strictEqual(isSet(new Date), false); - assert.strictEqual(isSet(new Error), false); - assert.strictEqual(isSet(_), false); - assert.strictEqual(isSet(slice), false); - assert.strictEqual(isSet({ 'a': 1 }), false); - assert.strictEqual(isSet(1), false); - assert.strictEqual(isSet(/x/), false); - assert.strictEqual(isSet('a'), false); - assert.strictEqual(isSet(symbol), false); - assert.strictEqual(isSet(weakSet), false); - }); - - it('should work for objects with a non-function `constructor` (test in IE 11)', function() { - var values = [false, true], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value) { - return isSet({ 'constructor': value }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with weak sets from another realm', function() { - if (realm.set) { - assert.strictEqual(isSet(realm.set), true); - } - }); -}); diff --git a/test/isString.spec.js b/test/isString.spec.js new file mode 100644 index 0000000000..2e6dbc5290 --- /dev/null +++ b/test/isString.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isString from '../src/isString'; + +describe('isString', () => { + it('should return `true` for strings', () => { + expect(isString('a')).toBe(true); + expect(isString(Object('a'))).toBe(true); + }); + + it('should return `false` for non-strings', () => { + const expected = lodashStable.map(falsey, (value) => value === ''); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isString(value) : isString(), + ); + + expect(actual).toEqual(expected); + + expect(isString(args)).toBe(false); + expect(isString([1, 2, 3])).toBe(false); + expect(isString(true)).toBe(false); + expect(isString(new Date())).toBe(false); + expect(isString(new Error())).toBe(false); + expect(isString(slice)).toBe(false); + expect(isString({ 0: 1, length: 1 })).toBe(false); + expect(isString(1)).toBe(false); + expect(isString(/x/)).toBe(false); + expect(isString(symbol)).toBe(false); + }); + + it('should work with strings from another realm', () => { + if (realm.string) { + expect(isString(realm.string)).toBe(true); + } + }); +}); diff --git a/test/isString.test.js b/test/isString.test.js deleted file mode 100644 index e046d507f5..0000000000 --- a/test/isString.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isString from '../isString.js'; - -describe('isString', function() { - it('should return `true` for strings', function() { - assert.strictEqual(isString('a'), true); - assert.strictEqual(isString(Object('a')), true); - }); - - it('should return `false` for non-strings', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === ''; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isString(value) : isString(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isString(args), false); - assert.strictEqual(isString([1, 2, 3]), false); - assert.strictEqual(isString(true), false); - assert.strictEqual(isString(new Date), false); - assert.strictEqual(isString(new Error), false); - assert.strictEqual(isString(_), false); - assert.strictEqual(isString(slice), false); - assert.strictEqual(isString({ '0': 1, 'length': 1 }), false); - assert.strictEqual(isString(1), false); - assert.strictEqual(isString(/x/), false); - assert.strictEqual(isString(symbol), false); - }); - - it('should work with strings from another realm', function() { - if (realm.string) { - assert.strictEqual(isString(realm.string), true); - } - }); -}); diff --git a/test/isSymbol.spec.js b/test/isSymbol.spec.js new file mode 100644 index 0000000000..320e4fe4cb --- /dev/null +++ b/test/isSymbol.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { symbol, falsey, stubFalse, args, slice, realm } from './utils'; +import isSymbol from '../src/isSymbol'; + +describe('isSymbol', () => { + it('should return `true` for symbols', () => { + if (Symbol) { + expect(isSymbol(symbol)).toBe(true); + expect(isSymbol(Object(symbol))).toBe(true); + } + }); + + it('should return `false` for non-symbols', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isSymbol(value) : isSymbol(), + ); + + expect(actual).toEqual(expected); + + expect(isSymbol(args)).toBe(false); + expect(isSymbol([1, 2, 3])).toBe(false); + expect(isSymbol(true)).toBe(false); + expect(isSymbol(new Date())).toBe(false); + expect(isSymbol(new Error())).toBe(false); + expect(isSymbol(slice)).toBe(false); + expect(isSymbol({ 0: 1, length: 1 })).toBe(false); + expect(isSymbol(1)).toBe(false); + expect(isSymbol(/x/)).toBe(false); + expect(isSymbol('a')).toBe(false); + }); + + it('should work with symbols from another realm', () => { + if (Symbol && realm.symbol) { + expect(isSymbol(realm.symbol)).toBe(true); + } + }); +}); diff --git a/test/isSymbol.test.js b/test/isSymbol.test.js deleted file mode 100644 index bf546c649a..0000000000 --- a/test/isSymbol.test.js +++ /dev/null @@ -1,41 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { symbol, falsey, stubFalse, args, slice, realm } from './utils.js'; -import isSymbol from '../isSymbol.js'; - -describe('isSymbol', function() { - it('should return `true` for symbols', function() { - if (Symbol) { - assert.strictEqual(isSymbol(symbol), true); - assert.strictEqual(isSymbol(Object(symbol)), true); - } - }); - - it('should return `false` for non-symbols', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isSymbol(value) : isSymbol(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isSymbol(args), false); - assert.strictEqual(isSymbol([1, 2, 3]), false); - assert.strictEqual(isSymbol(true), false); - assert.strictEqual(isSymbol(new Date), false); - assert.strictEqual(isSymbol(new Error), false); - assert.strictEqual(isSymbol(_), false); - assert.strictEqual(isSymbol(slice), false); - assert.strictEqual(isSymbol({ '0': 1, 'length': 1 }), false); - assert.strictEqual(isSymbol(1), false); - assert.strictEqual(isSymbol(/x/), false); - assert.strictEqual(isSymbol('a'), false); - }); - - it('should work with symbols from another realm', function() { - if (Symbol && realm.symbol) { - assert.strictEqual(isSymbol(realm.symbol), true); - } - }); -}); diff --git a/test/isType-checks.js b/test/isType-checks.js deleted file mode 100644 index 9096697212..0000000000 --- a/test/isType-checks.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { objToString, objectTag, _, xml } from './utils.js'; - -describe('isType checks', function() { - it('should return `false` for subclassed values', function() { - var funcs = [ - 'isArray', 'isBoolean', 'isDate', 'isFunction', - 'isNumber', 'isRegExp', 'isString' - ]; - - lodashStable.each(funcs, function(methodName) { - function Foo() {} - Foo.prototype = root[methodName.slice(2)].prototype; - - var object = new Foo; - if (objToString.call(object) == objectTag) { - assert.strictEqual(_[methodName](object), false, '`_.' + methodName + '` returns `false`'); - } - }); - }); - - it('should not error on host objects (test in IE)', function() { - var funcs = [ - 'isArguments', 'isArray', 'isArrayBuffer', 'isArrayLike', 'isBoolean', - 'isBuffer', 'isDate', 'isElement', 'isError', 'isFinite', 'isFunction', - 'isInteger', 'isMap', 'isNaN', 'isNil', 'isNull', 'isNumber', 'isObject', - 'isObjectLike', 'isRegExp', 'isSet', 'isSafeInteger', 'isString', - 'isUndefined', 'isWeakMap', 'isWeakSet' - ]; - - lodashStable.each(funcs, function(methodName) { - if (xml) { - _[methodName](xml); - assert.ok(true, '`_.' + methodName + '` should not error'); - } - }); - }); -}); diff --git a/test/isType-checks.spec.js b/test/isType-checks.spec.js new file mode 100644 index 0000000000..2a0eb3ace1 --- /dev/null +++ b/test/isType-checks.spec.js @@ -0,0 +1,68 @@ +import lodashStable from 'lodash'; +import { objToString, objectTag, _, xml } from './utils'; + +describe('isType checks', () => { + it('should return `false` for subclassed values', () => { + const funcs = [ + 'isArray', + 'isBoolean', + 'isDate', + 'isFunction', + 'isNumber', + 'isRegExp', + 'isString', + ]; + + lodashStable.each(funcs, (methodName) => { + function Foo() {} + Foo.prototype = root[methodName.slice(2)].prototype; + + const object = new Foo(); + if (objToString.call(object) === objectTag) { + assert.strictEqual( + _[methodName](object), + false, + `\`_.${methodName}\` returns \`false\``, + ); + } + }); + }); + + it('should not error on host objects (test in IE)', () => { + const funcs = [ + 'isArguments', + 'isArray', + 'isArrayBuffer', + 'isArrayLike', + 'isBoolean', + 'isBuffer', + 'isDate', + 'isElement', + 'isError', + 'isFinite', + 'isFunction', + 'isInteger', + 'isMap', + 'isNaN', + 'isNil', + 'isNull', + 'isNumber', + 'isObject', + 'isObjectLike', + 'isRegExp', + 'isSet', + 'isSafeInteger', + 'isString', + 'isUndefined', + 'isWeakMap', + 'isWeakSet', + ]; + + lodashStable.each(funcs, (methodName) => { + if (xml) { + _[methodName](xml); + expect(true, `\`_.${methodName}\` should not error`); + } + }); + }); +}); diff --git a/test/isTypedArray.js b/test/isTypedArray.js deleted file mode 100644 index 3c0d8be2e8..0000000000 --- a/test/isTypedArray.js +++ /dev/null @@ -1,59 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { typedArrays, falsey, stubFalse, args, slice, symbol, realm } from './utils.js'; -import isTypedArray from '../isTypedArray.js'; - -describe('isTypedArray', function() { - it('should return `true` for typed arrays', function() { - var expected = lodashStable.map(typedArrays, function(type) { - return type in root; - }); - - var actual = lodashStable.map(typedArrays, function(type) { - var Ctor = root[type]; - return Ctor ? isTypedArray(new Ctor(new ArrayBuffer(8))) : false; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `false` for non typed arrays', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isTypedArray(value) : isTypedArray(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isTypedArray(args), false); - assert.strictEqual(isTypedArray([1, 2, 3]), false); - assert.strictEqual(isTypedArray(true), false); - assert.strictEqual(isTypedArray(new Date), false); - assert.strictEqual(isTypedArray(new Error), false); - assert.strictEqual(isTypedArray(_), false); - assert.strictEqual(isTypedArray(slice), false); - assert.strictEqual(isTypedArray({ 'a': 1 }), false); - assert.strictEqual(isTypedArray(1), false); - assert.strictEqual(isTypedArray(/x/), false); - assert.strictEqual(isTypedArray('a'), false); - assert.strictEqual(isTypedArray(symbol), false); - }); - - it('should work with typed arrays from another realm', function() { - if (realm.object) { - var props = lodashStable.invokeMap(typedArrays, 'toLowerCase'); - - var expected = lodashStable.map(props, function(key) { - return realm[key] !== undefined; - }); - - var actual = lodashStable.map(props, function(key) { - var value = realm[key]; - return value ? isTypedArray(value) : false; - }); - - assert.deepStrictEqual(actual, expected); - } - }); -}); diff --git a/test/isTypedArray.spec.js b/test/isTypedArray.spec.js new file mode 100644 index 0000000000..84ec880a16 --- /dev/null +++ b/test/isTypedArray.spec.js @@ -0,0 +1,53 @@ +import lodashStable from 'lodash'; +import { typedArrays, falsey, stubFalse, args, slice, symbol, realm, root } from './utils'; +import isTypedArray from '../src/isTypedArray'; + +describe('isTypedArray', () => { + it('should return `true` for typed arrays', () => { + const expected = lodashStable.map(typedArrays, (type) => type in root); + + const actual = lodashStable.map(typedArrays, (type) => { + const Ctor = root[type]; + return Ctor ? isTypedArray(new Ctor(new ArrayBuffer(8))) : false; + }); + + expect(actual).toEqual(expected); + }); + + it('should return `false` for non typed arrays', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isTypedArray(value) : isTypedArray(), + ); + + expect(actual).toEqual(expected); + + expect(isTypedArray(args)).toBe(false); + expect(isTypedArray([1, 2, 3])).toBe(false); + expect(isTypedArray(true)).toBe(false); + expect(isTypedArray(new Date())).toBe(false); + expect(isTypedArray(new Error())).toBe(false); + expect(isTypedArray(slice)).toBe(false); + expect(isTypedArray({ a: 1 })).toBe(false); + expect(isTypedArray(1)).toBe(false); + expect(isTypedArray(/x/)).toBe(false); + expect(isTypedArray('a')).toBe(false); + expect(isTypedArray(symbol)).toBe(false); + }); + + it('should work with typed arrays from another realm', () => { + if (realm.object) { + const props = lodashStable.invokeMap(typedArrays, 'toLowerCase'); + + const expected = lodashStable.map(props, (key) => realm[key] !== undefined); + + const actual = lodashStable.map(props, (key) => { + const value = realm[key]; + return value ? isTypedArray(value) : false; + }); + + expect(actual).toEqual(expected); + } + }); +}); diff --git a/test/isUndefined.spec.js b/test/isUndefined.spec.js new file mode 100644 index 0000000000..90de0b4775 --- /dev/null +++ b/test/isUndefined.spec.js @@ -0,0 +1,41 @@ +import lodashStable from 'lodash'; +import { falsey, args, slice, symbol, realm } from './utils'; +import isUndefined from '../src/isUndefined'; + +describe('isUndefined', () => { + it('should return `true` for `undefined` values', () => { + expect(isUndefined()).toBe(true); + expect(isUndefined(undefined)).toBe(true); + }); + + it('should return `false` for non `undefined` values', () => { + const expected = lodashStable.map(falsey, (value) => value === undefined); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isUndefined(value) : isUndefined(), + ); + + expect(actual).toEqual(expected); + + expect(isUndefined(args)).toBe(false); + expect(isUndefined([1, 2, 3])).toBe(false); + expect(isUndefined(true)).toBe(false); + expect(isUndefined(new Date())).toBe(false); + expect(isUndefined(new Error())).toBe(false); + expect(isUndefined(slice)).toBe(false); + expect(isUndefined({ a: 1 })).toBe(false); + expect(isUndefined(1)).toBe(false); + expect(isUndefined(/x/)).toBe(false); + expect(isUndefined('a')).toBe(false); + + if (Symbol) { + expect(isUndefined(symbol)).toBe(false); + } + }); + + it('should work with `undefined` from another realm', () => { + if (realm.object) { + expect(isUndefined(realm.undefined)).toBe(true); + } + }); +}); diff --git a/test/isUndefined.test.js b/test/isUndefined.test.js deleted file mode 100644 index 4f8c1fcebb..0000000000 --- a/test/isUndefined.test.js +++ /dev/null @@ -1,45 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, args, slice, symbol, realm } from './utils.js'; -import isUndefined from '../isUndefined.js'; - -describe('isUndefined', function() { - it('should return `true` for `undefined` values', function() { - assert.strictEqual(isUndefined(), true); - assert.strictEqual(isUndefined(undefined), true); - }); - - it('should return `false` for non `undefined` values', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined; - }); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isUndefined(value) : isUndefined(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isUndefined(args), false); - assert.strictEqual(isUndefined([1, 2, 3]), false); - assert.strictEqual(isUndefined(true), false); - assert.strictEqual(isUndefined(new Date), false); - assert.strictEqual(isUndefined(new Error), false); - assert.strictEqual(isUndefined(_), false); - assert.strictEqual(isUndefined(slice), false); - assert.strictEqual(isUndefined({ 'a': 1 }), false); - assert.strictEqual(isUndefined(1), false); - assert.strictEqual(isUndefined(/x/), false); - assert.strictEqual(isUndefined('a'), false); - - if (Symbol) { - assert.strictEqual(isUndefined(symbol), false); - } - }); - - it('should work with `undefined` from another realm', function() { - if (realm.object) { - assert.strictEqual(isUndefined(realm.undefined), true); - } - }); -}); diff --git a/test/isWeakMap.spec.js b/test/isWeakMap.spec.js new file mode 100644 index 0000000000..24fe7002eb --- /dev/null +++ b/test/isWeakMap.spec.js @@ -0,0 +1,49 @@ +import lodashStable from 'lodash'; +import { weakMap, falsey, stubFalse, args, slice, map, symbol, realm } from './utils'; +import isWeakMap from '../src/isWeakMap'; + +describe('isWeakMap', () => { + it('should return `true` for weak maps', () => { + if (WeakMap) { + expect(isWeakMap(weakMap)).toBe(true); + } + }); + + it('should return `false` for non weak maps', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isWeakMap(value) : isWeakMap(), + ); + + expect(actual).toEqual(expected); + + expect(isWeakMap(args)).toBe(false); + expect(isWeakMap([1, 2, 3])).toBe(false); + expect(isWeakMap(true)).toBe(false); + expect(isWeakMap(new Date())).toBe(false); + expect(isWeakMap(new Error())).toBe(false); + expect(isWeakMap(slice)).toBe(false); + expect(isWeakMap({ a: 1 })).toBe(false); + expect(isWeakMap(map)).toBe(false); + expect(isWeakMap(1)).toBe(false); + expect(isWeakMap(/x/)).toBe(false); + expect(isWeakMap('a')).toBe(false); + expect(isWeakMap(symbol)).toBe(false); + }); + + it('should work for objects with a non-function `constructor` (test in IE 11)', () => { + const values = [false, true]; + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value) => isWeakMap({ constructor: value })); + + expect(actual).toEqual(expected); + }); + + it('should work with weak maps from another realm', () => { + if (realm.weakMap) { + expect(isWeakMap(realm.weakMap)).toBe(true); + } + }); +}); diff --git a/test/isWeakMap.test.js b/test/isWeakMap.test.js deleted file mode 100644 index f0c1532079..0000000000 --- a/test/isWeakMap.test.js +++ /dev/null @@ -1,53 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { weakMap, falsey, stubFalse, args, slice, map, symbol, realm } from './utils.js'; -import isWeakMap from '../isWeakMap.js'; - -describe('isWeakMap', function() { - it('should return `true` for weak maps', function() { - if (WeakMap) { - assert.strictEqual(isWeakMap(weakMap), true); - } - }); - - it('should return `false` for non weak maps', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isWeakMap(value) : isWeakMap(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isWeakMap(args), false); - assert.strictEqual(isWeakMap([1, 2, 3]), false); - assert.strictEqual(isWeakMap(true), false); - assert.strictEqual(isWeakMap(new Date), false); - assert.strictEqual(isWeakMap(new Error), false); - assert.strictEqual(isWeakMap(_), false); - assert.strictEqual(isWeakMap(slice), false); - assert.strictEqual(isWeakMap({ 'a': 1 }), false); - assert.strictEqual(isWeakMap(map), false); - assert.strictEqual(isWeakMap(1), false); - assert.strictEqual(isWeakMap(/x/), false); - assert.strictEqual(isWeakMap('a'), false); - assert.strictEqual(isWeakMap(symbol), false); - }); - - it('should work for objects with a non-function `constructor` (test in IE 11)', function() { - var values = [false, true], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value) { - return isWeakMap({ 'constructor': value }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with weak maps from another realm', function() { - if (realm.weakMap) { - assert.strictEqual(isWeakMap(realm.weakMap), true); - } - }); -}); diff --git a/test/isWeakSet.spec.js b/test/isWeakSet.spec.js new file mode 100644 index 0000000000..ac628bc3c3 --- /dev/null +++ b/test/isWeakSet.spec.js @@ -0,0 +1,40 @@ +import lodashStable from 'lodash'; +import { weakSet, falsey, stubFalse, args, slice, set, symbol, realm } from './utils'; +import isWeakSet from '../src/isWeakSet'; + +describe('isWeakSet', () => { + it('should return `true` for weak sets', () => { + if (WeakSet) { + expect(isWeakSet(weakSet)).toBe(true); + } + }); + + it('should return `false` for non weak sets', () => { + const expected = lodashStable.map(falsey, stubFalse); + + const actual = lodashStable.map(falsey, (value, index) => + index ? isWeakSet(value) : isWeakSet(), + ); + + expect(actual).toEqual(expected); + + expect(isWeakSet(args)).toBe(false); + expect(isWeakSet([1, 2, 3])).toBe(false); + expect(isWeakSet(true)).toBe(false); + expect(isWeakSet(new Date())).toBe(false); + expect(isWeakSet(new Error())).toBe(false); + expect(isWeakSet(slice)).toBe(false); + expect(isWeakSet({ a: 1 })).toBe(false); + expect(isWeakSet(1)).toBe(false); + expect(isWeakSet(/x/)).toBe(false); + expect(isWeakSet('a')).toBe(false); + expect(isWeakSet(set)).toBe(false); + expect(isWeakSet(symbol)).toBe(false); + }); + + it('should work with weak sets from another realm', () => { + if (realm.weakSet) { + expect(isWeakSet(realm.weakSet)).toBe(true); + } + }); +}); diff --git a/test/isWeakSet.test.js b/test/isWeakSet.test.js deleted file mode 100644 index a974bf1f54..0000000000 --- a/test/isWeakSet.test.js +++ /dev/null @@ -1,42 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { weakSet, falsey, stubFalse, args, slice, set, symbol, realm } from './utils.js'; -import isWeakSet from '../isWeakSet.js'; - -describe('isWeakSet', function() { - it('should return `true` for weak sets', function() { - if (WeakSet) { - assert.strictEqual(isWeakSet(weakSet), true); - } - }); - - it('should return `false` for non weak sets', function() { - var expected = lodashStable.map(falsey, stubFalse); - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? isWeakSet(value) : isWeakSet(); - }); - - assert.deepStrictEqual(actual, expected); - - assert.strictEqual(isWeakSet(args), false); - assert.strictEqual(isWeakSet([1, 2, 3]), false); - assert.strictEqual(isWeakSet(true), false); - assert.strictEqual(isWeakSet(new Date), false); - assert.strictEqual(isWeakSet(new Error), false); - assert.strictEqual(isWeakSet(_), false); - assert.strictEqual(isWeakSet(slice), false); - assert.strictEqual(isWeakSet({ 'a': 1 }), false); - assert.strictEqual(isWeakSet(1), false); - assert.strictEqual(isWeakSet(/x/), false); - assert.strictEqual(isWeakSet('a'), false); - assert.strictEqual(isWeakSet(set), false); - assert.strictEqual(isWeakSet(symbol), false); - }); - - it('should work with weak sets from another realm', function() { - if (realm.weakSet) { - assert.strictEqual(isWeakSet(realm.weakSet), true); - } - }); -}); diff --git a/test/iteratee.js b/test/iteratee.js deleted file mode 100644 index fd653dd2eb..0000000000 --- a/test/iteratee.js +++ /dev/null @@ -1,164 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, _, isNpm, push, stubFalse } from './utils.js'; -import partial from '../partial.js'; -import partialRight from '../partialRight.js'; -import map from '../map.js'; - -describe('iteratee', function() { - it('should provide arguments to `func`', function() { - var fn = function() { return slice.call(arguments); }, - iteratee = _.iteratee(fn), - actual = iteratee('a', 'b', 'c', 'd', 'e', 'f'); - - assert.deepStrictEqual(actual, ['a', 'b', 'c', 'd', 'e', 'f']); - }); - - it('should return `_.identity` when `func` is nullish', function() { - var object = {}, - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([!isNpm && _.identity, object])); - - var actual = lodashStable.map(values, function(value, index) { - var identity = index ? _.iteratee(value) : _.iteratee(); - return [!isNpm && identity, identity(object)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an iteratee created by `_.matches` when `func` is an object', function() { - var matches = _.iteratee({ 'a': 1, 'b': 2 }); - assert.strictEqual(matches({ 'a': 1, 'b': 2, 'c': 3 }), true); - assert.strictEqual(matches({ 'b': 2 }), false); - }); - - it('should not change `_.matches` behavior if `source` is modified', function() { - var sources = [ - { 'a': { 'b': 2, 'c': 3 } }, - { 'a': 1, 'b': 2 }, - { 'a': 1 } - ]; - - lodashStable.each(sources, function(source, index) { - var object = lodashStable.cloneDeep(source), - matches = _.iteratee(source); - - assert.strictEqual(matches(object), true); - - if (index) { - source.a = 2; - source.b = 1; - source.c = 3; - } else { - source.a.b = 1; - source.a.c = 2; - source.a.d = 3; - } - assert.strictEqual(matches(object), true); - assert.strictEqual(matches(source), false); - }); - }); - - it('should return an iteratee created by `_.matchesProperty` when `func` is an array', function() { - var array = ['a', undefined], - matches = _.iteratee([0, 'a']); - - assert.strictEqual(matches(array), true); - - matches = _.iteratee(['0', 'a']); - assert.strictEqual(matches(array), true); - - matches = _.iteratee([1, undefined]); - assert.strictEqual(matches(array), true); - }); - - it('should support deep paths for `_.matchesProperty` shorthands', function() { - var object = { 'a': { 'b': { 'c': 1, 'd': 2 } } }, - matches = _.iteratee(['a.b', { 'c': 1 }]); - - assert.strictEqual(matches(object), true); - }); - - it('should not change `_.matchesProperty` behavior if `source` is modified', function() { - var sources = [ - { 'a': { 'b': 2, 'c': 3 } }, - { 'a': 1, 'b': 2 }, - { 'a': 1 } - ]; - - lodashStable.each(sources, function(source, index) { - var object = { 'a': lodashStable.cloneDeep(source) }, - matches = _.iteratee(['a', source]); - - assert.strictEqual(matches(object), true); - - if (index) { - source.a = 2; - source.b = 1; - source.c = 3; - } else { - source.a.b = 1; - source.a.c = 2; - source.a.d = 3; - } - assert.strictEqual(matches(object), true); - assert.strictEqual(matches({ 'a': source }), false); - }); - }); - - it('should return an iteratee created by `_.property` when `func` is a number or string', function() { - var array = ['a'], - prop = _.iteratee(0); - - assert.strictEqual(prop(array), 'a'); - - prop = _.iteratee('0'); - assert.strictEqual(prop(array), 'a'); - }); - - it('should support deep paths for `_.property` shorthands', function() { - var object = { 'a': { 'b': 2 } }, - prop = _.iteratee('a.b'); - - assert.strictEqual(prop(object), 2); - }); - - it('should work with functions created by `_.partial` and `_.partialRight`', function() { - var fn = function() { - var result = [this.a]; - push.apply(result, arguments); - return result; - }; - - var expected = [1, 2, 3], - object = { 'a': 1 , 'iteratee': _.iteratee(partial(fn, 2)) }; - - assert.deepStrictEqual(object.iteratee(3), expected); - - object.iteratee = _.iteratee(partialRight(fn, 3)); - assert.deepStrictEqual(object.iteratee(2), expected); - }); - - it('should use internal `iteratee` if external is unavailable', function() { - var iteratee = _.iteratee; - delete _.iteratee; - - assert.deepStrictEqual(map([{ 'a': 1 }], 'a'), [1]); - - _.iteratee = iteratee; - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var fn = function() { return this instanceof Number; }, - array = [fn, fn, fn], - iteratees = lodashStable.map(array, _.iteratee), - expected = lodashStable.map(array, stubFalse); - - var actual = lodashStable.map(iteratees, function(iteratee) { - return iteratee(); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/iteratee.spec.js b/test/iteratee.spec.js new file mode 100644 index 0000000000..b9fa5a771c --- /dev/null +++ b/test/iteratee.spec.js @@ -0,0 +1,160 @@ +import lodashStable from 'lodash'; +import { slice, _, isNpm, push, stubFalse } from './utils'; +import partial from '../src/partial'; +import partialRight from '../src/partialRight'; +import map from '../src/map'; + +describe('iteratee', () => { + it('should provide arguments to `func`', () => { + const fn = function () { + return slice.call(arguments); + }; + const iteratee = _.iteratee(fn); + const actual = iteratee('a', 'b', 'c', 'd', 'e', 'f'); + + expect(actual, ['a', 'b', 'c', 'd', 'e').toEqual('f']); + }); + + it('should return `_.identity` when `func` is nullish', () => { + const object = {}; + const values = [, null, undefined]; + const expected = lodashStable.map( + values, + lodashStable.constant([!isNpm && _.identity, object]), + ); + + const actual = lodashStable.map(values, (value, index) => { + const identity = index ? _.iteratee(value) : _.iteratee(); + return [!isNpm && identity, identity(object)]; + }); + + expect(actual).toEqual(expected); + }); + + it('should return an iteratee created by `_.matches` when `func` is an object', () => { + const matches = _.iteratee({ a: 1, b: 2 }); + expect(matches({ a: 1, b: 2, c: 3 })).toBe(true); + expect(matches({ b: 2 })).toBe(false); + }); + + it('should not change `_.matches` behavior if `source` is modified', () => { + const sources = [{ a: { b: 2, c: 3 } }, { a: 1, b: 2 }, { a: 1 }]; + + lodashStable.each(sources, (source, index) => { + const object = lodashStable.cloneDeep(source); + const matches = _.iteratee(source); + + expect(matches(object)).toBe(true); + + if (index) { + source.a = 2; + source.b = 1; + source.c = 3; + } else { + source.a.b = 1; + source.a.c = 2; + source.a.d = 3; + } + expect(matches(object)).toBe(true); + expect(matches(source)).toBe(false); + }); + }); + + it('should return an iteratee created by `_.matchesProperty` when `func` is an array', () => { + const array = ['a', undefined]; + let matches = _.iteratee([0, 'a']); + + expect(matches(array)).toBe(true); + + matches = _.iteratee(['0', 'a']); + expect(matches(array)).toBe(true); + + matches = _.iteratee([1, undefined]); + expect(matches(array)).toBe(true); + }); + + it('should support deep paths for `_.matchesProperty` shorthands', () => { + const object = { a: { b: { c: 1, d: 2 } } }; + const matches = _.iteratee(['a.b', { c: 1 }]); + + expect(matches(object)).toBe(true); + }); + + it('should not change `_.matchesProperty` behavior if `source` is modified', () => { + const sources = [{ a: { b: 2, c: 3 } }, { a: 1, b: 2 }, { a: 1 }]; + + lodashStable.each(sources, (source, index) => { + const object = { a: lodashStable.cloneDeep(source) }; + const matches = _.iteratee(['a', source]); + + expect(matches(object)).toBe(true); + + if (index) { + source.a = 2; + source.b = 1; + source.c = 3; + } else { + source.a.b = 1; + source.a.c = 2; + source.a.d = 3; + } + expect(matches(object)).toBe(true); + expect(matches({ a: source })).toBe(false); + }); + }); + + it('should return an iteratee created by `_.property` when `func` is a number or string', () => { + const array = ['a']; + let prop = _.iteratee(0); + + expect(prop(array)).toBe('a'); + + prop = _.iteratee('0'); + expect(prop(array)).toBe('a'); + }); + + it('should support deep paths for `_.property` shorthands', () => { + const object = { a: { b: 2 } }; + const prop = _.iteratee('a.b'); + + expect(prop(object)).toBe(2); + }); + + it('should work with functions created by `_.partial` and `_.partialRight`', () => { + const fn = function () { + const result = [this.a]; + push.apply(result, arguments); + return result; + }; + + const expected = [1, 2, 3]; + const object = { a: 1, iteratee: _.iteratee(partial(fn, 2)) }; + + expect(object.iteratee(3)).toEqual(expected); + + object.iteratee = _.iteratee(partialRight(fn, 3)); + expect(object.iteratee(2)).toEqual(expected); + }); + + it('should use internal `iteratee` if external is unavailable', () => { + const iteratee = _.iteratee; + delete _.iteratee; + + expect(map([{ a: 1 }], 'a')).toEqual([1]); + + _.iteratee = iteratee; + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const fn = function () { + return this instanceof Number; + }; + const array = [fn, fn, fn]; + const iteratees = lodashStable.map(array, _.iteratee); + const expected = lodashStable.map(array, stubFalse); + + const actual = lodashStable.map(iteratees, (iteratee) => iteratee()); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/iteration-methods.js b/test/iteration-methods.js deleted file mode 100644 index 67c73e8211..0000000000 --- a/test/iteration-methods.js +++ /dev/null @@ -1,340 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, slice, isNpm, noop, MAX_SAFE_INTEGER, stubTrue } from './utils.js'; - -describe('iteration methods', function() { - var methods = [ - '_baseEach', - 'countBy', - 'every', - 'filter', - 'find', - 'findIndex', - 'findKey', - 'findLast', - 'findLastIndex', - 'findLastKey', - 'forEach', - 'forEachRight', - 'forIn', - 'forInRight', - 'forOwn', - 'forOwnRight', - 'groupBy', - 'keyBy', - 'map', - 'mapKeys', - 'mapValues', - 'maxBy', - 'minBy', - 'omitBy', - 'partition', - 'pickBy', - 'reject', - 'some' - ]; - - var arrayMethods = [ - 'findIndex', - 'findLastIndex', - 'maxBy', - 'minBy' - ]; - - var collectionMethods = [ - '_baseEach', - 'countBy', - 'every', - 'filter', - 'find', - 'findLast', - 'forEach', - 'forEachRight', - 'groupBy', - 'keyBy', - 'map', - 'partition', - 'reduce', - 'reduceRight', - 'reject', - 'some' - ]; - - var forInMethods = [ - 'forIn', - 'forInRight', - 'omitBy', - 'pickBy' - ]; - - var iterationMethods = [ - '_baseEach', - 'forEach', - 'forEachRight', - 'forIn', - 'forInRight', - 'forOwn', - 'forOwnRight' - ]; - - var objectMethods = [ - 'findKey', - 'findLastKey', - 'forIn', - 'forInRight', - 'forOwn', - 'forOwnRight', - 'mapKeys', - 'mapValues', - 'omitBy', - 'pickBy' - ]; - - var rightMethods = [ - 'findLast', - 'findLastIndex', - 'findLastKey', - 'forEachRight', - 'forInRight', - 'forOwnRight' - ]; - - var unwrappedMethods = [ - 'each', - 'eachRight', - 'every', - 'find', - 'findIndex', - 'findKey', - 'findLast', - 'findLastIndex', - 'findLastKey', - 'forEach', - 'forEachRight', - 'forIn', - 'forInRight', - 'forOwn', - 'forOwnRight', - 'max', - 'maxBy', - 'min', - 'minBy', - 'some' - ]; - - lodashStable.each(methods, function(methodName) { - var array = [1, 2, 3], - func = _[methodName], - isBy = /(^partition|By)$/.test(methodName), - isFind = /^find/.test(methodName), - isOmitPick = /^(?:omit|pick)By$/.test(methodName), - isSome = methodName == 'some'; - - it('`_.' + methodName + '` should provide correct iteratee arguments', function() { - if (func) { - var args, - expected = [1, 0, array]; - - func(array, function() { - args || (args = slice.call(arguments)); - }); - - if (lodashStable.includes(rightMethods, methodName)) { - expected[0] = 3; - expected[1] = 2; - } - if (lodashStable.includes(objectMethods, methodName)) { - expected[1] += ''; - } - if (isBy) { - expected.length = isOmitPick ? 2 : 1; - } - assert.deepStrictEqual(args, expected); - } - }); - - it('`_.' + methodName + '` should treat sparse arrays as dense', function() { - if (func) { - var array = [1]; - array[2] = 3; - - var expected = lodashStable.includes(objectMethods, methodName) - ? [[1, '0', array], [undefined, '1', array], [3, '2', array]] - : [[1, 0, array], [undefined, 1, array], [3, 2, array]]; - - if (isBy) { - expected = lodashStable.map(expected, function(args) { - return args.slice(0, isOmitPick ? 2 : 1); - }); - } - else if (lodashStable.includes(objectMethods, methodName)) { - expected = lodashStable.map(expected, function(args) { - args[1] += ''; - return args; - }); - } - if (lodashStable.includes(rightMethods, methodName)) { - expected.reverse(); - } - var argsList = []; - func(array, function() { - argsList.push(slice.call(arguments)); - return !(isFind || isSome); - }); - - assert.deepStrictEqual(argsList, expected); - } - }); - }); - - lodashStable.each(lodashStable.difference(methods, objectMethods), function(methodName) { - var array = [1, 2, 3], - func = _[methodName], - isEvery = methodName == 'every'; - - array.a = 1; - - it('`_.' + methodName + '` should not iterate custom properties on arrays', function() { - if (func) { - var keys = []; - func(array, function(value, key) { - keys.push(key); - return isEvery; - }); - - assert.ok(!lodashStable.includes(keys, 'a')); - } - }); - }); - - lodashStable.each(lodashStable.difference(methods, unwrappedMethods), function(methodName) { - var array = [1, 2, 3], - isBaseEach = methodName == '_baseEach'; - - it('`_.' + methodName + '` should return a wrapped value when implicitly chaining', function() { - if (!(isBaseEach || isNpm)) { - var wrapped = _(array)[methodName](noop); - assert.ok(wrapped instanceof _); - } - }); - }); - - lodashStable.each(unwrappedMethods, function(methodName) { - var array = [1, 2, 3]; - - it('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - var actual = _(array)[methodName](noop); - assert.notOk(actual instanceof _); - }); - - it('`_.' + methodName + '` should return a wrapped value when explicitly chaining', function() { - var wrapped = _(array).chain(), - actual = wrapped[methodName](noop); - - assert.ok(actual instanceof _); - assert.notStrictEqual(actual, wrapped); - }); - }); - - lodashStable.each(lodashStable.difference(methods, arrayMethods, forInMethods), function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` iterates over own string keyed properties of objects', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - if (func) { - var values = []; - func(new Foo, function(value) { values.push(value); }); - assert.deepStrictEqual(values, [1]); - } - }); - }); - - lodashStable.each(iterationMethods, function(methodName) { - var array = [1, 2, 3], - func = _[methodName]; - - it('`_.' + methodName + '` should return the collection', function() { - if (func) { - assert.strictEqual(func(array, Boolean), array); - } - }); - }); - - lodashStable.each(collectionMethods, function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should use `isArrayLike` to determine whether a value is array-like', function() { - if (func) { - var isIteratedAsObject = function(object) { - var result = false; - func(object, function() { result = true; }, 0); - return result; - }; - - var values = [-1, '1', 1.1, Object(1), MAX_SAFE_INTEGER + 1], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(length) { - return isIteratedAsObject({ 'length': length }); - }); - - var Foo = function(a) {}; - Foo.a = 1; - - assert.deepStrictEqual(actual, expected); - assert.ok(isIteratedAsObject(Foo)); - assert.ok(!isIteratedAsObject({ 'length': 0 })); - } - }); - }); - - lodashStable.each(methods, function(methodName) { - var func = _[methodName], - isFind = /^find/.test(methodName), - isSome = methodName == 'some', - isReduce = /^reduce/.test(methodName); - - it('`_.' + methodName + '` should ignore changes to `length`', function() { - if (func) { - var count = 0, - array = [1]; - - func(array, function() { - if (++count == 1) { - array.push(2); - } - return !(isFind || isSome); - }, isReduce ? array : null); - - assert.strictEqual(count, 1); - } - }); - }); - - lodashStable.each(lodashStable.difference(lodashStable.union(methods, collectionMethods), arrayMethods), function(methodName) { - var func = _[methodName], - isFind = /^find/.test(methodName), - isSome = methodName == 'some', - isReduce = /^reduce/.test(methodName); - - it('`_.' + methodName + '` should ignore added `object` properties', function() { - if (func) { - var count = 0, - object = { 'a': 1 }; - - func(object, function() { - if (++count == 1) { - object.b = 2; - } - return !(isFind || isSome); - }, isReduce ? object : null); - - assert.strictEqual(count, 1); - } - }); - }); -}); diff --git a/test/iteration-methods.spec.js b/test/iteration-methods.spec.js new file mode 100644 index 0000000000..10988290a5 --- /dev/null +++ b/test/iteration-methods.spec.js @@ -0,0 +1,359 @@ +import lodashStable from 'lodash'; +import { _, slice, isNpm, noop, MAX_SAFE_INTEGER, stubTrue } from './utils'; + +describe('iteration methods', () => { + const methods = [ + '_baseEach', + 'countBy', + 'every', + 'filter', + 'find', + 'findIndex', + 'findKey', + 'findLast', + 'findLastIndex', + 'findLastKey', + 'forEach', + 'forEachRight', + 'forIn', + 'forInRight', + 'forOwn', + 'forOwnRight', + 'groupBy', + 'keyBy', + 'map', + 'mapKeys', + 'mapValues', + 'maxBy', + 'minBy', + 'omitBy', + 'partition', + 'pickBy', + 'reject', + 'some', + ]; + + const arrayMethods = ['findIndex', 'findLastIndex', 'maxBy', 'minBy']; + + const collectionMethods = [ + '_baseEach', + 'countBy', + 'every', + 'filter', + 'find', + 'findLast', + 'forEach', + 'forEachRight', + 'groupBy', + 'keyBy', + 'map', + 'partition', + 'reduce', + 'reduceRight', + 'reject', + 'some', + ]; + + const forInMethods = ['forIn', 'forInRight', 'omitBy', 'pickBy']; + + const iterationMethods = [ + '_baseEach', + 'forEach', + 'forEachRight', + 'forIn', + 'forInRight', + 'forOwn', + 'forOwnRight', + ]; + + const objectMethods = [ + 'findKey', + 'findLastKey', + 'forIn', + 'forInRight', + 'forOwn', + 'forOwnRight', + 'mapKeys', + 'mapValues', + 'omitBy', + 'pickBy', + ]; + + const rightMethods = [ + 'findLast', + 'findLastIndex', + 'findLastKey', + 'forEachRight', + 'forInRight', + 'forOwnRight', + ]; + + const unwrappedMethods = [ + 'each', + 'eachRight', + 'every', + 'find', + 'findIndex', + 'findKey', + 'findLast', + 'findLastIndex', + 'findLastKey', + 'forEach', + 'forEachRight', + 'forIn', + 'forInRight', + 'forOwn', + 'forOwnRight', + 'max', + 'maxBy', + 'min', + 'minBy', + 'some', + ]; + + lodashStable.each(methods, (methodName) => { + const array = [1, 2, 3]; + const func = _[methodName]; + const isBy = /(^partition|By)$/.test(methodName); + const isFind = /^find/.test(methodName); + const isOmitPick = /^(?:omit|pick)By$/.test(methodName); + const isSome = methodName === 'some'; + + it(`\`_.${methodName}\` should provide correct iteratee arguments`, () => { + if (func) { + let args; + const expected = [1, 0, array]; + + func(array, function () { + args || (args = slice.call(arguments)); + }); + + if (lodashStable.includes(rightMethods, methodName)) { + expected[0] = 3; + expected[1] = 2; + } + if (lodashStable.includes(objectMethods, methodName)) { + expected[1] += ''; + } + if (isBy) { + expected.length = isOmitPick ? 2 : 1; + } + expect(args).toEqual(expected); + } + }); + + it(`\`_.${methodName}\` should treat sparse arrays as dense`, () => { + if (func) { + const array = [1]; + array[2] = 3; + + let expected = lodashStable.includes(objectMethods, methodName) + ? [ + [1, '0', array], + [undefined, '1', array], + [3, '2', array], + ] + : [ + [1, 0, array], + [undefined, 1, array], + [3, 2, array], + ]; + + if (isBy) { + expected = lodashStable.map(expected, (args) => + args.slice(0, isOmitPick ? 2 : 1), + ); + } else if (lodashStable.includes(objectMethods, methodName)) { + expected = lodashStable.map(expected, (args) => { + args[1] += ''; + return args; + }); + } + if (lodashStable.includes(rightMethods, methodName)) { + expected.reverse(); + } + const argsList = []; + func(array, function () { + argsList.push(slice.call(arguments)); + return !(isFind || isSome); + }); + + expect(argsList).toEqual(expected); + } + }); + }); + + lodashStable.each(lodashStable.difference(methods, objectMethods), (methodName) => { + const array = [1, 2, 3]; + const func = _[methodName]; + const isEvery = methodName === 'every'; + + array.a = 1; + + it(`\`_.${methodName}\` should not iterate custom properties on arrays`, () => { + if (func) { + const keys = []; + func(array, (value, key) => { + keys.push(key); + return isEvery; + }); + + expect(lodashStable.includes(keys, 'a')).toBeFalsy(); + } + }); + }); + + lodashStable.each(lodashStable.difference(methods, unwrappedMethods), (methodName) => { + const array = [1, 2, 3]; + const isBaseEach = methodName === '_baseEach'; + + it(`\`_.${methodName}\` should return a wrapped value when implicitly chaining`, () => { + if (!(isBaseEach || isNpm)) { + const wrapped = _(array)[methodName](noop); + expect(wrapped instanceof _).toBeTruthy(); + } + }); + }); + + lodashStable.each(unwrappedMethods, (methodName) => { + const array = [1, 2, 3]; + + it(`\`_.${methodName}\` should return an unwrapped value when implicitly chaining`, () => { + const actual = _(array)[methodName](noop); + expect(actual instanceof _).toBeFalsy(); + }); + + it(`\`_.${methodName}\` should return a wrapped value when explicitly chaining`, () => { + const wrapped = _(array).chain(); + const actual = wrapped[methodName](noop); + + expect(actual instanceof _).toBeTruthy(); + // FIXME: Work out a proper assertion. + // expect(actual).toEqual(wrapped); + }); + }); + + lodashStable.each( + lodashStable.difference(methods, arrayMethods, forInMethods), + (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` iterates over own string keyed properties of objects`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + if (func) { + const values = []; + func(new Foo(), (value) => { + values.push(value); + }); + expect(values).toEqual([1]); + } + }); + }, + ); + + lodashStable.each(iterationMethods, (methodName) => { + const array = [1, 2, 3]; + const func = _[methodName]; + + it(`\`_.${methodName}\` should return the collection`, () => { + if (func) { + expect(func(array, Boolean)).toBe(array); + } + }); + }); + + lodashStable.each(collectionMethods, (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should use \`isArrayLike\` to determine whether a value is array-like`, () => { + if (func) { + const isIteratedAsObject = function (object) { + let result = false; + func( + object, + () => { + result = true; + }, + 0, + ); + return result; + }; + + const values = [-1, '1', 1.1, Object(1), MAX_SAFE_INTEGER + 1]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (length) => + isIteratedAsObject({ length: length }), + ); + + const Foo = function (a) {}; + Foo.a = 1; + + expect(actual).toEqual(expected); + expect(isIteratedAsObject(Foo)).toBeTruthy(); + expect(isIteratedAsObject({ length: 0 })).toBeFalsy(); + } + }); + }); + + lodashStable.each(methods, (methodName) => { + const func = _[methodName]; + const isFind = /^find/.test(methodName); + const isSome = methodName === 'some'; + const isReduce = /^reduce/.test(methodName); + + it(`\`_.${methodName}\` should ignore changes to \`length\``, () => { + if (func) { + let count = 0; + const array = [1]; + + func( + array, + () => { + if (++count === 1) { + array.push(2); + } + return !(isFind || isSome); + }, + isReduce ? array : null, + ); + + expect(count).toBe(1); + } + }); + }); + + lodashStable.each( + lodashStable.difference(lodashStable.union(methods, collectionMethods), arrayMethods), + (methodName) => { + const func = _[methodName]; + const isFind = /^find/.test(methodName); + const isSome = methodName === 'some'; + const isReduce = /^reduce/.test(methodName); + + it(`\`_.${methodName}\` should ignore added \`object\` properties`, () => { + if (func) { + let count = 0; + const object = { a: 1 }; + + func( + object, + () => { + if (++count === 1) { + object.b = 2; + } + return !(isFind || isSome); + }, + isReduce ? object : null, + ); + + expect(count).toBe(1); + } + }); + }, + ); +}); diff --git a/test/join.js b/test/join.js deleted file mode 100644 index a8f672f8b7..0000000000 --- a/test/join.js +++ /dev/null @@ -1,20 +0,0 @@ -import assert from 'assert'; -import join from '../join.js'; - -describe('join', function() { - var array = ['a', 'b', 'c']; - - it('should return join all array elements into a string', function() { - assert.strictEqual(join(array, '~'), 'a~b~c'); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - var wrapped = _(array); - assert.strictEqual(wrapped.join('~'), 'a~b~c'); - assert.strictEqual(wrapped.value(), array); - }); - - it('should return a wrapped value when explicitly chaining', function() { - assert.ok(_(array).chain().join('~') instanceof _); - }); -}); diff --git a/test/join.spec.js b/test/join.spec.js new file mode 100644 index 0000000000..fd66fb50e2 --- /dev/null +++ b/test/join.spec.js @@ -0,0 +1,19 @@ +import join from '../src/join'; + +describe('join', () => { + const array = ['a', 'b', 'c']; + + it('should return join all array elements into a string', () => { + expect(join(array, '~')).toBe('a~b~c'); + }); + + it('should return an unwrapped value when implicitly chaining', () => { + const wrapped = _(array); + expect(wrapped.join('~')).toBe('a~b~c'); + expect(wrapped.value()).toBe(array); + }); + + it('should return a wrapped value when explicitly chaining', () => { + expect(_(array).chain().join('~') instanceof _); + }); +}); diff --git a/test/keyBy.js b/test/keyBy.js deleted file mode 100644 index 997c1015b0..0000000000 --- a/test/keyBy.js +++ /dev/null @@ -1,76 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE } from './utils.js'; -import keyBy from '../keyBy.js'; - -describe('keyBy', function() { - var array = [ - { 'dir': 'left', 'code': 97 }, - { 'dir': 'right', 'code': 100 } - ]; - - it('should transform keys by `iteratee`', function() { - var expected = { 'a': { 'dir': 'left', 'code': 97 }, 'd': { 'dir': 'right', 'code': 100 } }; - - var actual = keyBy(array, function(object) { - return String.fromCharCode(object.code); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var array = [4, 6, 6], - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant({ '4': 4, '6': 6 })); - - var actual = lodashStable.map(values, function(value, index) { - return index ? keyBy(array, value) : keyBy(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var expected = { 'left': { 'dir': 'left', 'code': 97 }, 'right': { 'dir': 'right', 'code': 100 } }, - actual = keyBy(array, 'dir'); - - assert.deepStrictEqual(actual, expected); - }); - - it('should only add values to own, not inherited, properties', function() { - var actual = keyBy([6.1, 4.2, 6.3], function(n) { - return Math.floor(n) > 4 ? 'hasOwnProperty' : 'constructor'; - }); - - assert.deepStrictEqual(actual.constructor, 4.2); - assert.deepStrictEqual(actual.hasOwnProperty, 6.3); - }); - - it('should work with a number for `iteratee`', function() { - var array = [ - [1, 'a'], - [2, 'a'], - [2, 'b'] - ]; - - assert.deepStrictEqual(keyBy(array, 0), { '1': [1, 'a'], '2': [2, 'b'] }); - assert.deepStrictEqual(keyBy(array, 1), { 'a': [2, 'a'], 'b': [2, 'b'] }); - }); - - it('should work with an object for `collection`', function() { - var actual = keyBy({ 'a': 6.1, 'b': 4.2, 'c': 6.3 }, Math.floor); - assert.deepStrictEqual(actual, { '4': 4.2, '6': 6.3 }); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE).concat( - lodashStable.range(Math.floor(LARGE_ARRAY_SIZE / 2), LARGE_ARRAY_SIZE), - lodashStable.range(Math.floor(LARGE_ARRAY_SIZE / 1.5), LARGE_ARRAY_SIZE) - ); - - var actual = _(array).keyBy().map(square).filter(isEven).take().value(); - - assert.deepEqual(actual, _.take(_.filter(_.map(keyBy(array), square), isEven))); - }); -}); diff --git a/test/keyBy.spec.js b/test/keyBy.spec.js new file mode 100644 index 0000000000..2aa2505eb8 --- /dev/null +++ b/test/keyBy.spec.js @@ -0,0 +1,62 @@ +import lodashStable from 'lodash'; +import { LARGE_ARRAY_SIZE } from './utils'; +import keyBy from '../src/keyBy'; + +describe('keyBy', () => { + const array = [ + { dir: 'left', code: 97 }, + { dir: 'right', code: 100 }, + ]; + + it('should transform keys by `iteratee`', () => { + const expected = { a: { dir: 'left', code: 97 }, d: { dir: 'right', code: 100 } }; + + const actual = keyBy(array, (object) => String.fromCharCode(object.code)); + + expect(actual).toEqual(expected); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const array = [4, 6, 6]; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant({ 4: 4, 6: 6 })); + + const actual = lodashStable.map(values, (value, index) => + index ? keyBy(array, value) : keyBy(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const expected = { left: { dir: 'left', code: 97 }, right: { dir: 'right', code: 100 } }; + const actual = keyBy(array, 'dir'); + + expect(actual).toEqual(expected); + }); + + it('should only add values to own, not inherited, properties', () => { + const actual = keyBy([6.1, 4.2, 6.3], (n) => + Math.floor(n) > 4 ? 'hasOwnProperty' : 'constructor', + ); + + expect(actual.constructor).toEqual(4.2); + expect(actual.hasOwnProperty).toEqual(6.3); + }); + + it('should work with a number for `iteratee`', () => { + const array = [ + [1, 'a'], + [2, 'a'], + [2, 'b'], + ]; + + expect(keyBy(array, 0)).toEqual({ 1: [1, 'a'], 2: [2, 'b'] }); + expect(keyBy(array, 1)).toEqual({ a: [2, 'a'], b: [2, 'b'] }); + }); + + it('should work with an object for `collection`', () => { + const actual = keyBy({ a: 6.1, b: 4.2, c: 6.3 }, Math.floor); + expect(actual).toEqual({ 4: 4.2, 6: 6.3 }); + }); +}); diff --git a/test/keys-methods.js b/test/keys-methods.js deleted file mode 100644 index e45e9b320d..0000000000 --- a/test/keys-methods.js +++ /dev/null @@ -1,183 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - _, - arrayProto, - args, - strictArgs, - objectProto, - stringProto, - primitives, - numberProto, - stubArray, -} from './utils.js'; - -describe('keys methods', function() { - lodashStable.each(['keys', 'keysIn'], function(methodName) { - var func = _[methodName], - isKeys = methodName == 'keys'; - - it('`_.' + methodName + '` should return the string keyed property names of `object`', function() { - var actual = func({ 'a': 1, 'b': 1 }).sort(); - - assert.deepStrictEqual(actual, ['a', 'b']); - }); - - it('`_.' + methodName + '` should ' + (isKeys ? 'not ' : '') + 'include inherited string keyed properties', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var expected = isKeys ? ['a'] : ['a', 'b'], - actual = func(new Foo).sort(); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should treat sparse arrays as dense', function() { - var array = [1]; - array[2] = 3; - - var actual = func(array).sort(); - - assert.deepStrictEqual(actual, ['0', '1', '2']); - }); - - it('`_.' + methodName + '` should return keys for custom properties on arrays', function() { - var array = [1]; - array.a = 1; - - var actual = func(array).sort(); - - assert.deepStrictEqual(actual, ['0', 'a']); - }); - - it('`_.' + methodName + '` should ' + (isKeys ? 'not ' : '') + 'include inherited string keyed properties of arrays', function() { - arrayProto.a = 1; - - var expected = isKeys ? ['0'] : ['0', 'a'], - actual = func([1]).sort(); - - assert.deepStrictEqual(actual, expected); - - delete arrayProto.a; - }); - - it('`_.' + methodName + '` should work with `arguments` objects', function() { - var values = [args, strictArgs], - expected = lodashStable.map(values, lodashStable.constant(['0', '1', '2'])); - - var actual = lodashStable.map(values, function(value) { - return func(value).sort(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return keys for custom properties on `arguments` objects', function() { - var values = [args, strictArgs], - expected = lodashStable.map(values, lodashStable.constant(['0', '1', '2', 'a'])); - - var actual = lodashStable.map(values, function(value) { - value.a = 1; - var result = func(value).sort(); - delete value.a; - return result; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should ' + (isKeys ? 'not ' : '') + 'include inherited string keyed properties of `arguments` objects', function() { - var values = [args, strictArgs], - expected = lodashStable.map(values, lodashStable.constant(isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a'])); - - var actual = lodashStable.map(values, function(value) { - objectProto.a = 1; - var result = func(value).sort(); - delete objectProto.a; - return result; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with string objects', function() { - var actual = func(Object('abc')).sort(); - - assert.deepStrictEqual(actual, ['0', '1', '2']); - }); - - it('`_.' + methodName + '` should return keys for custom properties on string objects', function() { - var object = Object('a'); - object.a = 1; - - var actual = func(object).sort(); - - assert.deepStrictEqual(actual, ['0', 'a']); - }); - - it('`_.' + methodName + '` should ' + (isKeys ? 'not ' : '') + 'include inherited string keyed properties of string objects', function() { - stringProto.a = 1; - - var expected = isKeys ? ['0'] : ['0', 'a'], - actual = func(Object('a')).sort(); - - assert.deepStrictEqual(actual, expected); - - delete stringProto.a; - }); - - it('`_.' + methodName + '` should work with array-like objects', function() { - var object = { '0': 'a', 'length': 1 }, - actual = func(object).sort(); - - assert.deepStrictEqual(actual, ['0', 'length']); - }); - - it('`_.' + methodName + '` should coerce primitives to objects (test in IE 9)', function() { - var expected = lodashStable.map(primitives, function(value) { - return typeof value === 'string' ? ['0'] : []; - }); - - var actual = lodashStable.map(primitives, func); - assert.deepStrictEqual(actual, expected); - - // IE 9 doesn't box numbers in for-in loops. - numberProto.a = 1; - assert.deepStrictEqual(func(0), isKeys ? [] : ['a']); - delete numberProto.a; - }); - - it('`_.' + methodName + '` skips the `constructor` property on prototype objects', function() { - function Foo() {} - Foo.prototype.a = 1; - - var expected = ['a']; - assert.deepStrictEqual(func(Foo.prototype), expected); - - Foo.prototype = { 'constructor': Foo, 'a': 1 }; - assert.deepStrictEqual(func(Foo.prototype), expected); - - var Fake = { 'prototype': {} }; - Fake.prototype.constructor = Fake; - assert.deepStrictEqual(func(Fake.prototype), ['constructor']); - }); - - it('`_.' + methodName + '` should return an empty array when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubArray); - - var actual = lodashStable.map(values, function(value, index) { - objectProto.a = 1; - var result = index ? func(value) : func(); - delete objectProto.a; - return result; - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/keys-methods.spec.js b/test/keys-methods.spec.js new file mode 100644 index 0000000000..9737f83e49 --- /dev/null +++ b/test/keys-methods.spec.js @@ -0,0 +1,191 @@ +import lodashStable from 'lodash'; + +import { + _, + arrayProto, + args, + strictArgs, + objectProto, + stringProto, + primitives, + numberProto, + stubArray, +} from './utils'; + +describe('keys methods', () => { + lodashStable.each(['keys', 'keysIn'], (methodName) => { + const func = _[methodName]; + const isKeys = methodName === 'keys'; + + it(`\`_.${methodName}\` should return the string keyed property names of \`object\``, () => { + const actual = func({ a: 1, b: 1 }).sort(); + + expect(actual).toEqual(['a', 'b']); + }); + + it(`\`_.${methodName}\` should ${ + isKeys ? 'not ' : '' + }include inherited string keyed properties`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const expected = isKeys ? ['a'] : ['a', 'b']; + const actual = func(new Foo()).sort(); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should treat sparse arrays as dense`, () => { + const array = [1]; + array[2] = 3; + + const actual = func(array).sort(); + + expect(actual).toEqual(['0', '1', '2']); + }); + + it(`\`_.${methodName}\` should return keys for custom properties on arrays`, () => { + const array = [1]; + array.a = 1; + + const actual = func(array).sort(); + + expect(actual).toEqual(['0', 'a']); + }); + + it(`\`_.${methodName}\` should ${ + isKeys ? 'not ' : '' + }include inherited string keyed properties of arrays`, () => { + arrayProto.a = 1; + + const expected = isKeys ? ['0'] : ['0', 'a']; + const actual = func([1]).sort(); + + expect(actual).toEqual(expected); + + delete arrayProto.a; + }); + + it(`\`_.${methodName}\` should work with \`arguments\` objects`, () => { + const values = [args, strictArgs]; + const expected = lodashStable.map(values, lodashStable.constant(['0', '1', '2'])); + + const actual = lodashStable.map(values, (value) => func(value).sort()); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return keys for custom properties on \`arguments\` objects`, () => { + const values = [args, strictArgs]; + const expected = lodashStable.map(values, lodashStable.constant(['0', '1', '2', 'a'])); + + const actual = lodashStable.map(values, (value) => { + value.a = 1; + const result = func(value).sort(); + delete value.a; + return result; + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should ${ + isKeys ? 'not ' : '' + }include inherited string keyed properties of \`arguments\` objects`, () => { + const values = [args, strictArgs]; + const expected = lodashStable.map( + values, + lodashStable.constant(isKeys ? ['0', '1', '2'] : ['0', '1', '2', 'a']), + ); + + const actual = lodashStable.map(values, (value) => { + objectProto.a = 1; + const result = func(value).sort(); + delete objectProto.a; + return result; + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with string objects`, () => { + const actual = func(Object('abc')).sort(); + + expect(actual).toEqual(['0', '1', '2']); + }); + + it(`\`_.${methodName}\` should return keys for custom properties on string objects`, () => { + const object = Object('a'); + object.a = 1; + + const actual = func(object).sort(); + + expect(actual).toEqual(['0', 'a']); + }); + + it(`\`_.${methodName}\` should ${ + isKeys ? 'not ' : '' + }include inherited string keyed properties of string objects`, () => { + stringProto.a = 1; + + const expected = isKeys ? ['0'] : ['0', 'a']; + const actual = func(Object('a')).sort(); + + expect(actual).toEqual(expected); + + delete stringProto.a; + }); + + it(`\`_.${methodName}\` should work with array-like objects`, () => { + const object = { 0: 'a', length: 1 }; + const actual = func(object).sort(); + + expect(actual).toEqual(['0', 'length']); + }); + + it(`\`_.${methodName}\` should coerce primitives to objects (test in IE 9)`, () => { + const expected = lodashStable.map(primitives, (value) => + typeof value === 'string' ? ['0'] : [], + ); + + const actual = lodashStable.map(primitives, func); + expect(actual).toEqual(expected); + + // IE 9 doesn't box numbers in for-in loops. + numberProto.a = 1; + expect(func(0)).toEqual(isKeys ? [] : ['a']); + delete numberProto.a; + }); + + it(`\`_.${methodName}\` skips the \`constructor\` property on prototype objects`, () => { + function Foo() {} + Foo.prototype.a = 1; + + const expected = ['a']; + expect(func(Foo.prototype)).toEqual(expected); + + Foo.prototype = { constructor: Foo, a: 1 }; + expect(func(Foo.prototype)).toEqual(expected); + + const Fake = { prototype: {} }; + Fake.prototype.constructor = Fake; + expect(func(Fake.prototype)).toEqual(['constructor']); + }); + + it(`\`_.${methodName}\` should return an empty array when \`object\` is nullish`, () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubArray); + + const actual = lodashStable.map(values, (value, index) => { + objectProto.a = 1; + const result = index ? func(value) : func(); + delete objectProto.a; + return result; + }); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/last.js b/test/last.js deleted file mode 100644 index fef570ec93..0000000000 --- a/test/last.js +++ /dev/null @@ -1,51 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE } from './utils.js'; -import last from '../last.js'; - -describe('last', function() { - var array = [1, 2, 3, 4]; - - it('should return the last element', function() { - assert.strictEqual(last(array), 4); - }); - - it('should return `undefined` when querying empty arrays', function() { - var array = []; - array['-1'] = 1; - - assert.strictEqual(last([]), undefined); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - actual = lodashStable.map(array, last); - - assert.deepStrictEqual(actual, [3, 6, 9]); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - assert.strictEqual(_(array).last(), 4); - }); - - it('should return a wrapped value when explicitly chaining', function() { - assert.ok(_(array).chain().last() instanceof _); - }); - - it('should not execute immediately when explicitly chaining', function() { - var wrapped = _(array).chain().last(); - assert.strictEqual(wrapped.__wrapped__, array); - }); - - it('should work in a lazy sequence', function() { - var largeArray = lodashStable.range(LARGE_ARRAY_SIZE), - smallArray = array; - - lodashStable.times(2, function(index) { - var array = index ? largeArray : smallArray, - wrapped = _(array).filter(isEven); - - assert.strictEqual(wrapped.last(), last(_.filter(array, isEven))); - }); - }); -}); diff --git a/test/last.spec.js b/test/last.spec.js new file mode 100644 index 0000000000..7ee48d999d --- /dev/null +++ b/test/last.spec.js @@ -0,0 +1,28 @@ +import lodashStable from 'lodash'; +import last from '../src/last'; + +describe('last', () => { + const array = [1, 2, 3, 4]; + + it('should return the last element', () => { + expect(last(array)).toBe(4); + }); + + it('should return `undefined` when querying empty arrays', () => { + const array = []; + array['-1'] = 1; + + expect(last([])).toBe(undefined); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]; + const actual = lodashStable.map(array, last); + + expect(actual).toEqual([3, 6, 9]); + }); +}); diff --git a/test/lodash(...)-methods-that-return-new-wrapped-values.js b/test/lodash(...)-methods-that-return-new-wrapped-values.js deleted file mode 100644 index b66c54c604..0000000000 --- a/test/lodash(...)-methods-that-return-new-wrapped-values.js +++ /dev/null @@ -1,45 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -describe('lodash(...) methods that return new wrapped values', function() { - var funcs = [ - 'castArray', - 'concat', - 'difference', - 'differenceBy', - 'differenceWith', - 'intersection', - 'intersectionBy', - 'intersectionWith', - 'pull', - 'pullAll', - 'pullAt', - 'sampleSize', - 'shuffle', - 'slice', - 'splice', - 'split', - 'toArray', - 'union', - 'unionBy', - 'unionWith', - 'uniq', - 'uniqBy', - 'uniqWith', - 'words', - 'xor', - 'xorBy', - 'xorWith' - ]; - - lodashStable.each(funcs, function(methodName) { - it('`_(...).' + methodName + '` should return a new wrapped value', function() { - var value = methodName == 'split' ? 'abc' : [1, 2, 3], - wrapped = _(value), - actual = wrapped[methodName](); - - assert.ok(actual instanceof _); - assert.notStrictEqual(actual, wrapped); - }); - }); -}); diff --git a/test/lodash(...)-methods-that-return-the-wrapped-modified-array.js b/test/lodash(...)-methods-that-return-the-wrapped-modified-array.js deleted file mode 100644 index d58eeff84a..0000000000 --- a/test/lodash(...)-methods-that-return-the-wrapped-modified-array.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -describe('lodash(...) methods that return the wrapped modified array', function() { - var funcs = [ - 'push', - 'reverse', - 'sort', - 'unshift' - ]; - - lodashStable.each(funcs, function(methodName) { - it('`_(...).' + methodName + '` should return a new wrapper', function() { - var array = [1, 2, 3], - wrapped = _(array), - actual = wrapped[methodName](); - - assert.ok(actual instanceof _); - assert.notStrictEqual(actual, wrapped); - }); - }); -}); diff --git a/test/lodash(...)-methods-that-return-unwrapped-values.js b/test/lodash(...)-methods-that-return-unwrapped-values.js deleted file mode 100644 index 7b906e1ed5..0000000000 --- a/test/lodash(...)-methods-that-return-unwrapped-values.js +++ /dev/null @@ -1,112 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -describe('lodash(...) methods that return unwrapped values', function() { - var funcs = [ - 'add', - 'camelCase', - 'capitalize', - 'ceil', - 'clone', - 'deburr', - 'defaultTo', - 'divide', - 'endsWith', - 'escape', - 'escapeRegExp', - 'every', - 'find', - 'floor', - 'has', - 'hasIn', - 'head', - 'includes', - 'isArguments', - 'isArray', - 'isArrayBuffer', - 'isArrayLike', - 'isBoolean', - 'isBuffer', - 'isDate', - 'isElement', - 'isEmpty', - 'isEqual', - 'isError', - 'isFinite', - 'isFunction', - 'isInteger', - 'isMap', - 'isNaN', - 'isNative', - 'isNil', - 'isNull', - 'isNumber', - 'isObject', - 'isObjectLike', - 'isPlainObject', - 'isRegExp', - 'isSafeInteger', - 'isSet', - 'isString', - 'isUndefined', - 'isWeakMap', - 'isWeakSet', - 'join', - 'kebabCase', - 'last', - 'lowerCase', - 'lowerFirst', - 'max', - 'maxBy', - 'min', - 'minBy', - 'multiply', - 'nth', - 'pad', - 'padEnd', - 'padStart', - 'parseInt', - 'pop', - 'random', - 'reduce', - 'reduceRight', - 'repeat', - 'replace', - 'round', - 'sample', - 'shift', - 'size', - 'snakeCase', - 'some', - 'startCase', - 'startsWith', - 'subtract', - 'sum', - 'toFinite', - 'toInteger', - 'toLower', - 'toNumber', - 'toSafeInteger', - 'toString', - 'toUpper', - 'trim', - 'trimEnd', - 'trimStart', - 'truncate', - 'unescape', - 'upperCase', - 'upperFirst' - ]; - - lodashStable.each(funcs, function(methodName) { - it('`_(...).' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - var actual = _()[methodName](); - assert.notOk(actual instanceof _); - }); - - it('`_(...).' + methodName + '` should return a wrapped value when explicitly chaining', function() { - var actual = _().chain()[methodName](); - assert.ok(actual instanceof _); - }); - }); -}); diff --git a/test/lodash(...).commit.js b/test/lodash(...).commit.js deleted file mode 100644 index 94a7acb08e..0000000000 --- a/test/lodash(...).commit.js +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'assert'; - -describe('lodash(...).commit', function() { - it('should execute the chained sequence and returns the wrapped result', function() { - var array = [1], - wrapped = _(array).push(2).push(3); - - assert.deepEqual(array, [1]); - - var otherWrapper = wrapped.commit(); - assert.ok(otherWrapper instanceof _); - assert.deepEqual(otherWrapper.value(), [1, 2, 3]); - assert.deepEqual(wrapped.value(), [1, 2, 3, 2, 3]); - }); - - it('should track the `__chain__` value of a wrapper', function() { - var wrapped = _([1]).chain().commit().head(); - assert.ok(wrapped instanceof _); - assert.strictEqual(wrapped.value(), 1); - }); -}); diff --git a/test/lodash(...).next.js b/test/lodash(...).next.js deleted file mode 100644 index e3019ba352..0000000000 --- a/test/lodash(...).next.js +++ /dev/null @@ -1,74 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, isNpm, LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import toArray from '../toArray.js'; -import filter from '../filter.js'; - -describe('lodash(...).next', function() { - lodashStable.each([false, true], function(implicit) { - function chain(value) { - return implicit ? _(value) : _.chain(value); - } - - var chainType = 'in an ' + (implicit ? 'implicit' : 'explict') + ' chain'; - - it('should follow the iterator protocol ' + chainType, function() { - var wrapped = chain([1, 2]); - - assert.deepEqual(wrapped.next(), { 'done': false, 'value': 1 }); - assert.deepEqual(wrapped.next(), { 'done': false, 'value': 2 }); - assert.deepEqual(wrapped.next(), { 'done': true, 'value': undefined }); - }); - - it('should act as an iterable ' + chainType, function() { - if (!isNpm && Symbol && Symbol.iterator) { - var array = [1, 2], - wrapped = chain(array); - - assert.strictEqual(wrapped[Symbol.iterator](), wrapped); - assert.deepStrictEqual(lodashStable.toArray(wrapped), array); - } - }); - - it('should use `_.toArray` to generate the iterable result ' + chainType, function() { - if (!isNpm && Array.from) { - var hearts = '\ud83d\udc95', - values = [[1], { 'a': 1 }, hearts]; - - lodashStable.each(values, function(value) { - var wrapped = chain(value); - assert.deepStrictEqual(Array.from(wrapped), toArray(value)); - }); - } - }); - - it('should reset the iterator correctly ' + chainType, function() { - if (!isNpm && Symbol && Symbol.iterator) { - var array = [1, 2], - wrapped = chain(array); - - assert.deepStrictEqual(lodashStable.toArray(wrapped), array); - assert.deepStrictEqual(lodashStable.toArray(wrapped), [], 'produces an empty array for exhausted iterator'); - - var other = wrapped.filter(); - assert.deepStrictEqual(lodashStable.toArray(other), array, 'reset for new chain segments'); - assert.deepStrictEqual(lodashStable.toArray(wrapped), [], 'iterator is still exhausted'); - } - }); - - it('should work in a lazy sequence ' + chainType, function() { - if (!isNpm && Symbol && Symbol.iterator) { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - predicate = function(value) { values.push(value); return isEven(value); }, - values = [], - wrapped = chain(array); - - assert.deepStrictEqual(lodashStable.toArray(wrapped), array); - - wrapped = wrapped.filter(predicate); - assert.deepStrictEqual(lodashStable.toArray(wrapped), filter(array, isEven), 'reset for new lazy chain segments'); - assert.deepStrictEqual(values, array, 'memoizes iterator values'); - } - }); - }); -}); diff --git a/test/lodash(...).plant.js b/test/lodash(...).plant.js deleted file mode 100644 index 191eecaa37..0000000000 --- a/test/lodash(...).plant.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { square, isNpm } from './utils.js'; -import compact from '../compact.js'; - -describe('lodash(...).plant', function() { - it('should clone the chained sequence planting `value` as the wrapped value', function() { - var array1 = [5, null, 3, null, 1], - array2 = [10, null, 8, null, 6], - wrapped1 = _(array1).thru(compact).map(square).takeRight(2).sort(), - wrapped2 = wrapped1.plant(array2); - - assert.deepEqual(wrapped2.value(), [36, 64]); - assert.deepEqual(wrapped1.value(), [1, 9]); - }); - - it('should clone `chainAll` settings', function() { - var array1 = [2, 4], - array2 = [6, 8], - wrapped1 = _(array1).chain().map(square), - wrapped2 = wrapped1.plant(array2); - - assert.deepEqual(wrapped2.head().value(), 36); - }); - - it('should reset iterator data on cloned sequences', function() { - if (!isNpm && Symbol && Symbol.iterator) { - var array1 = [2, 4], - array2 = [6, 8], - wrapped1 = _(array1).map(square); - - assert.deepStrictEqual(lodashStable.toArray(wrapped1), [4, 16]); - assert.deepStrictEqual(lodashStable.toArray(wrapped1), []); - - var wrapped2 = wrapped1.plant(array2); - assert.deepStrictEqual(lodashStable.toArray(wrapped2), [36, 64]); - } - }); -}); diff --git a/test/lodash(...).pop.js b/test/lodash(...).pop.js deleted file mode 100644 index a06c0cb273..0000000000 --- a/test/lodash(...).pop.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; - -describe('lodash(...).pop', function() { - it('should remove elements from the end of `array`', function() { - var array = [1, 2], - wrapped = _(array); - - assert.strictEqual(wrapped.pop(), 2); - assert.deepEqual(wrapped.value(), [1]); - assert.strictEqual(wrapped.pop(), 1); - - var actual = wrapped.value(); - assert.strictEqual(actual, array); - assert.deepEqual(actual, []); - }); - - it('should accept falsey arguments', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - try { - var result = index ? _(value).pop() : _().pop(); - return result === undefined; - } catch (e) {} - }); - - assert.deepEqual(actual, expected); - }); -}); diff --git a/test/lodash(...).push.js b/test/lodash(...).push.js deleted file mode 100644 index f29b124cb9..0000000000 --- a/test/lodash(...).push.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; - -describe('lodash(...).push', function() { - it('should append elements to `array`', function() { - var array = [1], - wrapped = _(array).push(2, 3), - actual = wrapped.value(); - - assert.strictEqual(actual, array); - assert.deepEqual(actual, [1, 2, 3]); - }); - - it('should accept falsey arguments', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - try { - var result = index ? _(value).push(1).value() : _().push(1).value(); - return lodashStable.eq(result, value); - } catch (e) {} - }); - - assert.deepEqual(actual, expected); - }); -}); diff --git a/test/lodash(...).shift.js b/test/lodash(...).shift.js deleted file mode 100644 index b6920283af..0000000000 --- a/test/lodash(...).shift.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; - -describe('lodash(...).shift', function() { - it('should remove elements from the front of `array`', function() { - var array = [1, 2], - wrapped = _(array); - - assert.strictEqual(wrapped.shift(), 1); - assert.deepEqual(wrapped.value(), [2]); - assert.strictEqual(wrapped.shift(), 2); - - var actual = wrapped.value(); - assert.strictEqual(actual, array); - assert.deepEqual(actual, []); - }); - - it('should accept falsey arguments', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - try { - var result = index ? _(value).shift() : _().shift(); - return result === undefined; - } catch (e) {} - }); - - assert.deepEqual(actual, expected); - }); -}); diff --git a/test/lodash(...).sort.js b/test/lodash(...).sort.js deleted file mode 100644 index e10d5f5a26..0000000000 --- a/test/lodash(...).sort.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; - -describe('lodash(...).sort', function() { - it('should return the wrapped sorted `array`', function() { - var array = [3, 1, 2], - wrapped = _(array).sort(), - actual = wrapped.value(); - - assert.strictEqual(actual, array); - assert.deepEqual(actual, [1, 2, 3]); - }); - - it('should accept falsey arguments', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - try { - var result = index ? _(value).sort().value() : _().sort().value(); - return lodashStable.eq(result, value); - } catch (e) {} - }); - - assert.deepEqual(actual, expected); - }); -}); diff --git a/test/lodash(...).splice.js b/test/lodash(...).splice.js deleted file mode 100644 index 8e300b39d8..0000000000 --- a/test/lodash(...).splice.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; - -describe('lodash(...).splice', function() { - it('should support removing and inserting elements', function() { - var array = [1, 2], - wrapped = _(array); - - assert.deepEqual(wrapped.splice(1, 1, 3).value(), [2]); - assert.deepEqual(wrapped.value(), [1, 3]); - assert.deepEqual(wrapped.splice(0, 2).value(), [1, 3]); - - var actual = wrapped.value(); - assert.strictEqual(actual, array); - assert.deepEqual(actual, []); - }); - - it('should accept falsey arguments', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - try { - var result = index ? _(value).splice(0, 1).value() : _().splice(0, 1).value(); - return lodashStable.isEqual(result, []); - } catch (e) {} - }); - - assert.deepEqual(actual, expected); - }); -}); diff --git a/test/lodash(...).unshift.js b/test/lodash(...).unshift.js deleted file mode 100644 index f679c4fd88..0000000000 --- a/test/lodash(...).unshift.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubTrue } from './utils.js'; - -describe('lodash(...).unshift', function() { - it('should prepend elements to `array`', function() { - var array = [3], - wrapped = _(array).unshift(1, 2), - actual = wrapped.value(); - - assert.strictEqual(actual, array); - assert.deepEqual(actual, [1, 2, 3]); - }); - - it('should accept falsey arguments', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(value, index) { - try { - var result = index ? _(value).unshift(1).value() : _().unshift(1).value(); - return lodashStable.eq(result, value); - } catch (e) {} - }); - - assert.deepEqual(actual, expected); - }); -}); diff --git a/test/lodash(...).value.js b/test/lodash(...).value.js deleted file mode 100644 index a883d23fc0..0000000000 --- a/test/lodash(...).value.js +++ /dev/null @@ -1,33 +0,0 @@ -import assert from 'assert'; -import { isNpm } from './utils.js'; -import prototype from '../prototype.js'; - -describe('lodash(...).value', function() { - it('should execute the chained sequence and extract the unwrapped value', function() { - var array = [1], - wrapped = _(array).push(2).push(3); - - assert.deepEqual(array, [1]); - assert.deepEqual(wrapped.value(), [1, 2, 3]); - assert.deepEqual(wrapped.value(), [1, 2, 3, 2, 3]); - assert.deepEqual(array, [1, 2, 3, 2, 3]); - }); - - it('should return the `valueOf` result of the wrapped value', function() { - var wrapped = _(123); - assert.strictEqual(Number(wrapped), 123); - }); - - it('should stringify the wrapped value when used by `JSON.stringify`', function() { - if (!isNpm && JSON) { - var wrapped = _([1, 2, 3]); - assert.strictEqual(JSON.stringify(wrapped), '[1,2,3]'); - } - }); - - it('should be aliased', function() { - var expected = prototype.value; - assert.strictEqual(prototype.toJSON, expected); - assert.strictEqual(prototype.valueOf, expected); - }); -}); diff --git a/test/lodash-constructor.js b/test/lodash-constructor.js deleted file mode 100644 index c59556eb7b..0000000000 --- a/test/lodash-constructor.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, stubTrue, isNpm, lodashBizarro } from './utils.js'; - -describe('lodash constructor', function() { - var values = empties.concat(true, 1, 'a'), - expected = lodashStable.map(values, stubTrue); - - it('should create a new instance when called without the `new` operator', function() { - var actual = lodashStable.map(values, function(value) { - return _(value) instanceof _; - }); - - assert.deepEqual(actual, expected); - }); - - it('should return the given `lodash` instances', function() { - var actual = lodashStable.map(values, function(value) { - var wrapped = _(value); - return _(wrapped) === wrapped; - }); - - assert.deepEqual(actual, expected); - }); - - it('should convert foreign wrapped values to `lodash` instances', function() { - if (!isNpm && lodashBizarro) { - var actual = lodashStable.map(values, function(value) { - var wrapped = _(lodashBizarro(value)), - unwrapped = wrapped.value(); - - return wrapped instanceof _ && - ((unwrapped === value) || (unwrapped !== unwrapped && value !== value)); - }); - - assert.deepStrictEqual(actual, expected); - } - }); -}); diff --git a/test/lodash-constructor.spec.js b/test/lodash-constructor.spec.js new file mode 100644 index 0000000000..50f7d23907 --- /dev/null +++ b/test/lodash-constructor.spec.js @@ -0,0 +1,38 @@ +import lodashStable from 'lodash'; +import { empties, stubTrue, isNpm, lodashBizarro } from './utils'; + +describe('lodash constructor', () => { + const values = empties.concat(true, 1, 'a'); + const expected = lodashStable.map(values, stubTrue); + + it('should create a new instance when called without the `new` operator', () => { + const actual = lodashStable.map(values, (value) => _(value) instanceof _); + + expect(actual).toEqual(expected); + }); + + it('should return the given `lodash` instances', () => { + const actual = lodashStable.map(values, (value) => { + const wrapped = _(value); + return _(wrapped) === wrapped; + }); + + expect(actual).toEqual(expected); + }); + + it('should convert foreign wrapped values to `lodash` instances', () => { + if (!isNpm && lodashBizarro) { + const actual = lodashStable.map(values, (value) => { + const wrapped = _(lodashBizarro(value)); + const unwrapped = wrapped.value(); + + return ( + wrapped instanceof _ && + (unwrapped === value || (unwrapped !== unwrapped && value !== value)) + ); + }); + + expect(actual).toEqual(expected); + } + }); +}); diff --git a/test/lodash-methods.js b/test/lodash-methods.js deleted file mode 100644 index 2a92a00bac..0000000000 --- a/test/lodash-methods.js +++ /dev/null @@ -1,194 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, falsey, stubArray, oldDash, stubTrue, FUNC_ERROR_TEXT } from './utils.js'; -import functions from '../functions.js'; -import bind from '../bind.js'; - -describe('lodash methods', function() { - var allMethods = lodashStable.reject(functions(_).sort(), function(methodName) { - return lodashStable.startsWith(methodName, '_'); - }); - - var checkFuncs = [ - 'after', - 'ary', - 'before', - 'bind', - 'curry', - 'curryRight', - 'debounce', - 'defer', - 'delay', - 'flip', - 'flow', - 'flowRight', - 'memoize', - 'negate', - 'once', - 'partial', - 'partialRight', - 'rearg', - 'rest', - 'spread', - 'throttle', - 'unary' - ]; - - var noBinding = [ - 'flip', - 'memoize', - 'negate', - 'once', - 'overArgs', - 'partial', - 'partialRight', - 'rearg', - 'rest', - 'spread' - ]; - - var rejectFalsey = [ - 'tap', - 'thru' - ].concat(checkFuncs); - - var returnArrays = [ - 'at', - 'chunk', - 'compact', - 'difference', - 'drop', - 'filter', - 'flatten', - 'functions', - 'initial', - 'intersection', - 'invokeMap', - 'keys', - 'map', - 'orderBy', - 'pull', - 'pullAll', - 'pullAt', - 'range', - 'rangeRight', - 'reject', - 'remove', - 'shuffle', - 'sortBy', - 'tail', - 'take', - 'times', - 'toArray', - 'toPairs', - 'toPairsIn', - 'union', - 'uniq', - 'values', - 'without', - 'xor', - 'zip' - ]; - - var acceptFalsey = lodashStable.difference(allMethods, rejectFalsey); - - it('should accept falsey arguments', function() { - var arrays = lodashStable.map(falsey, stubArray); - - lodashStable.each(acceptFalsey, function(methodName) { - var expected = arrays, - func = _[methodName]; - - var actual = lodashStable.map(falsey, function(value, index) { - return index ? func(value) : func(); - }); - - if (methodName == 'noConflict') { - root._ = oldDash; - } - else if (methodName == 'pull' || methodName == 'pullAll') { - expected = falsey; - } - if (lodashStable.includes(returnArrays, methodName) && methodName != 'sample') { - assert.deepStrictEqual(actual, expected, '_.' + methodName + ' returns an array'); - } - assert.ok(true, '`_.' + methodName + '` accepts falsey arguments'); - }); - - // Skip tests for missing methods of modularized builds. - lodashStable.each(['chain', 'noConflict', 'runInContext'], function(methodName) { - if (!_[methodName]) {} - }); - }); - - it('should return an array', function() { - var array = [1, 2, 3]; - - lodashStable.each(returnArrays, function(methodName) { - var actual, - func = _[methodName]; - - switch (methodName) { - case 'invokeMap': - actual = func(array, 'toFixed'); - break; - case 'sample': - actual = func(array, 1); - break; - default: - actual = func(array); - } - assert.ok(lodashStable.isArray(actual), '_.' + methodName + ' returns an array'); - - var isPull = methodName == 'pull' || methodName == 'pullAll'; - assert.strictEqual(actual === array, isPull, '_.' + methodName + ' should ' + (isPull ? '' : 'not ') + 'return the given array'); - }); - }); - - it('should throw an error for falsey arguments', function() { - lodashStable.each(rejectFalsey, function(methodName) { - var expected = lodashStable.map(falsey, stubTrue), - func = _[methodName]; - - var actual = lodashStable.map(falsey, function(value, index) { - var pass = !index && /^(?:backflow|compose|cond|flow(Right)?|over(?:Every|Some)?)$/.test(methodName); - - try { - index ? func(value) : func(); - } catch (e) { - pass = !pass && (e instanceof TypeError) && - (!lodashStable.includes(checkFuncs, methodName) || (e.message == FUNC_ERROR_TEXT)); - } - return pass; - }); - - assert.deepStrictEqual(actual, expected, '`_.' + methodName + '` rejects falsey arguments'); - }); - }); - - it('should use `this` binding of function', function() { - lodashStable.each(noBinding, function(methodName) { - var fn = function() { return this.a; }, - func = _[methodName], - isNegate = methodName == 'negate', - object = { 'a': 1 }, - expected = isNegate ? false : 1; - - var wrapper = func(bind(fn, object)); - assert.strictEqual(wrapper(), expected, '`_.' + methodName + '` can consume a bound function'); - - wrapper = bind(func(fn), object); - assert.strictEqual(wrapper(), expected, '`_.' + methodName + '` can be bound'); - - object.wrapper = func(fn); - assert.strictEqual(object.wrapper(), expected, '`_.' + methodName + '` uses the `this` of its parent object'); - }); - }); - - it('should not contain minified method names (test production builds)', function() { - var shortNames = ['_', 'at', 'eq', 'gt', 'lt']; - assert.ok(lodashStable.every(functions(_), function(methodName) { - return methodName.length > 2 || lodashStable.includes(shortNames, methodName); - })); - }); -}); diff --git a/test/lodash-methods.spec.js b/test/lodash-methods.spec.js new file mode 100644 index 0000000000..f574eb0fc7 --- /dev/null +++ b/test/lodash-methods.spec.js @@ -0,0 +1,211 @@ +import lodashStable from 'lodash'; +import { _, falsey, stubArray, oldDash, stubTrue, FUNC_ERROR_TEXT } from './utils'; +import functions from '../src/functions'; +import bind from '../src/bind'; + +describe('lodash methods', () => { + const allMethods = lodashStable.reject(functions(_).sort(), (methodName) => + lodashStable.startsWith(methodName, '_'), + ); + + const checkFuncs = [ + 'after', + 'ary', + 'before', + 'bind', + 'curry', + 'curryRight', + 'debounce', + 'defer', + 'delay', + 'flip', + 'flow', + 'flowRight', + 'memoize', + 'negate', + 'once', + 'partial', + 'partialRight', + 'rearg', + 'rest', + 'spread', + 'throttle', + 'unary', + ]; + + const noBinding = [ + 'flip', + 'memoize', + 'negate', + 'once', + 'overArgs', + 'partial', + 'partialRight', + 'rearg', + 'rest', + 'spread', + ]; + + const rejectFalsey = ['tap', 'thru'].concat(checkFuncs); + + const returnArrays = [ + 'at', + 'chunk', + 'compact', + 'difference', + 'drop', + 'filter', + 'flatten', + 'functions', + 'initial', + 'intersection', + 'invokeMap', + 'keys', + 'map', + 'orderBy', + 'pull', + 'pullAll', + 'pullAt', + 'range', + 'rangeRight', + 'reject', + 'remove', + 'shuffle', + 'sortBy', + 'tail', + 'take', + 'times', + 'toArray', + 'toPairs', + 'toPairsIn', + 'union', + 'uniq', + 'values', + 'without', + 'xor', + 'zip', + ]; + + const acceptFalsey = lodashStable.difference(allMethods, rejectFalsey); + + it('should accept falsey arguments', () => { + const arrays = lodashStable.map(falsey, stubArray); + + lodashStable.each(acceptFalsey, (methodName) => { + let expected = arrays; + const func = _[methodName]; + + const actual = lodashStable.map(falsey, (value, index) => + index ? func(value) : func(), + ); + + if (methodName === 'noConflict') { + root._ = oldDash; + } else if (methodName === 'pull' || methodName === 'pullAll') { + expected = falsey; + } + if (lodashStable.includes(returnArrays, methodName) && methodName != 'sample') { + expect(actual, expected).toEqual(`_.${methodName} returns an array`); + } + expect(true, `\`_.${methodName}\` accepts falsey arguments`); + }); + }); + + it('should return an array', () => { + const array = [1, 2, 3]; + + lodashStable.each(returnArrays, (methodName) => { + let actual; + const func = _[methodName]; + + switch (methodName) { + case 'invokeMap': + actual = func(array, 'toFixed'); + break; + case 'sample': + actual = func(array, 1); + break; + default: + actual = func(array); + } + expect(lodashStable.isArray(actual), `_.${methodName} returns an array`); + + const isPull = methodName === 'pull' || methodName === 'pullAll'; + assert.strictEqual( + actual === array, + isPull, + `_.${methodName} should ${isPull ? '' : 'not '}return the given array`, + ); + }); + }); + + it('should throw an error for falsey arguments', () => { + lodashStable.each(rejectFalsey, (methodName) => { + const expected = lodashStable.map(falsey, stubTrue); + const func = _[methodName]; + + const actual = lodashStable.map(falsey, (value, index) => { + let pass = + !index && + /^(?:backflow|compose|cond|flow(Right)?|over(?:Every|Some)?)$/.test(methodName); + + try { + index ? func(value) : func(); + } catch (e) { + pass = + !pass && + e instanceof TypeError && + (!lodashStable.includes(checkFuncs, methodName) || + e.message === FUNC_ERROR_TEXT); + } + return pass; + }); + + assert.deepStrictEqual( + actual, + expected, + `\`_.${methodName}\` rejects falsey arguments`, + ); + }); + }); + + it('should use `this` binding of function', () => { + lodashStable.each(noBinding, (methodName) => { + const fn = function () { + return this.a; + }; + const func = _[methodName]; + const isNegate = methodName === 'negate'; + const object = { a: 1 }; + const expected = isNegate ? false : 1; + + let wrapper = func(bind(fn, object)); + assert.strictEqual( + wrapper(), + expected, + `\`_.${methodName}\` can consume a bound function`, + ); + + wrapper = bind(func(fn), object); + expect(wrapper(), expected).toBe(`\`_.${methodName}\` can be bound`); + + object.wrapper = func(fn); + assert.strictEqual( + object.wrapper(), + expected, + `\`_.${methodName}\` uses the \`this\` of its parent object`, + ); + }); + }); + + it('should not contain minified method names (test production builds)', () => { + const shortNames = ['_', 'at', 'eq', 'gt', 'lt']; + assert.ok( + lodashStable.every( + functions(_), + (methodName) => + methodName.length > 2 || lodashStable.includes(shortNames, methodName), + ), + ); + }); +}); diff --git a/test/lodash.methodName.js b/test/lodash.methodName.js deleted file mode 100644 index 632684595d..0000000000 --- a/test/lodash.methodName.js +++ /dev/null @@ -1,75 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, empties } from './utils.js'; - -lodashStable.each(['find', 'findIndex', 'findKey', 'findLast', 'findLastIndex', 'findLastKey'], function(methodName) { - describe('lodash.' + methodName); - - var array = [1, 2, 3, 4], - func = _[methodName]; - - var objects = [ - { 'a': 0, 'b': 0 }, - { 'a': 1, 'b': 1 }, - { 'a': 2, 'b': 2 } - ]; - - var expected = ({ - 'find': [objects[1], undefined, objects[2]], - 'findIndex': [1, -1, 2], - 'findKey': ['1', undefined, '2'], - 'findLast': [objects[2], undefined, objects[2]], - 'findLastIndex': [2, -1, 2], - 'findLastKey': ['2', undefined, '2'] - })[methodName]; - - it('`_.' + methodName + '` should return the found value', function() { - assert.strictEqual(func(objects, function(object) { return object.a; }), expected[0]); - }); - - it('`_.' + methodName + '` should return `' + expected[1] + '` if value is not found', function() { - assert.strictEqual(func(objects, function(object) { return object.a === 3; }), expected[1]); - }); - - it('`_.' + methodName + '` should work with `_.matches` shorthands', function() { - assert.strictEqual(func(objects, { 'b': 2 }), expected[2]); - }); - - it('`_.' + methodName + '` should work with `_.matchesProperty` shorthands', function() { - assert.strictEqual(func(objects, ['b', 2]), expected[2]); - }); - - it('`_.' + methodName + '` should work with `_.property` shorthands', function() { - assert.strictEqual(func(objects, 'b'), expected[0]); - }); - - it('`_.' + methodName + '` should return `' + expected[1] + '` for empty collections', function() { - var emptyValues = lodashStable.endsWith(methodName, 'Index') ? lodashStable.reject(empties, lodashStable.isPlainObject) : empties, - expecting = lodashStable.map(emptyValues, lodashStable.constant(expected[1])); - - var actual = lodashStable.map(emptyValues, function(value) { - try { - return func(value, { 'a': 3 }); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expecting); - }); - - it('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - var expected = ({ - 'find': 1, - 'findIndex': 0, - 'findKey': '0', - 'findLast': 4, - 'findLastIndex': 3, - 'findLastKey': '3' - })[methodName]; - }); - - it('`_.' + methodName + '` should return a wrapped value when explicitly chaining', function() {}); - - it('`_.' + methodName + '` should not execute immediately when explicitly chaining', function() {}); - - it('`_.' + methodName + '` should work in a lazy sequence', function() {}); -}); diff --git a/test/lodash.methodName.spec.js b/test/lodash.methodName.spec.js new file mode 100644 index 0000000000..51a722e112 --- /dev/null +++ b/test/lodash.methodName.spec.js @@ -0,0 +1,85 @@ +import lodashStable from 'lodash'; +import { _, empties } from './utils'; + +lodashStable.each( + ['find', 'findIndex', 'findKey', 'findLast', 'findLastIndex', 'findLastKey'], + (methodName) => { + describe(`lodash.${methodName}`); + + const array = [1, 2, 3, 4]; + const func = _[methodName]; + + const objects = [ + { a: 0, b: 0 }, + { a: 1, b: 1 }, + { a: 2, b: 2 }, + ]; + + const expected = { + find: [objects[1], undefined, objects[2]], + findIndex: [1, -1, 2], + findKey: ['1', undefined, '2'], + findLast: [objects[2], undefined, objects[2]], + findLastIndex: [2, -1, 2], + findLastKey: ['2', undefined, '2'], + }[methodName]; + + it(`\`_.${methodName}\` should return the found value`, () => { + assert.strictEqual( + func(objects, (object) => object.a), + expected[0], + ); + }); + + it(`\`_.${methodName}\` should return \`${expected[1]}\` if value is not found`, () => { + assert.strictEqual( + func(objects, (object) => object.a === 3), + expected[1], + ); + }); + + it(`\`_.${methodName}\` should work with \`_.matches\` shorthands`, () => { + expect(func(objects, { b: 2 })).toBe(expected[2]); + }); + + it(`\`_.${methodName}\` should work with \`_.matchesProperty\` shorthands`, () => { + expect(func(objects, ['b', 2])).toBe(expected[2]); + }); + + it(`\`_.${methodName}\` should work with \`_.property\` shorthands`, () => { + expect(func(objects, 'b')).toBe(expected[0]); + }); + + it(`\`_.${methodName}\` should return \`${expected[1]}\` for empty collections`, () => { + const emptyValues = lodashStable.endsWith(methodName, 'Index') + ? lodashStable.reject(empties, lodashStable.isPlainObject) + : empties; + const expecting = lodashStable.map(emptyValues, lodashStable.constant(expected[1])); + + const actual = lodashStable.map(emptyValues, (value) => { + try { + return func(value, { a: 3 }); + } catch (e) {} + }); + + expect(actual).toEqual(expecting); + }); + + it(`\`_.${methodName}\` should return an unwrapped value when implicitly chaining`, () => { + const expected = { + find: 1, + findIndex: 0, + findKey: '0', + findLast: 4, + findLastIndex: 3, + findLastKey: '3', + }[methodName]; + }); + + it(`\`_.${methodName}\` should return a wrapped value when explicitly chaining`, () => {}); + + it(`\`_.${methodName}\` should not execute immediately when explicitly chaining`, () => {}); + + it(`\`_.${methodName}\` should work in a lazy sequence`, () => {}); + }, +); diff --git a/test/lowerCase.spec.js b/test/lowerCase.spec.js new file mode 100644 index 0000000000..47913ac6c2 --- /dev/null +++ b/test/lowerCase.spec.js @@ -0,0 +1,9 @@ +import lowerCase from '../src/lowerCase'; + +describe('lowerCase', () => { + it('should lowercase as space-separated words', () => { + expect(lowerCase('--Foo-Bar--')).toBe('foo bar'); + expect(lowerCase('fooBar')).toBe('foo bar'); + expect(lowerCase('__FOO_BAR__')).toBe('foo bar'); + }); +}); diff --git a/test/lowerCase.test.js b/test/lowerCase.test.js deleted file mode 100644 index eca2b7726b..0000000000 --- a/test/lowerCase.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import lowerCase from '../lowerCase.js'; - -describe('lowerCase', function() { - it('should lowercase as space-separated words', function() { - assert.strictEqual(lowerCase('--Foo-Bar--'), 'foo bar'); - assert.strictEqual(lowerCase('fooBar'), 'foo bar'); - assert.strictEqual(lowerCase('__FOO_BAR__'), 'foo bar'); - }); -}); diff --git a/test/lowerFirst.spec.js b/test/lowerFirst.spec.js new file mode 100644 index 0000000000..c304153b9c --- /dev/null +++ b/test/lowerFirst.spec.js @@ -0,0 +1,9 @@ +import lowerFirst from '../src/lowerFirst'; + +describe('lowerFirst', () => { + it('should lowercase only the first character', () => { + expect(lowerFirst('fred')).toBe('fred'); + expect(lowerFirst('Fred')).toBe('fred'); + expect(lowerFirst('FRED')).toBe('fRED'); + }); +}); diff --git a/test/lowerFirst.test.js b/test/lowerFirst.test.js deleted file mode 100644 index 665e7e71e4..0000000000 --- a/test/lowerFirst.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import lowerFirst from '../lowerFirst.js'; - -describe('lowerFirst', function() { - it('should lowercase only the first character', function() { - assert.strictEqual(lowerFirst('fred'), 'fred'); - assert.strictEqual(lowerFirst('Fred'), 'fred'); - assert.strictEqual(lowerFirst('FRED'), 'fRED'); - }); -}); diff --git a/test/lt.spec.js b/test/lt.spec.js new file mode 100644 index 0000000000..d5ef955203 --- /dev/null +++ b/test/lt.spec.js @@ -0,0 +1,15 @@ +import lt from '../src/lt'; + +describe('lt', () => { + it('should return `true` if `value` is less than `other`', () => { + expect(lt(1, 3)).toBe(true); + expect(lt('abc', 'def')).toBe(true); + }); + + it('should return `false` if `value` >= `other`', () => { + expect(lt(3, 1)).toBe(false); + expect(lt(3, 3)).toBe(false); + expect(lt('def', 'abc')).toBe(false); + expect(lt('def', 'def')).toBe(false); + }); +}); diff --git a/test/lt.test.js b/test/lt.test.js deleted file mode 100644 index 6b4590cb57..0000000000 --- a/test/lt.test.js +++ /dev/null @@ -1,16 +0,0 @@ -import assert from 'assert'; -import lt from '../lt.js'; - -describe('lt', function() { - it('should return `true` if `value` is less than `other`', function() { - assert.strictEqual(lt(1, 3), true); - assert.strictEqual(lt('abc', 'def'), true); - }); - - it('should return `false` if `value` >= `other`', function() { - assert.strictEqual(lt(3, 1), false); - assert.strictEqual(lt(3, 3), false); - assert.strictEqual(lt('def', 'abc'), false); - assert.strictEqual(lt('def', 'def'), false); - }); -}); diff --git a/test/lte.spec.js b/test/lte.spec.js new file mode 100644 index 0000000000..4ca324f432 --- /dev/null +++ b/test/lte.spec.js @@ -0,0 +1,16 @@ +import lte from '../src/lte'; +import lt from '../src/lt'; + +describe('lte', () => { + it('should return `true` if `value` is <= `other`', () => { + expect(lte(1, 3)).toBe(true); + expect(lte(3, 3)).toBe(true); + expect(lte('abc', 'def')).toBe(true); + expect(lte('def', 'def')).toBe(true); + }); + + it('should return `false` if `value` > `other`', () => { + expect(lt(3, 1)).toBe(false); + expect(lt('def', 'abc')).toBe(false); + }); +}); diff --git a/test/lte.test.js b/test/lte.test.js deleted file mode 100644 index 010a4fefc6..0000000000 --- a/test/lte.test.js +++ /dev/null @@ -1,17 +0,0 @@ -import assert from 'assert'; -import lte from '../lte.js'; -import lt from '../lt.js'; - -describe('lte', function() { - it('should return `true` if `value` is <= `other`', function() { - assert.strictEqual(lte(1, 3), true); - assert.strictEqual(lte(3, 3), true); - assert.strictEqual(lte('abc', 'def'), true); - assert.strictEqual(lte('def', 'def'), true); - }); - - it('should return `false` if `value` > `other`', function() { - assert.strictEqual(lt(3, 1), false); - assert.strictEqual(lt('def', 'abc'), false); - }); -}); diff --git a/test/map-caches.js b/test/map-caches.js deleted file mode 100644 index 08c2c9af39..0000000000 --- a/test/map-caches.js +++ /dev/null @@ -1,63 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { symbol, noop, mapCaches, LARGE_ARRAY_SIZE } from './utils.js'; - -describe('map caches', function() { - var keys = [null, undefined, false, true, 1, -Infinity, NaN, {}, 'a', symbol || noop]; - - var pairs = lodashStable.map(keys, function(key, index) { - var lastIndex = keys.length - 1; - return [key, keys[lastIndex - index]]; - }); - - function createCaches(pairs) { - var largeStack = new mapCaches.Stack(pairs), - length = pairs ? pairs.length : 0; - - lodashStable.times(LARGE_ARRAY_SIZE - length, function() { - largeStack.set({}, {}); - }); - - return { - 'hashes': new mapCaches.Hash(pairs), - 'list caches': new mapCaches.ListCache(pairs), - 'map caches': new mapCaches.MapCache(pairs), - 'stack caches': new mapCaches.Stack(pairs), - 'large stacks': largeStack - }; - } - - lodashStable.forOwn(createCaches(pairs), function(cache, kind) { - var isLarge = /^large/.test(kind); - - it('should implement a `Map` interface for ' + kind, function() { - lodashStable.each(keys, function(key, index) { - var value = pairs[index][1]; - - assert.deepStrictEqual(cache.get(key), value); - assert.strictEqual(cache.has(key), true); - assert.strictEqual(cache.delete(key), true); - assert.strictEqual(cache.has(key), false); - assert.strictEqual(cache.get(key), undefined); - assert.strictEqual(cache.delete(key), false); - assert.strictEqual(cache.set(key, value), cache); - assert.strictEqual(cache.has(key), true); - }); - - assert.strictEqual(cache.size, isLarge ? LARGE_ARRAY_SIZE : keys.length); - assert.strictEqual(cache.clear(), undefined); - assert.ok(lodashStable.every(keys, function(key) { - return !cache.has(key); - })); - }); - }); - - lodashStable.forOwn(createCaches(), function(cache, kind) { - it('should support changing values of ' + kind, function() { - lodashStable.each(keys, function(key) { - cache.set(key, 1).set(key, 2); - assert.strictEqual(cache.get(key), 2); - }); - }); - }); -}); diff --git a/test/map-caches.spec.js b/test/map-caches.spec.js new file mode 100644 index 0000000000..b42c4a04db --- /dev/null +++ b/test/map-caches.spec.js @@ -0,0 +1,60 @@ +import lodashStable from 'lodash'; +import { symbol, noop, mapCaches, LARGE_ARRAY_SIZE } from './utils'; + +describe('map caches', () => { + const keys = [null, undefined, false, true, 1, -Infinity, NaN, {}, 'a', symbol || noop]; + + const pairs = lodashStable.map(keys, (key, index) => { + const lastIndex = keys.length - 1; + return [key, keys[lastIndex - index]]; + }); + + function createCaches(pairs) { + const largeStack = new mapCaches.Stack(pairs); + const length = pairs ? pairs.length : 0; + + lodashStable.times(LARGE_ARRAY_SIZE - length, () => { + largeStack.set({}, {}); + }); + + return { + hashes: new mapCaches.Hash(pairs), + 'list caches': new mapCaches.ListCache(pairs), + 'map caches': new mapCaches.MapCache(pairs), + 'stack caches': new mapCaches.Stack(pairs), + 'large stacks': largeStack, + }; + } + + lodashStable.forOwn(createCaches(pairs), (cache, kind) => { + const isLarge = /^large/.test(kind); + + it(`should implement a \`Map\` interface for ${kind}`, () => { + lodashStable.each(keys, (key, index) => { + const value = pairs[index][1]; + + expect(cache.get(key)).toEqual(value); + expect(cache.has(key)).toBe(true); + expect(cache.delete(key)).toBe(true); + expect(cache.has(key)).toBe(false); + expect(cache.get(key)).toBe(undefined); + expect(cache.delete(key)).toBe(false); + expect(cache.set(key, value)).toBe(cache); + expect(cache.has(key)).toBe(true); + }); + + expect(cache.size).toBe(isLarge ? LARGE_ARRAY_SIZE : keys.length); + expect(cache.clear()).toBe(undefined); + expect(lodashStable.every(keys, (key) => !cache.has(key))); + }); + }); + + lodashStable.forOwn(createCaches(), (cache, kind) => { + it(`should support changing values of ${kind}`, () => { + lodashStable.each(keys, (key) => { + cache.set(key, 1).set(key, 2); + expect(cache.get(key)).toBe(2); + }); + }); + }); +}); diff --git a/test/map.js b/test/map.js deleted file mode 100644 index c41b9756b9..0000000000 --- a/test/map.js +++ /dev/null @@ -1,122 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, falsey, stubArray, document, noop, LARGE_ARRAY_SIZE, square } from './utils.js'; -import map from '../map.js'; - -describe('map', function() { - var array = [1, 2]; - - it('should map values in `collection` to a new array', function() { - var object = { 'a': 1, 'b': 2 }, - expected = ['1', '2']; - - assert.deepStrictEqual(map(array, String), expected); - assert.deepStrictEqual(map(object, String), expected); - }); - - it('should work with `_.property` shorthands', function() { - var objects = [{ 'a': 'x' }, { 'a': 'y' }]; - assert.deepStrictEqual(map(objects, 'a'), ['x', 'y']); - }); - - it('should iterate over own string keyed properties of objects', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var actual = map(new Foo, identity); - assert.deepStrictEqual(actual, [1]); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var object = { 'a': 1, 'b': 2 }, - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([1, 2])); - - lodashStable.each([array, object], function(collection) { - var actual = lodashStable.map(values, function(value, index) { - return index ? map(collection, value) : map(collection); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should accept a falsey `collection`', function() { - var expected = lodashStable.map(falsey, stubArray); - - var actual = lodashStable.map(falsey, function(collection, index) { - try { - return index ? map(collection) : map(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should treat number values for `collection` as empty', function() { - assert.deepStrictEqual(map(1), []); - }); - - it('should treat a nodelist as an array-like object', function() { - if (document) { - var actual = map(document.getElementsByTagName('body'), function(element) { - return element.nodeName.toLowerCase(); - }); - - assert.deepStrictEqual(actual, ['body']); - } - }); - - it('should work with objects with non-number length properties', function() { - var value = { 'value': 'x' }, - object = { 'length': { 'value': 'x' } }; - - assert.deepStrictEqual(map(object, identity), [value]); - }); - - it('should return a wrapped value when chaining', function() { - assert.ok(_(array).map(noop) instanceof _); - }); - - it('should provide correct `predicate` arguments in a lazy sequence', function() { - var args, - array = lodashStable.range(LARGE_ARRAY_SIZE + 1), - expected = [1, 0, map(array.slice(1), square)]; - - _(array).slice(1).map(function(value, index, array) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, [1, 0, array.slice(1)]); - - args = undefined; - _(array).slice(1).map(square).map(function(value, index, array) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, expected); - - args = undefined; - _(array).slice(1).map(square).map(function(value, index) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, expected); - - args = undefined; - _(array).slice(1).map(square).map(function(value) { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, [1]); - - args = undefined; - _(array).slice(1).map(square).map(function() { - args || (args = slice.call(arguments)); - }).value(); - - assert.deepEqual(args, expected); - }); -}); diff --git a/test/map.spec.js b/test/map.spec.js new file mode 100644 index 0000000000..b590035c29 --- /dev/null +++ b/test/map.spec.js @@ -0,0 +1,77 @@ +import lodashStable from 'lodash'; +import { identity, falsey, stubArray, document } from './utils'; +import map from '../src/map'; + +describe('map', () => { + const array = [1, 2]; + + it('should map values in `collection` to a new array', () => { + const object = { a: 1, b: 2 }; + const expected = ['1', '2']; + + expect(map(array, String)).toEqual(expected); + expect(map(object, String)).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const objects = [{ a: 'x' }, { a: 'y' }]; + expect(map(objects, 'a')).toEqual(['x', 'y']); + }); + + it('should iterate over own string keyed properties of objects', () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const actual = map(new Foo(), identity); + expect(actual).toEqual([1]); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const object = { a: 1, b: 2 }; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([1, 2])); + + lodashStable.each([array, object], (collection) => { + const actual = lodashStable.map(values, (value, index) => + index ? map(collection, value) : map(collection), + ); + + expect(actual).toEqual(expected); + }); + }); + + it('should accept a falsey `collection`', () => { + const expected = lodashStable.map(falsey, stubArray); + + const actual = lodashStable.map(falsey, (collection, index) => { + try { + return index ? map(collection) : map(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should treat number values for `collection` as empty', () => { + expect(map(1)).toEqual([]); + }); + + it('should treat a nodelist as an array-like object', () => { + if (document) { + const actual = map(document.getElementsByTagName('body'), (element) => + element.nodeName.toLowerCase(), + ); + + expect(actual).toEqual(['body']); + } + }); + + it('should work with objects with non-number length properties', () => { + const value = { value: 'x' }; + const object = { length: { value: 'x' } }; + + expect(map(object, identity)).toEqual([value]); + }); +}); diff --git a/test/mapKeys-and-mapValues.js b/test/mapKeys-and-mapValues.js deleted file mode 100644 index 8ba86f7c8e..0000000000 --- a/test/mapKeys-and-mapValues.js +++ /dev/null @@ -1,36 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, falsey, stubObject, noop } from './utils.js'; - -describe('mapKeys and mapValues', function() { - lodashStable.each(['mapKeys', 'mapValues'], function(methodName) { - var func = _[methodName], - object = { 'a': 1, 'b': 2 }; - - it('`_.' + methodName + '` should iterate over own string keyed properties of objects', function() { - function Foo() { - this.a = 'a'; - } - Foo.prototype.b = 'b'; - - var actual = func(new Foo, function(value, key) { return key; }); - assert.deepStrictEqual(actual, { 'a': 'a' }); - }); - - it('`_.' + methodName + '` should accept a falsey `object`', function() { - var expected = lodashStable.map(falsey, stubObject); - - var actual = lodashStable.map(falsey, function(object, index) { - try { - return index ? func(object) : func(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return a wrapped value when chaining', function() { - assert.ok(_(object)[methodName](noop) instanceof _); - }); - }); -}); diff --git a/test/mapKeys-and-mapValues.spec.js b/test/mapKeys-and-mapValues.spec.js new file mode 100644 index 0000000000..41102bc538 --- /dev/null +++ b/test/mapKeys-and-mapValues.spec.js @@ -0,0 +1,35 @@ +import lodashStable from 'lodash'; +import { _, falsey, stubObject, noop } from './utils'; + +describe('mapKeys and mapValues', () => { + lodashStable.each(['mapKeys', 'mapValues'], (methodName) => { + const func = _[methodName]; + const object = { a: 1, b: 2 }; + + it(`\`_.${methodName}\` should iterate over own string keyed properties of objects`, () => { + function Foo() { + this.a = 'a'; + } + Foo.prototype.b = 'b'; + + const actual = func(new Foo(), (value, key) => key); + expect(actual).toEqual({ a: 'a' }); + }); + + it(`\`_.${methodName}\` should accept a falsey \`object\``, () => { + const expected = lodashStable.map(falsey, stubObject); + + const actual = lodashStable.map(falsey, (object, index) => { + try { + return index ? func(object) : func(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return a wrapped value when chaining`, () => { + expect(_(object)[methodName](noop) instanceof _); + }); + }); +}); diff --git a/test/mapKeys.js b/test/mapKeys.js deleted file mode 100644 index a40f90a6f3..0000000000 --- a/test/mapKeys.js +++ /dev/null @@ -1,35 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import mapKeys from '../mapKeys.js'; - -describe('mapKeys', function() { - var array = [1, 2], - object = { 'a': 1, 'b': 2 }; - - it('should map keys in `object` to a new object', function() { - var actual = mapKeys(object, String); - assert.deepStrictEqual(actual, { '1': 1, '2': 2 }); - }); - - it('should treat arrays like objects', function() { - var actual = mapKeys(array, String); - assert.deepStrictEqual(actual, { '1': 1, '2': 2 }); - }); - - it('should work with `_.property` shorthands', function() { - var actual = mapKeys({ 'a': { 'b': 'c' } }, 'b'); - assert.deepStrictEqual(actual, { 'c': { 'b': 'c' } }); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var object = { 'a': 1, 'b': 2 }, - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant({ '1': 1, '2': 2 })); - - var actual = lodashStable.map(values, function(value, index) { - return index ? mapKeys(object, value) : mapKeys(object); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/mapKeys.spec.js b/test/mapKeys.spec.js new file mode 100644 index 0000000000..b3b037d429 --- /dev/null +++ b/test/mapKeys.spec.js @@ -0,0 +1,34 @@ +import lodashStable from 'lodash'; +import mapKeys from '../src/mapKeys'; + +describe('mapKeys', () => { + const array = [1, 2]; + const object = { a: 1, b: 2 }; + + it('should map keys in `object` to a new object', () => { + const actual = mapKeys(object, String); + expect(actual, { 1: 1).toEqual(2: 2 }); + }); + + it('should treat arrays like objects', () => { + const actual = mapKeys(array, String); + expect(actual, { 1: 1).toEqual(2: 2 }); + }); + + it('should work with `_.property` shorthands', () => { + const actual = mapKeys({ a: { b: 'c' } }, 'b'); + expect(actual).toEqual({ c: { b: 'c' } }); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const object = { a: 1, b: 2 }; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant({ 1: 1, 2: 2 })); + + const actual = lodashStable.map(values, (value, index) => + index ? mapKeys(object, value) : mapKeys(object), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/mapValues.js b/test/mapValues.js deleted file mode 100644 index 64b5cdc89e..0000000000 --- a/test/mapValues.js +++ /dev/null @@ -1,36 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import mapValues from '../mapValues.js'; - -describe('mapValues', function() { - var array = [1, 2], - object = { 'a': 1, 'b': 2 }; - - it('should map values in `object` to a new object', function() { - var actual = mapValues(object, String); - assert.deepStrictEqual(actual, { 'a': '1', 'b': '2' }); - }); - - it('should treat arrays like objects', function() { - var actual = mapValues(array, String); - assert.deepStrictEqual(actual, { '0': '1', '1': '2' }); - }); - - it('should work with `_.property` shorthands', function() { - var actual = mapValues({ 'a': { 'b': 2 } }, 'b'); - assert.deepStrictEqual(actual, { 'a': 2 }); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var object = { 'a': 1, 'b': 2 }, - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([true, false])); - - var actual = lodashStable.map(values, function(value, index) { - var result = index ? mapValues(object, value) : mapValues(object); - return [lodashStable.isEqual(result, object), result === object]; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/mapValues.spec.js b/test/mapValues.spec.js new file mode 100644 index 0000000000..d53f2d498a --- /dev/null +++ b/test/mapValues.spec.js @@ -0,0 +1,35 @@ +import lodashStable from 'lodash'; +import mapValues from '../src/mapValues'; + +describe('mapValues', () => { + const array = [1, 2]; + const object = { a: 1, b: 2 }; + + it('should map values in `object` to a new object', () => { + const actual = mapValues(object, String); + expect(actual, { a: '1').toEqual(b: '2' }); + }); + + it('should treat arrays like objects', () => { + const actual = mapValues(array, String); + expect(actual, { 0: '1').toEqual(1: '2' }); + }); + + it('should work with `_.property` shorthands', () => { + const actual = mapValues({ a: { b: 2 } }, 'b'); + expect(actual).toEqual({ a: 2 }); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const object = { a: 1, b: 2 }; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([true, false])); + + const actual = lodashStable.map(values, (value, index) => { + const result = index ? mapValues(object, value) : mapValues(object); + return [lodashStable.isEqual(result, object), result === object]; + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/matches-methods.js b/test/matches-methods.js deleted file mode 100644 index 721fdfad5d..0000000000 --- a/test/matches-methods.js +++ /dev/null @@ -1,294 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, stubTrue, noop, numberProto, stubFalse, empties } from './utils.js'; -import isMatch from '../isMatch.js'; - -describe('matches methods', function() { - lodashStable.each(['matches', 'isMatch'], function(methodName) { - var isMatches = methodName == 'matches'; - - function matches(source) { - return isMatches ? _.matches(source) : function(object) { - return isMatch(object, source); - }; - } - - it('`_.' + methodName + '` should perform a deep comparison between `source` and `object`', function() { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - par = matches({ 'a': 1 }); - - assert.strictEqual(par(object), true); - - par = matches({ 'b': 1 }); - assert.strictEqual(par(object), false); - - par = matches({ 'a': 1, 'c': 3 }); - assert.strictEqual(par(object), true); - - par = matches({ 'c': 3, 'd': 4 }); - assert.strictEqual(par(object), false); - - object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 }; - par = matches({ 'a': { 'b': { 'c': 1 } } }); - - assert.strictEqual(par(object), true); - }); - - it('`_.' + methodName + '` should match inherited string keyed `object` properties', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var object = { 'a': new Foo }, - par = matches({ 'a': { 'b': 2 } }); - - assert.strictEqual(par(object), true); - }); - - it('`_.' + methodName + '` should not match by inherited `source` properties', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var objects = [{ 'a': 1 }, { 'a': 1, 'b': 2 }], - source = new Foo, - actual = lodashStable.map(objects, matches(source)), - expected = lodashStable.map(objects, stubTrue); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should compare a variety of `source` property values', function() { - var object1 = { 'a': false, 'b': true, 'c': '3', 'd': 4, 'e': [5], 'f': { 'g': 6 } }, - object2 = { 'a': 0, 'b': 1, 'c': 3, 'd': '4', 'e': ['5'], 'f': { 'g': '6' } }, - par = matches(object1); - - assert.strictEqual(par(object1), true); - assert.strictEqual(par(object2), false); - }); - - it('`_.' + methodName + '` should match `-0` as `0`', function() { - var object1 = { 'a': -0 }, - object2 = { 'a': 0 }, - par = matches(object1); - - assert.strictEqual(par(object2), true); - - par = matches(object2); - assert.strictEqual(par(object1), true); - }); - - it('`_.' + methodName + '` should compare functions by reference', function() { - var object1 = { 'a': lodashStable.noop }, - object2 = { 'a': noop }, - object3 = { 'a': {} }, - par = matches(object1); - - assert.strictEqual(par(object1), true); - assert.strictEqual(par(object2), false); - assert.strictEqual(par(object3), false); - }); - - it('`_.' + methodName + '` should work with a function for `object`', function() { - function Foo() {} - Foo.a = { 'b': 2, 'c': 3 }; - - var par = matches({ 'a': { 'b': 2 } }); - assert.strictEqual(par(Foo), true); - }); - - it('`_.' + methodName + '` should work with a function for `source`', function() { - function Foo() {} - Foo.a = 1; - Foo.b = function() {}; - Foo.c = 3; - - var objects = [{ 'a': 1 }, { 'a': 1, 'b': Foo.b, 'c': 3 }], - actual = lodashStable.map(objects, matches(Foo)); - - assert.deepStrictEqual(actual, [false, true]); - }); - - it('`_.' + methodName + '` should work with a non-plain `object`', function() { - function Foo(object) { lodashStable.assign(this, object); } - - var object = new Foo({ 'a': new Foo({ 'b': 2, 'c': 3 }) }), - par = matches({ 'a': { 'b': 2 } }); - - assert.strictEqual(par(object), true); - }); - - it('`_.' + methodName + '` should partial match arrays', function() { - var objects = [{ 'a': ['b'] }, { 'a': ['c', 'd'] }], - actual = lodashStable.filter(objects, matches({ 'a': ['d'] })); - - assert.deepStrictEqual(actual, [objects[1]]); - - actual = lodashStable.filter(objects, matches({ 'a': ['b', 'd'] })); - assert.deepStrictEqual(actual, []); - - actual = lodashStable.filter(objects, matches({ 'a': ['d', 'b'] })); - assert.deepStrictEqual(actual, []); - }); - - it('`_.' + methodName + '` should partial match arrays with duplicate values', function() { - var objects = [{ 'a': [1, 2] }, { 'a': [2, 2] }], - actual = lodashStable.filter(objects, matches({ 'a': [2, 2] })); - - assert.deepStrictEqual(actual, [objects[1]]); - }); - - it('should partial match arrays of objects', function() { - var objects = [ - { 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 5, 'd': 6 }] }, - { 'a': [{ 'b': 1, 'c': 2 }, { 'b': 4, 'c': 6, 'd': 7 }] } - ]; - - var actual = lodashStable.filter(objects, matches({ 'a': [{ 'b': 1 }, { 'b': 4, 'c': 5 }] })); - assert.deepStrictEqual(actual, [objects[0]]); - }); - - it('`_.' + methodName + '` should partial match maps', function() { - if (Map) { - var objects = [{ 'a': new Map }, { 'a': new Map }]; - objects[0].a.set('a', 1); - objects[1].a.set('a', 1); - objects[1].a.set('b', 2); - - var map = new Map; - map.set('b', 2); - var actual = lodashStable.filter(objects, matches({ 'a': map })); - - assert.deepStrictEqual(actual, [objects[1]]); - - map.delete('b'); - actual = lodashStable.filter(objects, matches({ 'a': map })); - - assert.deepStrictEqual(actual, objects); - - map.set('c', 3); - actual = lodashStable.filter(objects, matches({ 'a': map })); - - assert.deepStrictEqual(actual, []); - } - }); - - it('`_.' + methodName + '` should partial match sets', function() { - if (Set) { - var objects = [{ 'a': new Set }, { 'a': new Set }]; - objects[0].a.add(1); - objects[1].a.add(1); - objects[1].a.add(2); - - var set = new Set; - set.add(2); - var actual = lodashStable.filter(objects, matches({ 'a': set })); - - assert.deepStrictEqual(actual, [objects[1]]); - - set.delete(2); - actual = lodashStable.filter(objects, matches({ 'a': set })); - - assert.deepStrictEqual(actual, objects); - - set.add(3); - actual = lodashStable.filter(objects, matches({ 'a': set })); - - assert.deepStrictEqual(actual, []); - } - }); - - it('`_.' + methodName + '` should match `undefined` values', function() { - var objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }], - actual = lodashStable.map(objects, matches({ 'b': undefined })), - expected = [false, false, true]; - - assert.deepStrictEqual(actual, expected); - - actual = lodashStable.map(objects, matches({ 'a': 1, 'b': undefined })); - - assert.deepStrictEqual(actual, expected); - - objects = [{ 'a': { 'b': 2 } }, { 'a': { 'b': 2, 'c': 3 } }, { 'a': { 'b': 2, 'c': undefined } }]; - actual = lodashStable.map(objects, matches({ 'a': { 'c': undefined } })); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should match `undefined` values on primitives', function() { - numberProto.a = 1; - numberProto.b = undefined; - - try { - var par = matches({ 'b': undefined }); - assert.strictEqual(par(1), true); - } catch (e) { - assert.ok(false, e.message); - } - try { - par = matches({ 'a': 1, 'b': undefined }); - assert.strictEqual(par(1), true); - } catch (e) { - assert.ok(false, e.message); - } - numberProto.a = { 'b': 1, 'c': undefined }; - try { - par = matches({ 'a': { 'c': undefined } }); - assert.strictEqual(par(1), true); - } catch (e) { - assert.ok(false, e.message); - } - delete numberProto.a; - delete numberProto.b; - }); - - it('`_.' + methodName + '` should return `false` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse), - par = matches({ 'a': 1 }); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? par(value) : par(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `true` when comparing an empty `source`', function() { - var object = { 'a': 1 }, - expected = lodashStable.map(empties, stubTrue); - - var actual = lodashStable.map(empties, function(value) { - var par = matches(value); - return par(object); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `true` when comparing an empty `source` to a nullish `object`', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubTrue), - par = matches({}); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? par(value) : par(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should return `true` when comparing a `source` of empty arrays and objects', function() { - var objects = [{ 'a': [1], 'b': { 'c': 1 } }, { 'a': [2, 3], 'b': { 'd': 2 } }], - actual = lodashStable.filter(objects, matches({ 'a': [], 'b': {} })); - - assert.deepStrictEqual(actual, objects); - }); - }); -}); diff --git a/test/matches-methods.spec.js b/test/matches-methods.spec.js new file mode 100644 index 0000000000..f761ce99f9 --- /dev/null +++ b/test/matches-methods.spec.js @@ -0,0 +1,310 @@ +import lodashStable from 'lodash'; +import { _, stubTrue, noop, numberProto, stubFalse, empties } from './utils'; +import isMatch from '../src/isMatch'; + +describe('matches methods', () => { + lodashStable.each(['matches', 'isMatch'], (methodName) => { + const isMatches = methodName === 'matches'; + + function matches(source) { + return isMatches + ? _.matches(source) + : function (object) { + return isMatch(object, source); + }; + } + + it(`\`_.${methodName}\` should perform a deep comparison between \`source\` and \`object\``, () => { + let object = { a: 1, b: 2, c: 3 }; + let par = matches({ a: 1 }); + + expect(par(object)).toBe(true); + + par = matches({ b: 1 }); + expect(par(object)).toBe(false); + + par = matches({ a: 1, c: 3 }); + expect(par(object)).toBe(true); + + par = matches({ c: 3, d: 4 }); + expect(par(object)).toBe(false); + + object = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 }; + par = matches({ a: { b: { c: 1 } } }); + + expect(par(object)).toBe(true); + }); + + it(`\`_.${methodName}\` should match inherited string keyed \`object\` properties`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const object = { a: new Foo() }; + const par = matches({ a: { b: 2 } }); + + expect(par(object)).toBe(true); + }); + + it(`\`_.${methodName}\` should not match by inherited \`source\` properties`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const objects = [{ a: 1 }, { a: 1, b: 2 }]; + const source = new Foo(); + const actual = lodashStable.map(objects, matches(source)); + const expected = lodashStable.map(objects, stubTrue); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should compare a variety of \`source\` property values`, () => { + const object1 = { a: false, b: true, c: '3', d: 4, e: [5], f: { g: 6 } }; + const object2 = { a: 0, b: 1, c: 3, d: '4', e: ['5'], f: { g: '6' } }; + const par = matches(object1); + + expect(par(object1)).toBe(true); + expect(par(object2)).toBe(false); + }); + + it(`\`_.${methodName}\` should match \`-0\` as \`0\``, () => { + const object1 = { a: -0 }; + const object2 = { a: 0 }; + let par = matches(object1); + + expect(par(object2)).toBe(true); + + par = matches(object2); + expect(par(object1)).toBe(true); + }); + + it(`\`_.${methodName}\` should compare functions by reference`, () => { + const object1 = { a: lodashStable.noop }; + const object2 = { a: noop }; + const object3 = { a: {} }; + const par = matches(object1); + + expect(par(object1)).toBe(true); + expect(par(object2)).toBe(false); + expect(par(object3)).toBe(false); + }); + + it(`\`_.${methodName}\` should work with a function for \`object\``, () => { + function Foo() {} + Foo.a = { b: 2, c: 3 }; + + const par = matches({ a: { b: 2 } }); + expect(par(Foo)).toBe(true); + }); + + it(`\`_.${methodName}\` should work with a function for \`source\``, () => { + function Foo() {} + Foo.a = 1; + Foo.b = function () {}; + Foo.c = 3; + + const objects = [{ a: 1 }, { a: 1, b: Foo.b, c: 3 }]; + const actual = lodashStable.map(objects, matches(Foo)); + + expect(actual, [false).toEqual(true]); + }); + + it(`\`_.${methodName}\` should work with a non-plain \`object\``, () => { + function Foo(object) { + lodashStable.assign(this, object); + } + + const object = new Foo({ a: new Foo({ b: 2, c: 3 }) }); + const par = matches({ a: { b: 2 } }); + + expect(par(object)).toBe(true); + }); + + it(`\`_.${methodName}\` should partial match arrays`, () => { + const objects = [{ a: ['b'] }, { a: ['c', 'd'] }]; + let actual = lodashStable.filter(objects, matches({ a: ['d'] })); + + expect(actual).toEqual([objects[1]]); + + actual = lodashStable.filter(objects, matches({ a: ['b', 'd'] })); + expect(actual).toEqual([]); + + actual = lodashStable.filter(objects, matches({ a: ['d', 'b'] })); + expect(actual).toEqual([]); + }); + + it(`\`_.${methodName}\` should partial match arrays with duplicate values`, () => { + const objects = [{ a: [1, 2] }, { a: [2, 2] }]; + const actual = lodashStable.filter(objects, matches({ a: [2, 2] })); + + expect(actual).toEqual([objects[1]]); + }); + + it('should partial match arrays of objects', () => { + const objects = [ + { + a: [ + { b: 1, c: 2 }, + { b: 4, c: 5, d: 6 }, + ], + }, + { + a: [ + { b: 1, c: 2 }, + { b: 4, c: 6, d: 7 }, + ], + }, + ]; + + const actual = lodashStable.filter(objects, matches({ a: [{ b: 1 }, { b: 4, c: 5 }] })); + expect(actual).toEqual([objects[0]]); + }); + + it(`\`_.${methodName}\` should partial match maps`, () => { + if (Map) { + const objects = [{ a: new Map() }, { a: new Map() }]; + objects[0].a.set('a', 1); + objects[1].a.set('a', 1); + objects[1].a.set('b', 2); + + const map = new Map(); + map.set('b', 2); + let actual = lodashStable.filter(objects, matches({ a: map })); + + expect(actual).toEqual([objects[1]]); + + map.delete('b'); + actual = lodashStable.filter(objects, matches({ a: map })); + + expect(actual).toEqual(objects); + + map.set('c', 3); + actual = lodashStable.filter(objects, matches({ a: map })); + + expect(actual).toEqual([]); + } + }); + + it(`\`_.${methodName}\` should partial match sets`, () => { + if (Set) { + const objects = [{ a: new Set() }, { a: new Set() }]; + objects[0].a.add(1); + objects[1].a.add(1); + objects[1].a.add(2); + + const set = new Set(); + set.add(2); + let actual = lodashStable.filter(objects, matches({ a: set })); + + expect(actual).toEqual([objects[1]]); + + set.delete(2); + actual = lodashStable.filter(objects, matches({ a: set })); + + expect(actual).toEqual(objects); + + set.add(3); + actual = lodashStable.filter(objects, matches({ a: set })); + + expect(actual).toEqual([]); + } + }); + + it(`\`_.${methodName}\` should match \`undefined\` values`, () => { + let objects = [{ a: 1 }, { a: 1, b: 1 }, { a: 1, b: undefined }]; + let actual = lodashStable.map(objects, matches({ b: undefined })); + const expected = [false, false, true]; + + expect(actual).toEqual(expected); + + actual = lodashStable.map(objects, matches({ a: 1, b: undefined })); + + expect(actual).toEqual(expected); + + objects = [{ a: { b: 2 } }, { a: { b: 2, c: 3 } }, { a: { b: 2, c: undefined } }]; + actual = lodashStable.map(objects, matches({ a: { c: undefined } })); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should match \`undefined\` values on primitives`, () => { + numberProto.a = 1; + numberProto.b = undefined; + + try { + var par = matches({ b: undefined }); + expect(par(1)).toBe(true); + } catch (e) { + expect(false, e.message) + } + try { + par = matches({ a: 1, b: undefined }); + expect(par(1)).toBe(true); + } catch (e) { + expect(false, e.message) + } + numberProto.a = { b: 1, c: undefined }; + try { + par = matches({ a: { c: undefined } }); + expect(par(1)).toBe(true); + } catch (e) { + expect(false, e.message) + } + delete numberProto.a; + delete numberProto.b; + }); + + it(`\`_.${methodName}\` should return \`false\` when \`object\` is nullish`, () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubFalse); + const par = matches({ a: 1 }); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? par(value) : par(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`true\` when comparing an empty \`source\``, () => { + const object = { a: 1 }; + const expected = lodashStable.map(empties, stubTrue); + + const actual = lodashStable.map(empties, (value) => { + const par = matches(value); + return par(object); + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`true\` when comparing an empty \`source\` to a nullish \`object\``, () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubTrue); + const par = matches({}); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? par(value) : par(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should return \`true\` when comparing a \`source\` of empty arrays and objects`, () => { + const objects = [ + { a: [1], b: { c: 1 } }, + { a: [2, 3], b: { d: 2 } }, + ]; + const actual = lodashStable.filter(objects, matches({ a: [], b: {} })); + + expect(actual).toEqual(objects); + }); + }); +}); diff --git a/test/matches.js b/test/matches.js deleted file mode 100644 index 5901655ee2..0000000000 --- a/test/matches.js +++ /dev/null @@ -1,32 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import matches from '../matches.js'; - -describe('matches', function() { - it('should not change behavior if `source` is modified', function() { - var sources = [ - { 'a': { 'b': 2, 'c': 3 } }, - { 'a': 1, 'b': 2 }, - { 'a': 1 } - ]; - - lodashStable.each(sources, function(source, index) { - var object = lodashStable.cloneDeep(source), - par = matches(source); - - assert.strictEqual(par(object), true); - - if (index) { - source.a = 2; - source.b = 1; - source.c = 3; - } else { - source.a.b = 1; - source.a.c = 2; - source.a.d = 3; - } - assert.strictEqual(par(object), true); - assert.strictEqual(par(source), false); - }); - }); -}); diff --git a/test/matches.spec.js b/test/matches.spec.js new file mode 100644 index 0000000000..141c8f1133 --- /dev/null +++ b/test/matches.spec.js @@ -0,0 +1,27 @@ +import lodashStable from 'lodash'; +import matches from '../src/matches'; + +describe('matches', () => { + it('should not change behavior if `source` is modified', () => { + const sources = [{ a: { b: 2, c: 3 } }, { a: 1, b: 2 }, { a: 1 }]; + + lodashStable.each(sources, (source, index) => { + const object = lodashStable.cloneDeep(source); + const par = matches(source); + + expect(par(object)).toBe(true); + + if (index) { + source.a = 2; + source.b = 1; + source.c = 3; + } else { + source.a.b = 1; + source.a.c = 2; + source.a.d = 3; + } + expect(par(object)).toBe(true); + expect(par(source)).toBe(false); + }); + }); +}); diff --git a/test/matchesProperty.js b/test/matchesProperty.js deleted file mode 100644 index 23eb042183..0000000000 --- a/test/matchesProperty.js +++ /dev/null @@ -1,368 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubTrue, stubFalse, noop, numberProto } from './utils.js'; -import matchesProperty from '../matchesProperty.js'; - -describe('matchesProperty', function() { - it('should create a function that performs a deep comparison between a property value and `srcValue`', function() { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - matches = matchesProperty('a', 1); - - assert.strictEqual(matches.length, 1); - assert.strictEqual(matches(object), true); - - matches = matchesProperty('b', 3); - assert.strictEqual(matches(object), false); - - matches = matchesProperty('a', { 'a': 1, 'c': 3 }); - assert.strictEqual(matches({ 'a': object }), true); - - matches = matchesProperty('a', { 'c': 3, 'd': 4 }); - assert.strictEqual(matches(object), false); - - object = { 'a': { 'b': { 'c': 1, 'd': 2 }, 'e': 3 }, 'f': 4 }; - matches = matchesProperty('a', { 'b': { 'c': 1 } }); - - assert.strictEqual(matches(object), true); - }); - - it('should support deep paths', function() { - var object = { 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var matches = matchesProperty(path, 2); - assert.strictEqual(matches(object), true); - }); - }); - - it('should work with a non-string `path`', function() { - var array = [1, 2, 3]; - - lodashStable.each([1, [1]], function(path) { - var matches = matchesProperty(path, 2); - assert.strictEqual(matches(array), true); - }); - }); - - it('should preserve the sign of `0`', function() { - var object1 = { '-0': 'a' }, - object2 = { '0': 'b' }, - pairs = [[object1, object2], [object1, object2], [object2, object1], [object2, object1]], - props = [-0, Object(-0), 0, Object(0)], - values = ['a', 'a', 'b', 'b'], - expected = lodashStable.map(props, lodashStable.constant([true, false])); - - var actual = lodashStable.map(props, function(key, index) { - var matches = matchesProperty(key, values[index]), - pair = pairs[index]; - - return [matches(pair[0]), matches(pair[1])]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should coerce `path` to a string', function() { - function fn() {} - fn.toString = lodashStable.constant('fn'); - - var object = { 'null': 1, 'undefined': 2, 'fn': 3, '[object Object]': 4 }, - paths = [null, undefined, fn, {}], - expected = lodashStable.map(paths, stubTrue); - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(paths, function(path) { - var matches = matchesProperty(index ? [path] : path, object[path]); - return matches(object); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should match a key over a path', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - var matches = matchesProperty(path, 1); - assert.strictEqual(matches(object), true); - }); - }); - - it('should return `false` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse); - - lodashStable.each(['constructor', ['constructor']], function(path) { - var matches = matchesProperty(path, 1); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? matches(value) : matches(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `false` for deep paths when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse); - - lodashStable.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) { - var matches = matchesProperty(path, 1); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? matches(value) : matches(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `false` if parts of `path` are missing', function() { - var object = {}; - - lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) { - var matches = matchesProperty(path, 1); - assert.strictEqual(matches(object), false); - }); - }); - - it('should match inherited string keyed `srcValue` properties', function() { - function Foo() {} - Foo.prototype.b = 2; - - var object = { 'a': new Foo }; - - lodashStable.each(['a', ['a']], function(path) { - var matches = matchesProperty(path, { 'b': 2 }); - assert.strictEqual(matches(object), true); - }); - }); - - it('should not match by inherited `srcValue` properties', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': 2 } }], - expected = lodashStable.map(objects, stubTrue); - - lodashStable.each(['a', ['a']], function(path) { - assert.deepStrictEqual(lodashStable.map(objects, matchesProperty(path, new Foo)), expected); - }); - }); - - it('should compare a variety of values', function() { - var object1 = { 'a': false, 'b': true, 'c': '3', 'd': 4, 'e': [5], 'f': { 'g': 6 } }, - object2 = { 'a': 0, 'b': 1, 'c': 3, 'd': '4', 'e': ['5'], 'f': { 'g': '6' } }, - matches = matchesProperty('a', object1); - - assert.strictEqual(matches({ 'a': object1 }), true); - assert.strictEqual(matches({ 'a': object2 }), false); - }); - - it('should match `-0` as `0`', function() { - var matches = matchesProperty('a', -0); - assert.strictEqual(matches({ 'a': 0 }), true); - - matches = matchesProperty('a', 0); - assert.strictEqual(matches({ 'a': -0 }), true); - }); - - it('should compare functions by reference', function() { - var object1 = { 'a': lodashStable.noop }, - object2 = { 'a': noop }, - object3 = { 'a': {} }, - matches = matchesProperty('a', object1); - - assert.strictEqual(matches({ 'a': object1 }), true); - assert.strictEqual(matches({ 'a': object2 }), false); - assert.strictEqual(matches({ 'a': object3 }), false); - }); - - it('should work with a function for `srcValue`', function() { - function Foo() {} - Foo.a = 1; - Foo.b = function() {}; - Foo.c = 3; - - var objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': Foo.b, 'c': 3 } }], - actual = lodashStable.map(objects, matchesProperty('a', Foo)); - - assert.deepStrictEqual(actual, [false, true]); - }); - - it('should work with a non-plain `srcValue`', function() { - function Foo(object) { lodashStable.assign(this, object); } - - var object = new Foo({ 'a': new Foo({ 'b': 1, 'c': 2 }) }), - matches = matchesProperty('a', { 'b': 1 }); - - assert.strictEqual(matches(object), true); - }); - - it('should partial match arrays', function() { - var objects = [{ 'a': ['b'] }, { 'a': ['c', 'd'] }], - actual = lodashStable.filter(objects, matchesProperty('a', ['d'])); - - assert.deepStrictEqual(actual, [objects[1]]); - - actual = lodashStable.filter(objects, matchesProperty('a', ['b', 'd'])); - assert.deepStrictEqual(actual, []); - - actual = lodashStable.filter(objects, matchesProperty('a', ['d', 'b'])); - assert.deepStrictEqual(actual, []); - }); - - it('should partial match arrays with duplicate values', function() { - var objects = [{ 'a': [1, 2] }, { 'a': [2, 2] }], - actual = lodashStable.filter(objects, matchesProperty('a', [2, 2])); - - assert.deepStrictEqual(actual, [objects[1]]); - }); - - it('should partial match arrays of objects', function() { - var objects = [ - { 'a': [{ 'a': 1, 'b': 2 }, { 'a': 4, 'b': 5, 'c': 6 }] }, - { 'a': [{ 'a': 1, 'b': 2 }, { 'a': 4, 'b': 6, 'c': 7 }] } - ]; - - var actual = lodashStable.filter(objects, matchesProperty('a', [{ 'a': 1 }, { 'a': 4, 'b': 5 }])); - assert.deepStrictEqual(actual, [objects[0]]); - }); - it('should partial match maps', function() { - if (Map) { - var objects = [{ 'a': new Map }, { 'a': new Map }]; - objects[0].a.set('a', 1); - objects[1].a.set('a', 1); - objects[1].a.set('b', 2); - - var map = new Map; - map.set('b', 2); - var actual = lodashStable.filter(objects, matchesProperty('a', map)); - - assert.deepStrictEqual(actual, [objects[1]]); - - map.delete('b'); - actual = lodashStable.filter(objects, matchesProperty('a', map)); - - assert.deepStrictEqual(actual, objects); - - map.set('c', 3); - actual = lodashStable.filter(objects, matchesProperty('a', map)); - - assert.deepStrictEqual(actual, []); - } - }); - - it('should partial match sets', function() { - if (Set) { - var objects = [{ 'a': new Set }, { 'a': new Set }]; - objects[0].a.add(1); - objects[1].a.add(1); - objects[1].a.add(2); - - var set = new Set; - set.add(2); - var actual = lodashStable.filter(objects, matchesProperty('a', set)); - - assert.deepStrictEqual(actual, [objects[1]]); - - set.delete(2); - actual = lodashStable.filter(objects, matchesProperty('a', set)); - - assert.deepStrictEqual(actual, objects); - - set.add(3); - actual = lodashStable.filter(objects, matchesProperty('a', set)); - - assert.deepStrictEqual(actual, []); - } - }); - - it('should match `undefined` values', function() { - var objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }], - actual = lodashStable.map(objects, matchesProperty('b', undefined)), - expected = [false, false, true]; - - assert.deepStrictEqual(actual, expected); - - objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': 1 } }, { 'a': { 'a': 1, 'b': undefined } }]; - actual = lodashStable.map(objects, matchesProperty('a', { 'b': undefined })); - - assert.deepStrictEqual(actual, expected); - }); - - it('should match `undefined` values of nested objects', function() { - var object = { 'a': { 'b': undefined } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var matches = matchesProperty(path, undefined); - assert.strictEqual(matches(object), true); - }); - - lodashStable.each(['a.a', ['a', 'a']], function(path) { - var matches = matchesProperty(path, undefined); - assert.strictEqual(matches(object), false); - }); - }); - - it('should match `undefined` values on primitives', function() { - numberProto.a = 1; - numberProto.b = undefined; - - try { - var matches = matchesProperty('b', undefined); - assert.strictEqual(matches(1), true); - } catch (e) { - assert.ok(false, e.message); - } - numberProto.a = { 'b': 1, 'c': undefined }; - try { - matches = matchesProperty('a', { 'c': undefined }); - assert.strictEqual(matches(1), true); - } catch (e) { - assert.ok(false, e.message); - } - delete numberProto.a; - delete numberProto.b; - }); - - it('should return `true` when comparing a `srcValue` of empty arrays and objects', function() { - var objects = [{ 'a': [1], 'b': { 'c': 1 } }, { 'a': [2, 3], 'b': { 'd': 2 } }], - matches = matchesProperty('a', { 'a': [], 'b': {} }); - - var actual = lodashStable.filter(objects, function(object) { - return matches({ 'a': object }); - }); - - assert.deepStrictEqual(actual, objects); - }); - - it('should not change behavior if `srcValue` is modified', function() { - lodashStable.each([{ 'a': { 'b': 2, 'c': 3 } }, { 'a': 1, 'b': 2 }, { 'a': 1 }], function(source, index) { - var object = lodashStable.cloneDeep(source), - matches = matchesProperty('a', source); - - assert.strictEqual(matches({ 'a': object }), true); - - if (index) { - source.a = 2; - source.b = 1; - source.c = 3; - } else { - source.a.b = 1; - source.a.c = 2; - source.a.d = 3; - } - assert.strictEqual(matches({ 'a': object }), true); - assert.strictEqual(matches({ 'a': source }), false); - }); - }); -}); diff --git a/test/matchesProperty.spec.js b/test/matchesProperty.spec.js new file mode 100644 index 0000000000..571a2e70c3 --- /dev/null +++ b/test/matchesProperty.spec.js @@ -0,0 +1,394 @@ +import lodashStable from 'lodash'; +import { stubTrue, stubFalse, noop, numberProto } from './utils'; +import matchesProperty from '../src/matchesProperty'; + +describe('matchesProperty', () => { + it('should create a function that performs a deep comparison between a property value and `srcValue`', () => { + let object = { a: 1, b: 2, c: 3 }; + let matches = matchesProperty('a', 1); + + expect(matches.length).toBe(1); + expect(matches(object)).toBe(true); + + matches = matchesProperty('b', 3); + expect(matches(object)).toBe(false); + + matches = matchesProperty('a', { a: 1, c: 3 }); + expect(matches({ a: object })).toBe(true); + + matches = matchesProperty('a', { c: 3, d: 4 }); + expect(matches(object)).toBe(false); + + object = { a: { b: { c: 1, d: 2 }, e: 3 }, f: 4 }; + matches = matchesProperty('a', { b: { c: 1 } }); + + expect(matches(object)).toBe(true); + }); + + it('should support deep paths', () => { + const object = { a: { b: 2 } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const matches = matchesProperty(path, 2); + expect(matches(object)).toBe(true); + }); + }); + + it('should work with a non-string `path`', () => { + const array = [1, 2, 3]; + + lodashStable.each([1, [1]], (path) => { + const matches = matchesProperty(path, 2); + expect(matches(array)).toBe(true); + }); + }); + + it('should preserve the sign of `0`', () => { + const object1 = { '-0': 'a' }; + const object2 = { 0: 'b' }; + const pairs = [ + [object1, object2], + [object1, object2], + [object2, object1], + [object2, object1], + ]; + const props = [-0, Object(-0), 0, Object(0)]; + const values = ['a', 'a', 'b', 'b']; + const expected = lodashStable.map(props, lodashStable.constant([true, false])); + + const actual = lodashStable.map(props, (key, index) => { + const matches = matchesProperty(key, values[index]); + const pair = pairs[index]; + + return [matches(pair[0]), matches(pair[1])]; + }); + + expect(actual).toEqual(expected); + }); + + it('should coerce `path` to a string', () => { + function fn() {} + fn.toString = lodashStable.constant('fn'); + + const object = { null: 1, undefined: 2, fn: 3, '[object Object]': 4 }; + const paths = [null, undefined, fn, {}]; + const expected = lodashStable.map(paths, stubTrue); + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(paths, (path) => { + const matches = matchesProperty(index ? [path] : path, object[path]); + return matches(object); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should match a key over a path', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + const matches = matchesProperty(path, 1); + expect(matches(object)).toBe(true); + }); + }); + + it('should return `false` when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubFalse); + + lodashStable.each(['constructor', ['constructor']], (path) => { + const matches = matchesProperty(path, 1); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? matches(value) : matches(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should return `false` for deep paths when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubFalse); + + lodashStable.each( + ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], + (path) => { + const matches = matchesProperty(path, 1); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? matches(value) : matches(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }, + ); + }); + + it('should return `false` if parts of `path` are missing', () => { + const object = {}; + + lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], (path) => { + const matches = matchesProperty(path, 1); + expect(matches(object)).toBe(false); + }); + }); + + it('should match inherited string keyed `srcValue` properties', () => { + function Foo() {} + Foo.prototype.b = 2; + + const object = { a: new Foo() }; + + lodashStable.each(['a', ['a']], (path) => { + const matches = matchesProperty(path, { b: 2 }); + expect(matches(object)).toBe(true); + }); + }); + + it('should not match by inherited `srcValue` properties', () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const objects = [{ a: { a: 1 } }, { a: { a: 1, b: 2 } }]; + const expected = lodashStable.map(objects, stubTrue); + + lodashStable.each(['a', ['a']], (path) => { + assert.deepStrictEqual( + lodashStable.map(objects, matchesProperty(path, new Foo())), + expected, + ); + }); + }); + + it('should compare a variety of values', () => { + const object1 = { a: false, b: true, c: '3', d: 4, e: [5], f: { g: 6 } }; + const object2 = { a: 0, b: 1, c: 3, d: '4', e: ['5'], f: { g: '6' } }; + const matches = matchesProperty('a', object1); + + expect(matches({ a: object1 })).toBe(true); + expect(matches({ a: object2 })).toBe(false); + }); + + it('should match `-0` as `0`', () => { + let matches = matchesProperty('a', -0); + expect(matches({ a: 0 })).toBe(true); + + matches = matchesProperty('a', 0); + expect(matches({ a: -0 })).toBe(true); + }); + + it('should compare functions by reference', () => { + const object1 = { a: lodashStable.noop }; + const object2 = { a: noop }; + const object3 = { a: {} }; + const matches = matchesProperty('a', object1); + + expect(matches({ a: object1 })).toBe(true); + expect(matches({ a: object2 })).toBe(false); + expect(matches({ a: object3 })).toBe(false); + }); + + it('should work with a function for `srcValue`', () => { + function Foo() {} + Foo.a = 1; + Foo.b = function () {}; + Foo.c = 3; + + const objects = [{ a: { a: 1 } }, { a: { a: 1, b: Foo.b, c: 3 } }]; + const actual = lodashStable.map(objects, matchesProperty('a', Foo)); + + expect(actual, [false).toEqual(true]); + }); + + it('should work with a non-plain `srcValue`', () => { + function Foo(object) { + lodashStable.assign(this, object); + } + + const object = new Foo({ a: new Foo({ b: 1, c: 2 }) }); + const matches = matchesProperty('a', { b: 1 }); + + expect(matches(object)).toBe(true); + }); + + it('should partial match arrays', () => { + const objects = [{ a: ['b'] }, { a: ['c', 'd'] }]; + let actual = lodashStable.filter(objects, matchesProperty('a', ['d'])); + + expect(actual).toEqual([objects[1]]); + + actual = lodashStable.filter(objects, matchesProperty('a', ['b', 'd'])); + expect(actual).toEqual([]); + + actual = lodashStable.filter(objects, matchesProperty('a', ['d', 'b'])); + expect(actual).toEqual([]); + }); + + it('should partial match arrays with duplicate values', () => { + const objects = [{ a: [1, 2] }, { a: [2, 2] }]; + const actual = lodashStable.filter(objects, matchesProperty('a', [2, 2])); + + expect(actual).toEqual([objects[1]]); + }); + + it('should partial match arrays of objects', () => { + const objects = [ + { + a: [ + { a: 1, b: 2 }, + { a: 4, b: 5, c: 6 }, + ], + }, + { + a: [ + { a: 1, b: 2 }, + { a: 4, b: 6, c: 7 }, + ], + }, + ]; + + const actual = lodashStable.filter( + objects, + matchesProperty('a', [{ a: 1 }, { a: 4, b: 5 }]), + ); + expect(actual).toEqual([objects[0]]); + }); + it('should partial match maps', () => { + if (Map) { + const objects = [{ a: new Map() }, { a: new Map() }]; + objects[0].a.set('a', 1); + objects[1].a.set('a', 1); + objects[1].a.set('b', 2); + + const map = new Map(); + map.set('b', 2); + let actual = lodashStable.filter(objects, matchesProperty('a', map)); + + expect(actual).toEqual([objects[1]]); + + map.delete('b'); + actual = lodashStable.filter(objects, matchesProperty('a', map)); + + expect(actual).toEqual(objects); + + map.set('c', 3); + actual = lodashStable.filter(objects, matchesProperty('a', map)); + + expect(actual).toEqual([]); + } + }); + + it('should partial match sets', () => { + if (Set) { + const objects = [{ a: new Set() }, { a: new Set() }]; + objects[0].a.add(1); + objects[1].a.add(1); + objects[1].a.add(2); + + const set = new Set(); + set.add(2); + let actual = lodashStable.filter(objects, matchesProperty('a', set)); + + expect(actual).toEqual([objects[1]]); + + set.delete(2); + actual = lodashStable.filter(objects, matchesProperty('a', set)); + + expect(actual).toEqual(objects); + + set.add(3); + actual = lodashStable.filter(objects, matchesProperty('a', set)); + + expect(actual).toEqual([]); + } + }); + + it('should match `undefined` values', () => { + let objects = [{ a: 1 }, { a: 1, b: 1 }, { a: 1, b: undefined }]; + let actual = lodashStable.map(objects, matchesProperty('b', undefined)); + const expected = [false, false, true]; + + expect(actual).toEqual(expected); + + objects = [{ a: { a: 1 } }, { a: { a: 1, b: 1 } }, { a: { a: 1, b: undefined } }]; + actual = lodashStable.map(objects, matchesProperty('a', { b: undefined })); + + expect(actual).toEqual(expected); + }); + + it('should match `undefined` values of nested objects', () => { + const object = { a: { b: undefined } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const matches = matchesProperty(path, undefined); + expect(matches(object)).toBe(true); + }); + + lodashStable.each(['a.a', ['a', 'a']], (path) => { + const matches = matchesProperty(path, undefined); + expect(matches(object)).toBe(false); + }); + }); + + it('should match `undefined` values on primitives', () => { + numberProto.a = 1; + numberProto.b = undefined; + + try { + var matches = matchesProperty('b', undefined); + expect(matches(1)).toBe(true); + } catch (e) { + expect(false, e.message) + } + numberProto.a = { b: 1, c: undefined }; + try { + matches = matchesProperty('a', { c: undefined }); + expect(matches(1)).toBe(true); + } catch (e) { + expect(false, e.message) + } + delete numberProto.a; + delete numberProto.b; + }); + + it('should return `true` when comparing a `srcValue` of empty arrays and objects', () => { + const objects = [ + { a: [1], b: { c: 1 } }, + { a: [2, 3], b: { d: 2 } }, + ]; + const matches = matchesProperty('a', { a: [], b: {} }); + + const actual = lodashStable.filter(objects, (object) => matches({ a: object })); + + expect(actual).toEqual(objects); + }); + + it('should not change behavior if `srcValue` is modified', () => { + lodashStable.each([{ a: { b: 2, c: 3 } }, { a: 1, b: 2 }, { a: 1 }], (source, index) => { + const object = lodashStable.cloneDeep(source); + const matches = matchesProperty('a', source); + + expect(matches({ a: object })).toBe(true); + + if (index) { + source.a = 2; + source.b = 1; + source.c = 3; + } else { + source.a.b = 1; + source.a.c = 2; + source.a.d = 3; + } + expect(matches({ a: object })).toBe(true); + expect(matches({ a: source })).toBe(false); + }); + }); +}); diff --git a/test/math-operator-methods.js b/test/math-operator-methods.js deleted file mode 100644 index cd5e88c4ee..0000000000 --- a/test/math-operator-methods.js +++ /dev/null @@ -1,56 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, symbol } from './utils.js'; - -describe('math operator methods', function() { - lodashStable.each(['add', 'divide', 'multiply', 'subtract'], function(methodName) { - var func = _[methodName], - isAddSub = methodName == 'add' || methodName == 'subtract'; - - it('`_.' + methodName + '` should return `' + (isAddSub ? 0 : 1) + '` when no arguments are given', function() { - assert.strictEqual(func(), isAddSub ? 0 : 1); - }); - - it('`_.' + methodName + '` should work with only one defined argument', function() { - assert.strictEqual(func(6), 6); - assert.strictEqual(func(6, undefined), 6); - assert.strictEqual(func(undefined, 4), 4); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var values = [0, '0', -0, '-0'], - expected = [[0, Infinity], ['0', Infinity], [-0, -Infinity], ['-0', -Infinity]]; - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(values, function(value) { - var result = index ? func(undefined, value) : func(value); - return [result, 1 / result]; - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('`_.' + methodName + '` should convert objects to `NaN`', function() { - assert.deepStrictEqual(func(0, {}), NaN); - assert.deepStrictEqual(func({}, 0), NaN); - }); - - it('`_.' + methodName + '` should convert symbols to `NaN`', function() { - if (Symbol) { - assert.deepStrictEqual(func(0, symbol), NaN); - assert.deepStrictEqual(func(symbol, 0), NaN); - } - }); - - it('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - var actual = _(1)[methodName](2); - assert.notOk(actual instanceof _); - }); - - it('`_.' + methodName + '` should return a wrapped value when explicitly chaining', function() { - var actual = _(1).chain()[methodName](2); - assert.ok(actual instanceof _); - }); - }); -}); diff --git a/test/math-operator-methods.spec.js b/test/math-operator-methods.spec.js new file mode 100644 index 0000000000..db616d8f29 --- /dev/null +++ b/test/math-operator-methods.spec.js @@ -0,0 +1,62 @@ +import lodashStable from 'lodash'; +import { _, symbol } from './utils'; + +describe('math operator methods', () => { + lodashStable.each(['add', 'divide', 'multiply', 'subtract'], (methodName) => { + const func = _[methodName]; + const isAddSub = methodName === 'add' || methodName === 'subtract'; + + it(`\`_.${methodName}\` should return \`${ + isAddSub ? 0 : 1 + }\` when no arguments are given`, () => { + expect(func()).toBe(isAddSub ? 0 : 1); + }); + + it(`\`_.${methodName}\` should work with only one defined argument`, () => { + expect(func(6)).toBe(6); + expect(func(6, undefined)).toBe(6); + expect(func(undefined, 4)).toBe(4); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const values = [0, '0', -0, '-0']; + const expected = [ + [0, Infinity], + ['0', Infinity], + [-0, -Infinity], + ['-0', -Infinity], + ]; + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(values, (value) => { + const result = index ? func(undefined, value) : func(value); + return [result, 1 / result]; + }); + + expect(actual).toEqual(expected); + }); + }); + + it(`\`_.${methodName}\` should convert objects to \`NaN\``, () => { + expect(func(0, {})).toEqual(NaN); + expect(func({}, 0)).toEqual(NaN); + }); + + it(`\`_.${methodName}\` should convert symbols to \`NaN\``, () => { + if (Symbol) { + expect(func(0, symbol)).toEqual(NaN); + expect(func(symbol, 0)).toEqual(NaN); + } + }); + + it(`\`_.${methodName}\` should return an unwrapped value when implicitly chaining`, () => { + const actual = _(1)[methodName](2); + assert.notOk(actual instanceof _); + }); + + it(`\`_.${methodName}\` should return a wrapped value when explicitly chaining`, () => { + const actual = _(1).chain()[methodName](2); + expect(actual instanceof _); + }); + }); +}); diff --git a/test/max.js b/test/max.js deleted file mode 100644 index 28fca7954e..0000000000 --- a/test/max.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, noop } from './utils.js'; -import max from '../max.js'; - -describe('max', function() { - it('should return the largest value from a collection', function() { - assert.strictEqual(max([1, 2, 3]), 3); - }); - - it('should return `undefined` for empty collections', function() { - var values = falsey.concat([[]]), - expected = lodashStable.map(values, noop); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? max(value) : max(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with non-numeric collection values', function() { - assert.strictEqual(max(['a', 'b']), 'b'); - }); -}); diff --git a/test/max.spec.js b/test/max.spec.js new file mode 100644 index 0000000000..37bc02e8de --- /dev/null +++ b/test/max.spec.js @@ -0,0 +1,26 @@ +import lodashStable from 'lodash'; +import { falsey, noop } from './utils'; +import max from '../src/max'; + +describe('max', () => { + it('should return the largest value from a collection', () => { + expect(max([1, 2, 3])).toBe(3); + }); + + it('should return `undefined` for empty collections', () => { + const values = falsey.concat([[]]); + const expected = lodashStable.map(values, noop); + + const actual = lodashStable.map(values, (value, index) => { + try { + return index ? max(value) : max(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should work with non-numeric collection values', () => { + expect(max(['a', 'b'])).toBe('b'); + }); +}); diff --git a/test/mean.spec.js b/test/mean.spec.js new file mode 100644 index 0000000000..d9f3e6e06d --- /dev/null +++ b/test/mean.spec.js @@ -0,0 +1,17 @@ +import lodashStable from 'lodash'; +import { empties, stubNaN } from './utils'; +import mean from '../src/mean'; + +describe('mean', () => { + it('should return the mean of an array of numbers', () => { + const array = [4, 2, 8, 6]; + expect(mean(array)).toBe(5); + }); + + it('should return `NaN` when passing empty `array` values', () => { + const expected = lodashStable.map(empties, stubNaN); + const actual = lodashStable.map(empties, mean); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/mean.test.js b/test/mean.test.js deleted file mode 100644 index f0f09061b8..0000000000 --- a/test/mean.test.js +++ /dev/null @@ -1,18 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, stubNaN } from './utils.js'; -import mean from '../mean.js'; - -describe('mean', function() { - it('should return the mean of an array of numbers', function() { - var array = [4, 2, 8, 6]; - assert.strictEqual(mean(array), 5); - }); - - it('should return `NaN` when passing empty `array` values', function() { - var expected = lodashStable.map(empties, stubNaN), - actual = lodashStable.map(empties, mean); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/meanBy.js b/test/meanBy.js deleted file mode 100644 index 987eebdd3d..0000000000 --- a/test/meanBy.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import meanBy from '../meanBy.js'; - -describe('meanBy', function() { - var objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }]; - - it('should work with an `iteratee`', function() { - var actual = meanBy(objects, function(object) { - return object.a; - }); - - assert.deepStrictEqual(actual, 2); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - meanBy(objects, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [{ 'a': 2 }]); - }); - - it('should work with `_.property` shorthands', function() { - var arrays = [[2], [3], [1]]; - assert.strictEqual(meanBy(arrays, 0), 2); - assert.strictEqual(meanBy(objects, 'a'), 2); - }); -}); diff --git a/test/meanBy.spec.js b/test/meanBy.spec.js new file mode 100644 index 0000000000..963687e648 --- /dev/null +++ b/test/meanBy.spec.js @@ -0,0 +1,28 @@ +import { slice } from './utils'; +import meanBy from '../src/meanBy'; + +describe('meanBy', () => { + const objects = [{ a: 2 }, { a: 3 }, { a: 1 }]; + + it('should work with an `iteratee`', () => { + const actual = meanBy(objects, (object) => object.a); + + expect(actual).toEqual(2); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + meanBy(objects, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([{ a: 2 }]); + }); + + it('should work with `_.property` shorthands', () => { + const arrays = [[2], [3], [1]]; + expect(meanBy(arrays, 0)).toBe(2); + expect(meanBy(objects, 'a')).toBe(2); + }); +}); diff --git a/test/memoize.spec.js b/test/memoize.spec.js new file mode 100644 index 0000000000..93d68ce6d1 --- /dev/null +++ b/test/memoize.spec.js @@ -0,0 +1,175 @@ +import lodashStable from 'lodash'; +import { noop, stubTrue, identity } from './utils'; +import memoize from '../src/memoize'; +import isFunction from '../src/isFunction'; + +describe('memoize', () => { + function CustomCache() { + this.clear(); + } + + CustomCache.prototype = { + clear: function () { + this.__data__ = []; + return this; + }, + get: function (key) { + const entry = lodashStable.find(this.__data__, ['key', key]); + return entry && entry.value; + }, + has: function (key) { + return lodashStable.some(this.__data__, ['key', key]); + }, + set: function (key, value) { + this.__data__.push({ key: key, value: value }); + return this; + }, + }; + + function ImmutableCache() { + this.__data__ = []; + } + + ImmutableCache.prototype = lodashStable.create(CustomCache.prototype, { + constructor: ImmutableCache, + clear: function () { + return new ImmutableCache(); + }, + set: function (key, value) { + const result = new ImmutableCache(); + result.__data__ = this.__data__.concat({ key: key, value: value }); + return result; + }, + }); + + it('should memoize results based on the first argument given', () => { + const memoized = memoize((a, b, c) => a + b + c); + + expect(memoized(1, 2, 3)).toBe(6); + expect(memoized(1, 3, 5)).toBe(6); + }); + + it('should support a `resolver`', () => { + const fn = function (a, b, c) { + return a + b + c; + }; + const memoized = memoize(fn, fn); + + expect(memoized(1, 2, 3)).toBe(6); + expect(memoized(1, 3, 5)).toBe(9); + }); + + it('should use `this` binding of function for `resolver`', () => { + const fn = function (a, b, c) { + return a + this.b + this.c; + }; + const memoized = memoize(fn, fn); + + const object = { memoized: memoized, b: 2, c: 3 }; + expect(object.memoized(1)).toBe(6); + + object.b = 3; + object.c = 5; + expect(object.memoized(1)).toBe(9); + }); + + it('should throw a TypeError if `resolve` is truthy and not a function', () => { + expect(() => { + memoize(noop, true); + }).toThrowError(TypeError); + }); + + it('should not error if `resolver` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (resolver, index) => { + try { + return isFunction(index ? memoize(noop, resolver) : memoize(noop)); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should check cache for own properties', () => { + const props = [ + 'constructor', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'toLocaleString', + 'toString', + 'valueOf', + ]; + + const memoized = memoize(identity); + + const actual = lodashStable.map(props, (value) => memoized(value)); + + expect(actual).toEqual(props); + }); + + it('should cache the `__proto__` key', () => { + const array = []; + const key = '__proto__'; + + lodashStable.times(2, (index) => { + let count = 0; + const resolver = index ? identity : undefined; + + const memoized = memoize(() => { + count++; + return array; + }, resolver); + + const cache = memoized.cache; + + memoized(key); + memoized(key); + + expect(count).toBe(1); + expect(cache.get(key)).toBe(array); + expect(cache.__data__ instanceof Array).toBe(false); + expect(cache.delete(key)).toBe(true); + }); + }); + + it('should allow `_.memoize.Cache` to be customized', () => { + const oldCache = memoize.Cache; + memoize.Cache = CustomCache; + + const memoized = memoize((object) => object.id); + + const cache = memoized.cache; + const key1 = { id: 'a' }; + const key2 = { id: 'b' }; + + expect(memoized(key1)).toBe('a'); + expect(cache.has(key1)).toBe(true); + + expect(memoized(key2)).toBe('b'); + expect(cache.has(key2)).toBe(true); + + memoize.Cache = oldCache; + }); + + it('should works with an immutable `_.memoize.Cache` ', () => { + const oldCache = memoize.Cache; + memoize.Cache = ImmutableCache; + + const memoized = memoize((object) => object.id); + + const key1 = { id: 'a' }; + const key2 = { id: 'b' }; + + memoized(key1); + memoized(key2); + + const cache = memoized.cache; + expect(cache.has(key1)).toBe(true); + expect(cache.has(key2)).toBe(true); + + memoize.Cache = oldCache; + }); +}); diff --git a/test/memoize.test.js b/test/memoize.test.js deleted file mode 100644 index e3f92cd6d8..0000000000 --- a/test/memoize.test.js +++ /dev/null @@ -1,178 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop, stubTrue, identity } from './utils.js'; -import memoize from '../memoize.js'; -import isFunction from '../isFunction.js'; - -describe('memoize', function() { - function CustomCache() { - this.clear(); - } - - CustomCache.prototype = { - 'clear': function() { - this.__data__ = []; - return this; - }, - 'get': function(key) { - var entry = lodashStable.find(this.__data__, ['key', key]); - return entry && entry.value; - }, - 'has': function(key) { - return lodashStable.some(this.__data__, ['key', key]); - }, - 'set': function(key, value) { - this.__data__.push({ 'key': key, 'value': value }); - return this; - } - }; - - function ImmutableCache() { - this.__data__ = []; - } - - ImmutableCache.prototype = lodashStable.create(CustomCache.prototype, { - 'constructor': ImmutableCache, - 'clear': function() { - return new ImmutableCache; - }, - 'set': function(key, value) { - var result = new ImmutableCache; - result.__data__ = this.__data__.concat({ 'key': key, 'value': value }); - return result; - } - }); - - it('should memoize results based on the first argument given', function() { - var memoized = memoize(function(a, b, c) { - return a + b + c; - }); - - assert.strictEqual(memoized(1, 2, 3), 6); - assert.strictEqual(memoized(1, 3, 5), 6); - }); - - it('should support a `resolver`', function() { - var fn = function(a, b, c) { return a + b + c; }, - memoized = memoize(fn, fn); - - assert.strictEqual(memoized(1, 2, 3), 6); - assert.strictEqual(memoized(1, 3, 5), 9); - }); - - it('should use `this` binding of function for `resolver`', function() { - var fn = function(a, b, c) { return a + this.b + this.c; }, - memoized = memoize(fn, fn); - - var object = { 'memoized': memoized, 'b': 2, 'c': 3 }; - assert.strictEqual(object.memoized(1), 6); - - object.b = 3; - object.c = 5; - assert.strictEqual(object.memoized(1), 9); - }); - - it('should throw a TypeError if `resolve` is truthy and not a function', function() { - assert.throws(function() { memoize(noop, true); }, TypeError); - }); - - it('should not error if `resolver` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(resolver, index) { - try { - return isFunction(index ? memoize(noop, resolver) : memoize(noop)); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should check cache for own properties', function() { - var props = [ - 'constructor', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'toLocaleString', - 'toString', - 'valueOf' - ]; - - var memoized = memoize(identity); - - var actual = lodashStable.map(props, function(value) { - return memoized(value); - }); - - assert.deepStrictEqual(actual, props); - }); - - it('should cache the `__proto__` key', function() { - var array = [], - key = '__proto__'; - - lodashStable.times(2, function(index) { - var count = 0, - resolver = index ? identity : undefined; - - var memoized = memoize(function() { - count++; - return array; - }, resolver); - - var cache = memoized.cache; - - memoized(key); - memoized(key); - - assert.strictEqual(count, 1); - assert.strictEqual(cache.get(key), array); - assert.ok(!(cache.__data__ instanceof Array)); - assert.strictEqual(cache.delete(key), true); - }); - }); - - it('should allow `_.memoize.Cache` to be customized', function() { - var oldCache = memoize.Cache; - memoize.Cache = CustomCache; - - var memoized = memoize(function(object) { - return object.id; - }); - - var cache = memoized.cache, - key1 = { 'id': 'a' }, - key2 = { 'id': 'b' }; - - assert.strictEqual(memoized(key1), 'a'); - assert.strictEqual(cache.has(key1), true); - - assert.strictEqual(memoized(key2), 'b'); - assert.strictEqual(cache.has(key2), true); - - memoize.Cache = oldCache; - }); - - it('should works with an immutable `_.memoize.Cache` ', function() { - var oldCache = memoize.Cache; - memoize.Cache = ImmutableCache; - - var memoized = memoize(function(object) { - return object.id; - }); - - var key1 = { 'id': 'a' }, - key2 = { 'id': 'b' }; - - memoized(key1); - memoized(key2); - - var cache = memoized.cache; - assert.strictEqual(cache.has(key1), true); - assert.strictEqual(cache.has(key2), true); - - memoize.Cache = oldCache; - }); -}); diff --git a/test/memoizeCapped.spec.js b/test/memoizeCapped.spec.js new file mode 100644 index 0000000000..36214dda7d --- /dev/null +++ b/test/memoizeCapped.spec.js @@ -0,0 +1,20 @@ +import lodashStable from 'lodash'; +import { identity, MAX_MEMOIZE_SIZE } from './utils'; +import _memoizeCapped from '../src/.internal/memoizeCapped'; + +describe('memoizeCapped', () => { + const func = _memoizeCapped; + + it('should enforce a max cache size of `MAX_MEMOIZE_SIZE`', () => { + if (func) { + const memoized = func(identity); + const cache = memoized.cache; + + lodashStable.times(MAX_MEMOIZE_SIZE, memoized); + expect(cache.size).toBe(MAX_MEMOIZE_SIZE); + + memoized(MAX_MEMOIZE_SIZE); + expect(cache.size).toBe(1); + } + }); +}); diff --git a/test/memoizeCapped.test.js b/test/memoizeCapped.test.js deleted file mode 100644 index c87920a2d3..0000000000 --- a/test/memoizeCapped.test.js +++ /dev/null @@ -1,21 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, MAX_MEMOIZE_SIZE } from './utils.js'; -import _memoizeCapped from '../.internal/memoizeCapped.js'; - -describe('memoizeCapped', function() { - var func = _memoizeCapped; - - it('should enforce a max cache size of `MAX_MEMOIZE_SIZE`', function() { - if (func) { - var memoized = func(identity), - cache = memoized.cache; - - lodashStable.times(MAX_MEMOIZE_SIZE, memoized); - assert.strictEqual(cache.size, MAX_MEMOIZE_SIZE); - - memoized(MAX_MEMOIZE_SIZE); - assert.strictEqual(cache.size, 1); - } - }); -}); diff --git a/test/merge.spec.js b/test/merge.spec.js new file mode 100644 index 0000000000..0ae44012fe --- /dev/null +++ b/test/merge.spec.js @@ -0,0 +1,353 @@ +import lodashStable from 'lodash'; +import { args, typedArrays, stubTrue, defineProperty, document, root } from './utils'; +import merge from '../src/merge'; +import isArguments from '../src/isArguments'; + +describe('merge', () => { + it('should merge `source` into `object`', () => { + const names = { + characters: [{ name: 'barney' }, { name: 'fred' }], + }; + + const ages = { + characters: [{ age: 36 }, { age: 40 }], + }; + + const heights = { + characters: [{ height: '5\'4"' }, { height: '5\'5"' }], + }; + + const expected = { + characters: [ + { name: 'barney', age: 36, height: '5\'4"' }, + { name: 'fred', age: 40, height: '5\'5"' }, + ], + }; + + expect(merge(names, ages, heights)).toEqual(expected); + }); + + it('should merge sources containing circular references', () => { + const object = { + foo: { a: 1 }, + bar: { a: 2 }, + }; + + const source = { + foo: { b: { c: { d: {} } } }, + bar: {}, + }; + + source.foo.b.c.d = source; + source.bar.b = source.foo.b; + + const actual = merge(object, source); + + assert.notStrictEqual(actual.bar.b, actual.foo.b); + expect(actual.foo.b.c.d).toBe(actual.foo.b.c.d.foo.b.c.d); + }); + + it('should work with four arguments', () => { + const expected = { a: 4 }; + const actual = merge({ a: 1 }, { a: 2 }, { a: 3 }, expected); + + expect(actual).toEqual(expected); + }); + + it('should merge onto function `object` values', () => { + function Foo() {} + + const source = { a: 1 }; + const actual = merge(Foo, source); + + expect(actual).toBe(Foo); + expect(Foo.a).toBe(1); + }); + + it('should merge first source object properties to function', () => { + const fn = function () {}; + const object = { prop: {} }; + const actual = merge({ prop: fn }, object); + + expect(actual).toEqual(object); + }); + + it('should merge first and second source object properties to function', () => { + const fn = function () {}; + const object = { prop: {} }; + const actual = merge({ prop: fn }, { prop: fn }, object); + + expect(actual).toEqual(object); + }); + + it('should not merge onto function values of sources', () => { + const source1 = { a: function () {} }; + const source2 = { a: { b: 2 } }; + const expected = { a: { b: 2 } }; + let actual = merge({}, source1, source2); + + expect(actual).toEqual(expected); + expect(('b' in source1.a)).toBe(false) + + actual = merge(source1, source2); + expect(actual).toEqual(expected); + }); + + it('should merge onto non-plain `object` values', () => { + function Foo() {} + + const object = new Foo(); + const actual = merge(object, { a: 1 }); + + expect(actual).toBe(object); + expect(object.a).toBe(1); + }); + + // TODO: revisit. + it.skip('should treat sparse array sources as dense', () => { + const array = [1]; + array[2] = 3; + + const actual = merge([], array); + const expected = array.slice(); + + expected[1] = undefined; + + expect('1' in actual) + expect(actual).toEqual(expected); + }); + + it('should merge `arguments` objects', () => { + const object1 = { value: args }; + const object2 = { value: { 3: 4 } }; + let expected = { 0: 1, 1: 2, 2: 3, 3: 4 }; + let actual = merge(object1, object2); + + expect(('3' in args)).toBe(false) + expect(isArguments(actual.value)).toBe(false) + expect(actual.value).toEqual(expected); + object1.value = args; + + actual = merge(object2, object1); + expect(isArguments(actual.value)).toBe(false) + expect(actual.value).toEqual(expected); + + expected = { 0: 1, 1: 2, 2: 3 }; + + actual = merge({}, object1); + expect(isArguments(actual.value)).toBe(false) + expect(actual.value).toEqual(expected); + }); + + it('should merge typed arrays', () => { + const array1 = [0]; + const array2 = [0, 0]; + const array3 = [0, 0, 0, 0]; + const array4 = [0, 0, 0, 0, 0, 0, 0, 0]; + + const arrays = [array2, array1, array4, array3, array2, array4, array4, array3, array2]; + const buffer = ArrayBuffer && new ArrayBuffer(8); + + let expected = lodashStable.map(typedArrays, (type, index) => { + const array = arrays[index].slice(); + array[0] = 1; + return root[type] ? { value: array } : false; + }); + + let actual = lodashStable.map(typedArrays, (type) => { + const Ctor = root[type]; + return Ctor ? merge({ value: new Ctor(buffer) }, { value: [1] }) : false; + }); + + expect(lodashStable.isArray(actual)) + expect(actual).toEqual(expected); + + expected = lodashStable.map(typedArrays, (type, index) => { + const array = arrays[index].slice(); + array.push(1); + return root[type] ? { value: array } : false; + }); + + actual = lodashStable.map(typedArrays, (type, index) => { + const Ctor = root[type]; + const array = lodashStable.range(arrays[index].length); + + array.push(1); + return Ctor ? merge({ value: array }, { value: new Ctor(buffer) }) : false; + }); + + expect(lodashStable.isArray(actual)) + expect(actual).toEqual(expected); + }); + + it('should assign `null` values', () => { + const actual = merge({ a: 1 }, { a: null }); + expect(actual.a).toBe(null); + }); + + it('should assign non array/buffer/typed-array/plain-object source values directly', () => { + function Foo() {} + + const values = [ + new Foo(), + new Boolean(), + new Date(), + Foo, + new Number(), + new String(), + new RegExp(), + ]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => { + const object = merge({}, { a: value, b: { c: value } }); + return object.a === value && object.b.c === value; + }); + + expect(actual).toEqual(expected); + }); + + it('should clone buffer source values', () => { + if (Buffer) { + const buffer = Buffer.alloc([1]); + const actual = merge({}, { value: buffer }).value; + + expect(lodashStable.isBuffer(actual)) + expect(actual[0]).toBe(buffer[0]); + assert.notStrictEqual(actual, buffer); + } + }); + + it('should deep clone array/typed-array/plain-object source values', () => { + const typedArray = Uint8Array ? new Uint8Array([1]) : { buffer: [1] }; + + const props = ['0', 'buffer', 'a']; + const values = [[{ a: 1 }], typedArray, { a: [1] }]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value, index) => { + const key = props[index]; + const object = merge({}, { value: value }); + const subValue = value[key]; + const newValue = object.value; + const newSubValue = newValue[key]; + + return ( + newValue !== value && + newSubValue !== subValue && + lodashStable.isEqual(newValue, value) + ); + }); + + expect(actual).toEqual(expected); + }); + + it('should not augment source objects', () => { + var source1 = { a: [{ a: 1 }] }; + var source2 = { a: [{ b: 2 }] }; + var actual = merge({}, source1, source2); + + expect(source1.a).toEqual([{ a: 1 }]); + expect(source2.a).toEqual([{ b: 2 }]); + expect(actual.a, [{ a: 1).toEqual(b: 2 }]); + + var source1 = { a: [[1, 2, 3]] }; + var source2 = { a: [[3, 4]] }; + var actual = merge({}, source1, source2); + + expect(source1.a, [[1, 2).toEqual(3]]); + expect(source2.a, [[3).toEqual(4]]); + expect(actual.a, [[3, 4).toEqual(3]]); + }); + + it('should merge plain objects onto non-plain objects', () => { + function Foo(object) { + lodashStable.assign(this, object); + } + + const object = { a: 1 }; + let actual = merge(new Foo(), object); + + expect(actual instanceof Foo) + expect(actual).toEqual(new Foo(object)); + + actual = merge([new Foo()], [object]); + expect(actual[0] instanceof Foo) + expect(actual).toEqual([new Foo(object)]); + }); + + it('should not overwrite existing values with `undefined` values of object sources', () => { + const actual = merge({ a: 1 }, { a: undefined, b: undefined }); + expect(actual, { a: 1).toEqual(b: undefined }); + }); + + it('should not overwrite existing values with `undefined` values of array sources', () => { + let array = [1]; + array[2] = 3; + + let actual = merge([4, 5, 6], array); + const expected = [1, 5, 3]; + + expect(actual).toEqual(expected); + + array = [1, , 3]; + array[1] = undefined; + + actual = merge([4, 5, 6], array); + expect(actual).toEqual(expected); + }); + + it('should skip merging when `object` and `source` are the same value', () => { + const object = {}; + let pass = true; + + defineProperty(object, 'a', { + configurable: true, + enumerable: true, + get: function () { + pass = false; + }, + set: function () { + pass = false; + }, + }); + + merge(object, object); + expect(pass) + }); + + it('should convert values to arrays when merging arrays of `source`', () => { + const object = { a: { 1: 'y', b: 'z', length: 2 } }; + let actual = merge(object, { a: ['x'] }); + + expect(actual, { a: ['x').toEqual('y'] }); + + actual = merge({ a: {} }, { a: [] }); + expect(actual).toEqual({ a: [] }); + }); + + it('should convert strings to arrays when merging arrays of `source`', () => { + const object = { a: 'abcde' }; + const actual = merge(object, { a: ['x', 'y', 'z'] }); + + expect(actual, { a: ['x', 'y').toEqual('z'] }); + }); + + it('should not error on DOM elements', () => { + const object1 = { el: document && document.createElement('div') }; + const object2 = { el: document && document.createElement('div') }; + const pairs = [ + [{}, object1], + [object1, object2], + ]; + const expected = lodashStable.map(pairs, stubTrue); + + const actual = lodashStable.map(pairs, (pair) => { + try { + return merge(pair[0], pair[1]).el === pair[1].el; + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/merge.spec.ts b/test/merge.spec.ts new file mode 100644 index 0000000000..82b491c9a3 --- /dev/null +++ b/test/merge.spec.ts @@ -0,0 +1,353 @@ +import lodashStable from 'lodash'; +import { args, typedArrays, stubTrue, defineProperty, document, root } from './utils'; +import merge from '../src/merge'; +import isArguments from '../src/isArguments'; + +describe('merge', () => { + it('should merge `source` into `object`', () => { + const names = { + characters: [{ name: 'barney' }, { name: 'fred' }], + }; + + const ages = { + characters: [{ age: 36 }, { age: 40 }], + }; + + const heights = { + characters: [{ height: '5\'4"' }, { height: '5\'5"' }], + }; + + const expected = { + characters: [ + { name: 'barney', age: 36, height: '5\'4"' }, + { name: 'fred', age: 40, height: '5\'5"' }, + ], + }; + + expect(merge(names, ages, heights)).toEqual(expected); + }); + + it('should merge sources containing circular references', () => { + const object = { + foo: { a: 1 }, + bar: { a: 2 }, + }; + + const source = { + foo: { b: { c: { d: {} } } }, + bar: {}, + }; + + source.foo.b.c.d = source; + source.bar.b = source.foo.b; + + const actual = merge(object, source); + + assert.notStrictEqual(actual.bar.b, actual.foo.b); + expect(actual.foo.b.c.d).toBe(actual.foo.b.c.d.foo.b.c.d); + }); + + it('should work with four arguments', () => { + const expected = { a: 4 }, + actual = merge({ a: 1 }, { a: 2 }, { a: 3 }, expected); + + expect(actual).toEqual(expected); + }); + + it('should merge onto function `object` values', () => { + function Foo() {} + + const source = { a: 1 }, + actual = merge(Foo, source); + + expect(actual).toBe(Foo); + expect(Foo.a).toBe(1); + }); + + it('should merge first source object properties to function', () => { + const fn = function () {}, + object = { prop: {} }, + actual = merge({ prop: fn }, object); + + expect(actual).toEqual(object); + }); + + it('should merge first and second source object properties to function', () => { + const fn = function () {}, + object = { prop: {} }, + actual = merge({ prop: fn }, { prop: fn }, object); + + expect(actual).toEqual(object); + }); + + it('should not merge onto function values of sources', () => { + let source1 = { a: function () {} }, + source2 = { a: { b: 2 } }, + expected = { a: { b: 2 } }, + actual = merge({}, source1, source2); + + expect(actual).toEqual(expected); + expect(('b' in source1.a)).toBe(false) + + actual = merge(source1, source2); + expect(actual).toEqual(expected); + }); + + it('should merge onto non-plain `object` values', () => { + function Foo() {} + + const object = new Foo(), + actual = merge(object, { a: 1 }); + + expect(actual).toBe(object); + expect(object.a).toBe(1); + }); + + // TODO: revisit. + it.skip('should treat sparse array sources as dense', () => { + const array = [1]; + array[2] = 3; + + const actual = merge([], array), + expected = array.slice(); + + expected[1] = undefined; + + expect('1' in actual) + expect(actual).toEqual(expected); + }); + + it('should merge `arguments` objects', () => { + let object1 = { value: args }, + object2 = { value: { '3': 4 } }, + expected = { '0': 1, '1': 2, '2': 3, '3': 4 }, + actual = merge(object1, object2); + + expect(('3' in args)).toBe(false) + expect(isArguments(actual.value)).toBe(false) + expect(actual.value).toEqual(expected); + object1.value = args; + + actual = merge(object2, object1); + expect(isArguments(actual.value)).toBe(false) + expect(actual.value).toEqual(expected); + + expected = { '0': 1, '1': 2, '2': 3 }; + + actual = merge({}, object1); + expect(isArguments(actual.value)).toBe(false) + expect(actual.value).toEqual(expected); + }); + + it('should merge typed arrays', () => { + const array1 = [0], + array2 = [0, 0], + array3 = [0, 0, 0, 0], + array4 = [0, 0, 0, 0, 0, 0, 0, 0]; + + const arrays = [array2, array1, array4, array3, array2, array4, array4, array3, array2], + buffer = ArrayBuffer && new ArrayBuffer(8); + + let expected = lodashStable.map(typedArrays, (type, index) => { + const array = arrays[index].slice(); + array[0] = 1; + return root[type] ? { value: array } : false; + }); + + let actual = lodashStable.map(typedArrays, (type) => { + const Ctor = root[type]; + return Ctor ? merge({ value: new Ctor(buffer) }, { value: [1] }) : false; + }); + + expect(lodashStable.isArray(actual)) + expect(actual).toEqual(expected); + + expected = lodashStable.map(typedArrays, (type, index) => { + const array = arrays[index].slice(); + array.push(1); + return root[type] ? { value: array } : false; + }); + + actual = lodashStable.map(typedArrays, (type, index) => { + const Ctor = root[type], + array = lodashStable.range(arrays[index].length); + + array.push(1); + return Ctor ? merge({ value: array }, { value: new Ctor(buffer) }) : false; + }); + + expect(lodashStable.isArray(actual)) + expect(actual).toEqual(expected); + }); + + it('should assign `null` values', () => { + const actual = merge({ a: 1 }, { a: null }); + expect(actual.a).toBe(null); + }); + + it('should assign non array/buffer/typed-array/plain-object source values directly', () => { + function Foo() {} + + const values = [ + new Foo(), + new Boolean(), + new Date(), + Foo, + new Number(), + new String(), + new RegExp(), + ], + expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => { + const object = merge({}, { a: value, b: { c: value } }); + return object.a === value && object.b.c === value; + }); + + expect(actual).toEqual(expected); + }); + + it('should clone buffer source values', () => { + if (Buffer) { + const buffer = Buffer.alloc([1]), + actual = merge({}, { value: buffer }).value; + + expect(lodashStable.isBuffer(actual)) + expect(actual[0]).toBe(buffer[0]); + assert.notStrictEqual(actual, buffer); + } + }); + + it('should deep clone array/typed-array/plain-object source values', () => { + const typedArray = Uint8Array ? new Uint8Array([1]) : { buffer: [1] }; + + const props = ['0', 'buffer', 'a'], + values = [[{ a: 1 }], typedArray, { a: [1] }], + expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value, index) => { + const key = props[index], + object = merge({}, { value: value }), + subValue = value[key], + newValue = object.value, + newSubValue = newValue[key]; + + return ( + newValue !== value && + newSubValue !== subValue && + lodashStable.isEqual(newValue, value) + ); + }); + + expect(actual).toEqual(expected); + }); + + it('should not augment source objects', () => { + var source1 = { a: [{ a: 1 }] }, + source2 = { a: [{ b: 2 }] }, + actual = merge({}, source1, source2); + + expect(source1.a).toEqual([{ a: 1 }]); + expect(source2.a).toEqual([{ b: 2 }]); + expect(actual.a).toEqual([{ a: 1, b: 2 }]); + + var source1 = { a: [[1, 2, 3]] }, + source2 = { a: [[3, 4]] }, + actual = merge({}, source1, source2); + + expect(source1.a).toEqual([[1, 2, 3]]); + expect(source2.a).toEqual([[3, 4]]); + expect(actual.a).toEqual([[3, 4, 3]]); + }); + + it('should merge plain objects onto non-plain objects', () => { + function Foo(object) { + lodashStable.assign(this, object); + } + + let object = { a: 1 }, + actual = merge(new Foo(), object); + + expect(actual instanceof Foo) + expect(actual).toEqual(new Foo(object)); + + actual = merge([new Foo()], [object]); + expect(actual[0] instanceof Foo) + expect(actual).toEqual([new Foo(object)]); + }); + + it('should not overwrite existing values with `undefined` values of object sources', () => { + const actual = merge({ a: 1 }, { a: undefined, b: undefined }); + expect(actual).toEqual({ a: 1, b: undefined }); + }); + + it('should not overwrite existing values with `undefined` values of array sources', () => { + let array = [1]; + array[2] = 3; + + let actual = merge([4, 5, 6], array), + expected = [1, 5, 3]; + + expect(actual).toEqual(expected); + + array = [1, , 3]; + array[1] = undefined; + + actual = merge([4, 5, 6], array); + expect(actual).toEqual(expected); + }); + + it('should skip merging when `object` and `source` are the same value', () => { + let object = {}, + pass = true; + + defineProperty(object, 'a', { + configurable: true, + enumerable: true, + get: function () { + pass = false; + }, + set: function () { + pass = false; + }, + }); + + merge(object, object); + expect(pass) + }); + + it('should convert values to arrays when merging arrays of `source`', () => { + let object = { a: { '1': 'y', b: 'z', length: 2 } }; + let actual = merge(object, { a: ['x'] }); + + expect(actual).toEqual({ a: ['x', 'y'] }); + + actual = merge({ a: {} }, { a: [] }); + expect(actual).toEqual({ a: [] }); + }); + + it('should convert strings to arrays when merging arrays of `source`', () => { + const object = { a: 'abcde' }; + const actual = merge(object, { a: ['x', 'y', 'z'] }); + + expect(actual).toEqual({ a: ['x', 'y', 'z'] }); + }); + + it('should not error on DOM elements', () => { + const object1 = { el: document && document.createElement('div') }, + object2 = { el: document && document.createElement('div') }, + pairs = [ + [{}, object1], + [object1, object2], + ], + expected = lodashStable.map(pairs, stubTrue); + + const actual = lodashStable.map(pairs, (pair) => { + try { + return merge(pair[0], pair[1]).el === pair[1].el; + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/merge.test.js b/test/merge.test.js deleted file mode 100644 index 397a9aafad..0000000000 --- a/test/merge.test.js +++ /dev/null @@ -1,350 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, typedArrays, stubTrue, defineProperty, document, root } from './utils.js'; -import merge from '../merge.js'; -import isArguments from '../isArguments.js'; - -describe('merge', function() { - it('should merge `source` into `object`', function() { - var names = { - 'characters': [ - { 'name': 'barney' }, - { 'name': 'fred' } - ] - }; - - var ages = { - 'characters': [ - { 'age': 36 }, - { 'age': 40 } - ] - }; - - var heights = { - 'characters': [ - { 'height': '5\'4"' }, - { 'height': '5\'5"' } - ] - }; - - var expected = { - 'characters': [ - { 'name': 'barney', 'age': 36, 'height': '5\'4"' }, - { 'name': 'fred', 'age': 40, 'height': '5\'5"' } - ] - }; - - assert.deepStrictEqual(merge(names, ages, heights), expected); - }); - - it('should merge sources containing circular references', function() { - var object = { - 'foo': { 'a': 1 }, - 'bar': { 'a': 2 } - }; - - var source = { - 'foo': { 'b': { 'c': { 'd': {} } } }, - 'bar': {} - }; - - source.foo.b.c.d = source; - source.bar.b = source.foo.b; - - var actual = merge(object, source); - - assert.notStrictEqual(actual.bar.b, actual.foo.b); - assert.strictEqual(actual.foo.b.c.d, actual.foo.b.c.d.foo.b.c.d); - }); - - it('should work with four arguments', function() { - var expected = { 'a': 4 }, - actual = merge({ 'a': 1 }, { 'a': 2 }, { 'a': 3 }, expected); - - assert.deepStrictEqual(actual, expected); - }); - - it('should merge onto function `object` values', function() { - function Foo() {} - - var source = { 'a': 1 }, - actual = merge(Foo, source); - - assert.strictEqual(actual, Foo); - assert.strictEqual(Foo.a, 1); - }); - - it('should merge first source object properties to function', function() { - var fn = function() {}, - object = { 'prop': {} }, - actual = merge({ 'prop': fn }, object); - - assert.deepStrictEqual(actual, object); - }); - - it('should merge first and second source object properties to function', function() { - var fn = function() {}, - object = { 'prop': {} }, - actual = merge({ 'prop': fn }, { 'prop': fn }, object); - - assert.deepStrictEqual(actual, object); - }); - - it('should not merge onto function values of sources', function() { - var source1 = { 'a': function() {} }, - source2 = { 'a': { 'b': 2 } }, - expected = { 'a': { 'b': 2 } }, - actual = merge({}, source1, source2); - - assert.deepStrictEqual(actual, expected); - assert.ok(!('b' in source1.a)); - - actual = merge(source1, source2); - assert.deepStrictEqual(actual, expected); - }); - - it('should merge onto non-plain `object` values', function() { - function Foo() {} - - var object = new Foo, - actual = merge(object, { 'a': 1 }); - - assert.strictEqual(actual, object); - assert.strictEqual(object.a, 1); - }); - - // TODO: revisit. - it.skip('should treat sparse array sources as dense', function() { - var array = [1]; - array[2] = 3; - - var actual = merge([], array), - expected = array.slice(); - - expected[1] = undefined; - - assert.ok('1' in actual); - assert.deepStrictEqual(actual, expected); - }); - - it('should merge `arguments` objects', function() { - var object1 = { 'value': args }, - object2 = { 'value': { '3': 4 } }, - expected = { '0': 1, '1': 2, '2': 3, '3': 4 }, - actual = merge(object1, object2); - - assert.ok(!('3' in args)); - assert.ok(!isArguments(actual.value)); - assert.deepStrictEqual(actual.value, expected); - object1.value = args; - - actual = merge(object2, object1); - assert.ok(!isArguments(actual.value)); - assert.deepStrictEqual(actual.value, expected); - - expected = { '0': 1, '1': 2, '2': 3 }; - - actual = merge({}, object1); - assert.ok(!isArguments(actual.value)); - assert.deepStrictEqual(actual.value, expected); - }); - - it('should merge typed arrays', function() { - var array1 = [0], - array2 = [0, 0], - array3 = [0, 0, 0, 0], - array4 = [0, 0, 0, 0, 0, 0, 0, 0]; - - var arrays = [array2, array1, array4, array3, array2, array4, array4, array3, array2], - buffer = ArrayBuffer && new ArrayBuffer(8); - - var expected = lodashStable.map(typedArrays, function(type, index) { - var array = arrays[index].slice(); - array[0] = 1; - return root[type] ? { 'value': array } : false; - }); - - var actual = lodashStable.map(typedArrays, function(type) { - var Ctor = root[type]; - return Ctor ? merge({ 'value': new Ctor(buffer) }, { 'value': [1] }) : false; - }); - - assert.ok(lodashStable.isArray(actual)); - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(typedArrays, function(type, index) { - var array = arrays[index].slice(); - array.push(1); - return root[type] ? { 'value': array } : false; - }); - - actual = lodashStable.map(typedArrays, function(type, index) { - var Ctor = root[type], - array = lodashStable.range(arrays[index].length); - - array.push(1); - return Ctor ? merge({ 'value': array }, { 'value': new Ctor(buffer) }) : false; - }); - - assert.ok(lodashStable.isArray(actual)); - assert.deepStrictEqual(actual, expected); - }); - - it('should assign `null` values', function() { - var actual = merge({ 'a': 1 }, { 'a': null }); - assert.strictEqual(actual.a, null); - }); - - it('should assign non array/buffer/typed-array/plain-object source values directly', function() { - function Foo() {} - - var values = [new Foo, new Boolean, new Date, Foo, new Number, new String, new RegExp], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - var object = merge({}, { 'a': value, 'b': { 'c': value } }); - return object.a === value && object.b.c === value; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should clone buffer source values', function() { - if (Buffer) { - var buffer = new Buffer([1]), - actual = merge({}, { 'value': buffer }).value; - - assert.ok(lodashStable.isBuffer(actual)); - assert.strictEqual(actual[0], buffer[0]); - assert.notStrictEqual(actual, buffer); - } - }); - - it('should deep clone array/typed-array/plain-object source values', function() { - var typedArray = Uint8Array - ? new Uint8Array([1]) - : { 'buffer': [1] }; - - var props = ['0', 'buffer', 'a'], - values = [[{ 'a': 1 }], typedArray, { 'a': [1] }], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value, index) { - var key = props[index], - object = merge({}, { 'value': value }), - subValue = value[key], - newValue = object.value, - newSubValue = newValue[key]; - - return ( - newValue !== value && - newSubValue !== subValue && - lodashStable.isEqual(newValue, value) - ); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should not augment source objects', function() { - var source1 = { 'a': [{ 'a': 1 }] }, - source2 = { 'a': [{ 'b': 2 }] }, - actual = merge({}, source1, source2); - - assert.deepStrictEqual(source1.a, [{ 'a': 1 }]); - assert.deepStrictEqual(source2.a, [{ 'b': 2 }]); - assert.deepStrictEqual(actual.a, [{ 'a': 1, 'b': 2 }]); - - var source1 = { 'a': [[1, 2, 3]] }, - source2 = { 'a': [[3, 4]] }, - actual = merge({}, source1, source2); - - assert.deepStrictEqual(source1.a, [[1, 2, 3]]); - assert.deepStrictEqual(source2.a, [[3, 4]]); - assert.deepStrictEqual(actual.a, [[3, 4, 3]]); - }); - - it('should merge plain objects onto non-plain objects', function() { - function Foo(object) { - lodashStable.assign(this, object); - } - - var object = { 'a': 1 }, - actual = merge(new Foo, object); - - assert.ok(actual instanceof Foo); - assert.deepStrictEqual(actual, new Foo(object)); - - actual = merge([new Foo], [object]); - assert.ok(actual[0] instanceof Foo); - assert.deepStrictEqual(actual, [new Foo(object)]); - }); - - it('should not overwrite existing values with `undefined` values of object sources', function() { - var actual = merge({ 'a': 1 }, { 'a': undefined, 'b': undefined }); - assert.deepStrictEqual(actual, { 'a': 1, 'b': undefined }); - }); - - it('should not overwrite existing values with `undefined` values of array sources', function() { - var array = [1]; - array[2] = 3; - - var actual = merge([4, 5, 6], array), - expected = [1, 5, 3]; - - assert.deepStrictEqual(actual, expected); - - array = [1, , 3]; - array[1] = undefined; - - actual = merge([4, 5, 6], array); - assert.deepStrictEqual(actual, expected); - }); - - it('should skip merging when `object` and `source` are the same value', function() { - var object = {}, - pass = true; - - defineProperty(object, 'a', { - 'configurable': true, - 'enumerable': true, - 'get': function() { pass = false; }, - 'set': function() { pass = false; } - }); - - merge(object, object); - assert.ok(pass); - }); - - it('should convert values to arrays when merging arrays of `source`', function() { - var object = { 'a': { '1': 'y', 'b': 'z', 'length': 2 } }, - actual = merge(object, { 'a': ['x'] }); - - assert.deepStrictEqual(actual, { 'a': ['x', 'y'] }); - - actual = merge({ 'a': {} }, { 'a': [] }); - assert.deepStrictEqual(actual, { 'a': [] }); - }); - - it('should convert strings to arrays when merging arrays of `source`', function() { - var object = { 'a': 'abcde' }, - actual = merge(object, { 'a': ['x', 'y', 'z'] }); - - assert.deepStrictEqual(actual, { 'a': ['x', 'y', 'z'] }); - }); - - it('should not error on DOM elements', function() { - var object1 = { 'el': document && document.createElement('div') }, - object2 = { 'el': document && document.createElement('div') }, - pairs = [[{}, object1], [object1, object2]], - expected = lodashStable.map(pairs, stubTrue); - - var actual = lodashStable.map(pairs, function(pair) { - try { - return merge(pair[0], pair[1]).el === pair[1].el; - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/mergeWith.js b/test/mergeWith.js deleted file mode 100644 index 45f10a9079..0000000000 --- a/test/mergeWith.js +++ /dev/null @@ -1,64 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop, identity, isNpm, mapCaches } from './utils.js'; -import mergeWith from '../mergeWith.js'; -import last from '../last.js'; - -describe('mergeWith', function() { - it('should handle merging when `customizer` returns `undefined`', function() { - var actual = mergeWith({ 'a': { 'b': [1, 1] } }, { 'a': { 'b': [0] } }, noop); - assert.deepStrictEqual(actual, { 'a': { 'b': [0, 1] } }); - - actual = mergeWith([], [undefined], identity); - assert.deepStrictEqual(actual, [undefined]); - }); - - it('should clone sources when `customizer` returns `undefined`', function() { - var source1 = { 'a': { 'b': { 'c': 1 } } }, - source2 = { 'a': { 'b': { 'd': 2 } } }; - - mergeWith({}, source1, source2, noop); - assert.deepStrictEqual(source1.a.b, { 'c': 1 }); - }); - - it('should defer to `customizer` for non `undefined` results', function() { - var actual = mergeWith({ 'a': { 'b': [0, 1] } }, { 'a': { 'b': [2] } }, function(a, b) { - return lodashStable.isArray(a) ? a.concat(b) : undefined; - }); - - assert.deepStrictEqual(actual, { 'a': { 'b': [0, 1, 2] } }); - }); - - it('should provide `stack` to `customizer`', function() { - var actual; - - mergeWith({}, { 'a': { 'b': 2 } }, function() { - actual = last(arguments); - }); - - assert.ok(isNpm - ? actual.constructor.name == 'Stack' - : actual instanceof mapCaches.Stack - ); - }); - - it('should overwrite primitives with source object clones', function() { - var actual = mergeWith({ 'a': 0 }, { 'a': { 'b': ['c'] } }, function(a, b) { - return lodashStable.isArray(a) ? a.concat(b) : undefined; - }); - - assert.deepStrictEqual(actual, { 'a': { 'b': ['c'] } }); - }); - - it('should pop the stack of sources for each sibling property', function() { - var array = ['b', 'c'], - object = { 'a': ['a'] }, - source = { 'a': array, 'b': array }; - - var actual = mergeWith(object, source, function(a, b) { - return lodashStable.isArray(a) ? a.concat(b) : undefined; - }); - - assert.deepStrictEqual(actual, { 'a': ['a', 'b', 'c'], 'b': ['b', 'c'] }); - }); -}); diff --git a/test/mergeWith.spec.js b/test/mergeWith.spec.js new file mode 100644 index 0000000000..997c81cd9e --- /dev/null +++ b/test/mergeWith.spec.js @@ -0,0 +1,60 @@ +import lodashStable from 'lodash'; +import { noop, identity, isNpm, mapCaches } from './utils'; +import mergeWith from '../src/mergeWith'; +import last from '../src/last'; + +describe('mergeWith', () => { + it('should handle merging when `customizer` returns `undefined`', () => { + let actual = mergeWith({ a: { b: [1, 1] } }, { a: { b: [0] } }, noop); + expect(actual).toEqual({ a: { b: [0, 1] } }); + + actual = mergeWith([], [undefined], identity); + expect(actual).toEqual([undefined]); + }); + + it('should clone sources when `customizer` returns `undefined`', () => { + const source1 = { a: { b: { c: 1 } } }; + const source2 = { a: { b: { d: 2 } } }; + + mergeWith({}, source1, source2, noop); + expect(source1.a.b).toEqual({ c: 1 }); + }); + + it('should defer to `customizer` for non `undefined` results', () => { + const actual = mergeWith({ a: { b: [0, 1] } }, { a: { b: [2] } }, (a, b) => + lodashStable.isArray(a) ? a.concat(b) : undefined, + ); + + expect(actual).toEqual({ a: { b: [0, 1, 2] } }); + }); + + it('should provide `stack` to `customizer`', () => { + let actual; + + mergeWith({}, { a: { b: 2 } }, function () { + actual = last(arguments); + }); + + expect(isNpm ? actual.constructor.name === 'Stack' : actual instanceof mapCaches.Stack) + }); + + it('should overwrite primitives with source object clones', () => { + const actual = mergeWith({ a: 0 }, { a: { b: ['c'] } }, (a, b) => + lodashStable.isArray(a) ? a.concat(b) : undefined, + ); + + expect(actual).toEqual({ a: { b: ['c'] } }); + }); + + it('should pop the stack of sources for each sibling property', () => { + const array = ['b', 'c']; + const object = { a: ['a'] }; + const source = { a: array, b: array }; + + const actual = mergeWith(object, source, (a, b) => + lodashStable.isArray(a) ? a.concat(b) : undefined, + ); + + expect(actual).toEqual({ a: ['a', 'b', 'c'], b: ['b', 'c'] }); + }); +}); diff --git a/test/method.js b/test/method.js deleted file mode 100644 index 617c89105b..0000000000 --- a/test/method.js +++ /dev/null @@ -1,132 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubOne, _, stubTwo, stubThree, stubFour, noop, slice } from './utils.js'; -import constant from '../constant.js'; - -describe('method', function() { - it('should create a function that calls a method of a given object', function() { - var object = { 'a': stubOne }; - - lodashStable.each(['a', ['a']], function(path) { - var method = _.method(path); - assert.strictEqual(method.length, 1); - assert.strictEqual(method(object), 1); - }); - }); - - it('should work with deep property values', function() { - var object = { 'a': { 'b': stubTwo } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var method = _.method(path); - assert.strictEqual(method(object), 2); - }); - }); - - it('should work with a non-string `path`', function() { - var array = lodashStable.times(3, constant); - - lodashStable.each([1, [1]], function(path) { - var method = _.method(path); - assert.strictEqual(method(array), 1); - }); - }); - - it('should coerce `path` to a string', function() { - function fn() {} - fn.toString = lodashStable.constant('fn'); - - var expected = [1, 2, 3, 4], - object = { 'null': stubOne, 'undefined': stubTwo, 'fn': stubThree, '[object Object]': stubFour }, - paths = [null, undefined, fn, {}]; - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(paths, function(path) { - var method = _.method(index ? [path] : path); - return method(object); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should work with inherited property values', function() { - function Foo() {} - Foo.prototype.a = stubOne; - - lodashStable.each(['a', ['a']], function(path) { - var method = _.method(path); - assert.strictEqual(method(new Foo), 1); - }); - }); - - it('should use a key over a path', function() { - var object = { 'a.b': stubOne, 'a': { 'b': stubTwo } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - var method = _.method(path); - assert.strictEqual(method(object), 1); - }); - }); - - it('should return `undefined` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor', ['constructor']], function(path) { - var method = _.method(path); - - var actual = lodashStable.map(values, function(value, index) { - return index ? method(value) : method(); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` for deep paths when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) { - var method = _.method(path); - - var actual = lodashStable.map(values, function(value, index) { - return index ? method(value) : method(); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` if parts of `path` are missing', function() { - var object = {}; - - lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) { - var method = _.method(path); - assert.strictEqual(method(object), undefined); - }); - }); - - it('should apply partial arguments to function', function() { - var object = { - 'fn': function() { - return slice.call(arguments); - } - }; - - lodashStable.each(['fn', ['fn']], function(path) { - var method = _.method(path, 1, 2, 3); - assert.deepStrictEqual(method(object), [1, 2, 3]); - }); - }); - - it('should invoke deep property methods with the correct `this` binding', function() { - var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var method = _.method(path); - assert.strictEqual(method(object), 1); - }); - }); -}); diff --git a/test/method.spec.js b/test/method.spec.js new file mode 100644 index 0000000000..c7cd76948c --- /dev/null +++ b/test/method.spec.js @@ -0,0 +1,146 @@ +import lodashStable from 'lodash'; +import { stubOne, _, stubTwo, stubThree, stubFour, noop, slice } from './utils'; +import constant from '../src/constant'; + +describe('method', () => { + it('should create a function that calls a method of a given object', () => { + const object = { a: stubOne }; + + lodashStable.each(['a', ['a']], (path) => { + const method = _.method(path); + expect(method.length).toBe(1); + expect(method(object)).toBe(1); + }); + }); + + it('should work with deep property values', () => { + const object = { a: { b: stubTwo } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const method = _.method(path); + expect(method(object)).toBe(2); + }); + }); + + it('should work with a non-string `path`', () => { + const array = lodashStable.times(3, constant); + + lodashStable.each([1, [1]], (path) => { + const method = _.method(path); + expect(method(array)).toBe(1); + }); + }); + + it('should coerce `path` to a string', () => { + function fn() {} + fn.toString = lodashStable.constant('fn'); + + const expected = [1, 2, 3, 4]; + const object = { + null: stubOne, + undefined: stubTwo, + fn: stubThree, + '[object Object]': stubFour, + }; + const paths = [null, undefined, fn, {}]; + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(paths, (path) => { + const method = _.method(index ? [path] : path); + return method(object); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should work with inherited property values', () => { + function Foo() {} + Foo.prototype.a = stubOne; + + lodashStable.each(['a', ['a']], (path) => { + const method = _.method(path); + expect(method(new Foo())).toBe(1); + }); + }); + + it('should use a key over a path', () => { + const object = { 'a.b': stubOne, a: { b: stubTwo } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + const method = _.method(path); + expect(method(object)).toBe(1); + }); + }); + + it('should return `undefined` when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each(['constructor', ['constructor']], (path) => { + const method = _.method(path); + + const actual = lodashStable.map(values, (value, index) => + index ? method(value) : method(), + ); + + expect(actual).toEqual(expected); + }); + }); + + it('should return `undefined` for deep paths when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each( + ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], + (path) => { + const method = _.method(path); + + const actual = lodashStable.map(values, (value, index) => + index ? method(value) : method(), + ); + + expect(actual).toEqual(expected); + }, + ); + }); + + it('should return `undefined` if parts of `path` are missing', () => { + const object = {}; + + lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], (path) => { + const method = _.method(path); + expect(method(object)).toBe(undefined); + }); + }); + + it('should apply partial arguments to function', () => { + const object = { + fn: function () { + return slice.call(arguments); + }, + }; + + lodashStable.each(['fn', ['fn']], (path) => { + const method = _.method(path, 1, 2, 3); + expect(method(object), [1, 2).toEqual(3]); + }); + }); + + it('should invoke deep property methods with the correct `this` binding', () => { + const object = { + a: { + b: function () { + return this.c; + }, + c: 1, + }, + }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const method = _.method(path); + expect(method(object)).toBe(1); + }); + }); +}); diff --git a/test/methodOf.js b/test/methodOf.js deleted file mode 100644 index 29170a29f7..0000000000 --- a/test/methodOf.js +++ /dev/null @@ -1,131 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubOne, _, stubTwo, stubThree, stubFour, noop, slice } from './utils.js'; -import constant from '../constant.js'; - -describe('methodOf', function() { - it('should create a function that calls a method of a given key', function() { - var object = { 'a': stubOne }; - - lodashStable.each(['a', ['a']], function(path) { - var methodOf = _.methodOf(object); - assert.strictEqual(methodOf.length, 1); - assert.strictEqual(methodOf(path), 1); - }); - }); - - it('should work with deep property values', function() { - var object = { 'a': { 'b': stubTwo } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var methodOf = _.methodOf(object); - assert.strictEqual(methodOf(path), 2); - }); - }); - - it('should work with a non-string `path`', function() { - var array = lodashStable.times(3, constant); - - lodashStable.each([1, [1]], function(path) { - var methodOf = _.methodOf(array); - assert.strictEqual(methodOf(path), 1); - }); - }); - - it('should coerce `path` to a string', function() { - function fn() {} - fn.toString = lodashStable.constant('fn'); - - var expected = [1, 2, 3, 4], - object = { 'null': stubOne, 'undefined': stubTwo, 'fn': stubThree, '[object Object]': stubFour }, - paths = [null, undefined, fn, {}]; - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(paths, function(path) { - var methodOf = _.methodOf(object); - return methodOf(index ? [path] : path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should work with inherited property values', function() { - function Foo() {} - Foo.prototype.a = stubOne; - - lodashStable.each(['a', ['a']], function(path) { - var methodOf = _.methodOf(new Foo); - assert.strictEqual(methodOf(path), 1); - }); - }); - - it('should use a key over a path', function() { - var object = { 'a.b': stubOne, 'a': { 'b': stubTwo } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - var methodOf = _.methodOf(object); - assert.strictEqual(methodOf(path), 1); - }); - }); - - it('should return `undefined` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor', ['constructor']], function(path) { - var actual = lodashStable.map(values, function(value, index) { - var methodOf = index ? _.methodOf() : _.methodOf(value); - return methodOf(path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` for deep paths when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) { - var actual = lodashStable.map(values, function(value, index) { - var methodOf = index ? _.methodOf() : _.methodOf(value); - return methodOf(path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` if parts of `path` are missing', function() { - var object = {}, - methodOf = _.methodOf(object); - - lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) { - assert.strictEqual(methodOf(path), undefined); - }); - }); - - it('should apply partial arguments to function', function() { - var object = { - 'fn': function() { - return slice.call(arguments); - } - }; - - var methodOf = _.methodOf(object, 1, 2, 3); - - lodashStable.each(['fn', ['fn']], function(path) { - assert.deepStrictEqual(methodOf(path), [1, 2, 3]); - }); - }); - - it('should invoke deep property methods with the correct `this` binding', function() { - var object = { 'a': { 'b': function() { return this.c; }, 'c': 1 } }, - methodOf = _.methodOf(object); - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(methodOf(path), 1); - }); - }); -}); diff --git a/test/methodOf.spec.js b/test/methodOf.spec.js new file mode 100644 index 0000000000..7dabec6cfd --- /dev/null +++ b/test/methodOf.spec.js @@ -0,0 +1,145 @@ +import lodashStable from 'lodash'; +import { stubOne, _, stubTwo, stubThree, stubFour, noop, slice } from './utils'; +import constant from '../src/constant'; + +describe('methodOf', () => { + it('should create a function that calls a method of a given key', () => { + const object = { a: stubOne }; + + lodashStable.each(['a', ['a']], (path) => { + const methodOf = _.methodOf(object); + expect(methodOf.length).toBe(1); + expect(methodOf(path)).toBe(1); + }); + }); + + it('should work with deep property values', () => { + const object = { a: { b: stubTwo } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const methodOf = _.methodOf(object); + expect(methodOf(path)).toBe(2); + }); + }); + + it('should work with a non-string `path`', () => { + const array = lodashStable.times(3, constant); + + lodashStable.each([1, [1]], (path) => { + const methodOf = _.methodOf(array); + expect(methodOf(path)).toBe(1); + }); + }); + + it('should coerce `path` to a string', () => { + function fn() {} + fn.toString = lodashStable.constant('fn'); + + const expected = [1, 2, 3, 4]; + const object = { + null: stubOne, + undefined: stubTwo, + fn: stubThree, + '[object Object]': stubFour, + }; + const paths = [null, undefined, fn, {}]; + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(paths, (path) => { + const methodOf = _.methodOf(object); + return methodOf(index ? [path] : path); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should work with inherited property values', () => { + function Foo() {} + Foo.prototype.a = stubOne; + + lodashStable.each(['a', ['a']], (path) => { + const methodOf = _.methodOf(new Foo()); + expect(methodOf(path)).toBe(1); + }); + }); + + it('should use a key over a path', () => { + const object = { 'a.b': stubOne, a: { b: stubTwo } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + const methodOf = _.methodOf(object); + expect(methodOf(path)).toBe(1); + }); + }); + + it('should return `undefined` when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each(['constructor', ['constructor']], (path) => { + const actual = lodashStable.map(values, (value, index) => { + const methodOf = index ? _.methodOf() : _.methodOf(value); + return methodOf(path); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should return `undefined` for deep paths when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each( + ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], + (path) => { + const actual = lodashStable.map(values, (value, index) => { + const methodOf = index ? _.methodOf() : _.methodOf(value); + return methodOf(path); + }); + + expect(actual).toEqual(expected); + }, + ); + }); + + it('should return `undefined` if parts of `path` are missing', () => { + const object = {}; + const methodOf = _.methodOf(object); + + lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], (path) => { + expect(methodOf(path)).toBe(undefined); + }); + }); + + it('should apply partial arguments to function', () => { + const object = { + fn: function () { + return slice.call(arguments); + }, + }; + + const methodOf = _.methodOf(object, 1, 2, 3); + + lodashStable.each(['fn', ['fn']], (path) => { + expect(methodOf(path)).toEqual([1, 2, 3]); + }); + }); + + it('should invoke deep property methods with the correct `this` binding', () => { + const object = { + a: { + b: function () { + return this.c; + }, + c: 1, + }, + }; + const methodOf = _.methodOf(object); + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(methodOf(path)).toBe(1); + }); + }); +}); diff --git a/test/methods-using-createWrapper.js b/test/methods-using-createWrapper.js deleted file mode 100644 index df44aa770e..0000000000 --- a/test/methods-using-createWrapper.js +++ /dev/null @@ -1,198 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, _, push, HOT_COUNT } from './utils.js'; -import bind from '../bind.js'; -import bindKey from '../bindKey.js'; -import partial from '../partial.js'; -import partialRight from '../partialRight.js'; -import last from '../last.js'; - -describe('methods using `createWrapper`', function() { - function fn() { - return slice.call(arguments); - } - - var ph1 = bind.placeholder, - ph2 = bindKey.placeholder, - ph3 = partial.placeholder, - ph4 = partialRight.placeholder; - - it('should work with combinations of partial functions', function() { - var a = partial(fn), - b = partialRight(a, 3), - c = partial(b, 1); - - assert.deepStrictEqual(c(2), [1, 2, 3]); - }); - - it('should work with combinations of bound and partial functions', function() { - var fn = function() { - var result = [this.a]; - push.apply(result, arguments); - return result; - }; - - var expected = [1, 2, 3, 4], - object = { 'a': 1, 'fn': fn }; - - var a = bindKey(object, 'fn'), - b = partialRight(a, 4), - c = partial(b, 2); - - assert.deepStrictEqual(c(3), expected); - - a = bind(fn, object); - b = partialRight(a, 4); - c = partial(b, 2); - - assert.deepStrictEqual(c(3), expected); - - a = partial(fn, 2); - b = bind(a, object); - c = partialRight(b, 4); - - assert.deepStrictEqual(c(3), expected); - }); - - it('should ensure `new combo` is an instance of `func`', function() { - function Foo(a, b, c) { - return b === 0 && object; - } - - var combo = partial(partialRight(Foo, 3), 1), - object = {}; - - assert.ok(new combo(2) instanceof Foo); - assert.strictEqual(new combo(0), object); - }); - - it('should work with combinations of functions with placeholders', function() { - var expected = [1, 2, 3, 4, 5, 6], - object = { 'fn': fn }; - - var a = bindKey(object, 'fn', ph2, 2), - b = partialRight(a, ph4, 6), - c = partial(b, 1, ph3, 4); - - assert.deepStrictEqual(c(3, 5), expected); - - a = bind(fn, object, ph1, 2); - b = partialRight(a, ph4, 6); - c = partial(b, 1, ph3, 4); - - assert.deepStrictEqual(c(3, 5), expected); - - a = partial(fn, ph3, 2); - b = bind(a, object, 1, ph1, 4); - c = partialRight(b, ph4, 6); - - assert.deepStrictEqual(c(3, 5), expected); - }); - - it('should work with combinations of functions with overlapping placeholders', function() { - var expected = [1, 2, 3, 4], - object = { 'fn': fn }; - - var a = bindKey(object, 'fn', ph2, 2), - b = partialRight(a, ph4, 4), - c = partial(b, ph3, 3); - - assert.deepStrictEqual(c(1), expected); - - a = bind(fn, object, ph1, 2); - b = partialRight(a, ph4, 4); - c = partial(b, ph3, 3); - - assert.deepStrictEqual(c(1), expected); - - a = partial(fn, ph3, 2); - b = bind(a, object, ph1, 3); - c = partialRight(b, ph4, 4); - - assert.deepStrictEqual(c(1), expected); - }); - - it('should work with recursively bound functions', function() { - var fn = function() { - return this.a; - }; - - var a = bind(fn, { 'a': 1 }), - b = bind(a, { 'a': 2 }), - c = bind(b, { 'a': 3 }); - - assert.strictEqual(c(), 1); - }); - - it('should work when hot', function() { - lodashStable.times(2, function(index) { - var fn = function() { - var result = [this]; - push.apply(result, arguments); - return result; - }; - - var object = {}, - bound1 = index ? bind(fn, object, 1) : bind(fn, object), - expected = [object, 1, 2, 3]; - - var actual = last(lodashStable.times(HOT_COUNT, function() { - var bound2 = index ? bind(bound1, null, 2) : bind(bound1); - return index ? bound2(3) : bound2(1, 2, 3); - })); - - assert.deepStrictEqual(actual, expected); - - actual = last(lodashStable.times(HOT_COUNT, function() { - var bound1 = index ? bind(fn, object, 1) : bind(fn, object), - bound2 = index ? bind(bound1, null, 2) : bind(bound1); - - return index ? bound2(3) : bound2(1, 2, 3); - })); - - assert.deepStrictEqual(actual, expected); - }); - - lodashStable.each(['curry', 'curryRight'], function(methodName, index) { - var fn = function(a, b, c) { return [a, b, c]; }, - curried = _[methodName](fn), - expected = index ? [3, 2, 1] : [1, 2, 3]; - - var actual = last(lodashStable.times(HOT_COUNT, function() { - return curried(1)(2)(3); - })); - - assert.deepStrictEqual(actual, expected); - - actual = last(lodashStable.times(HOT_COUNT, function() { - var curried = _[methodName](fn); - return curried(1)(2)(3); - })); - - assert.deepStrictEqual(actual, expected); - }); - - lodashStable.each(['partial', 'partialRight'], function(methodName, index) { - var func = _[methodName], - fn = function() { return slice.call(arguments); }, - par1 = func(fn, 1), - expected = index ? [3, 2, 1] : [1, 2, 3]; - - var actual = last(lodashStable.times(HOT_COUNT, function() { - var par2 = func(par1, 2); - return par2(3); - })); - - assert.deepStrictEqual(actual, expected); - - actual = last(lodashStable.times(HOT_COUNT, function() { - var par1 = func(fn, 1), - par2 = func(par1, 2); - - return par2(3); - })); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/methods-using-createWrapper.spec.js b/test/methods-using-createWrapper.spec.js new file mode 100644 index 0000000000..8cc1c59856 --- /dev/null +++ b/test/methods-using-createWrapper.spec.js @@ -0,0 +1,209 @@ +import lodashStable from 'lodash'; +import { slice, _, push, HOT_COUNT } from './utils'; +import bind from '../src/bind'; +import bindKey from '../src/bindKey'; +import partial from '../src/partial'; +import partialRight from '../src/partialRight'; +import last from '../src/last'; + +describe('methods using `createWrapper`', () => { + function fn() { + return slice.call(arguments); + } + + const ph1 = bind.placeholder; + const ph2 = bindKey.placeholder; + const ph3 = partial.placeholder; + const ph4 = partialRight.placeholder; + + it('should work with combinations of partial functions', () => { + const a = partial(fn); + const b = partialRight(a, 3); + const c = partial(b, 1); + + expect(c(2), [1, 2).toEqual(3]); + }); + + it('should work with combinations of bound and partial functions', () => { + const fn = function () { + const result = [this.a]; + push.apply(result, arguments); + return result; + }; + + const expected = [1, 2, 3, 4]; + const object = { a: 1, fn: fn }; + + let a = bindKey(object, 'fn'); + let b = partialRight(a, 4); + let c = partial(b, 2); + + expect(c(3)).toEqual(expected); + + a = bind(fn, object); + b = partialRight(a, 4); + c = partial(b, 2); + + expect(c(3)).toEqual(expected); + + a = partial(fn, 2); + b = bind(a, object); + c = partialRight(b, 4); + + expect(c(3)).toEqual(expected); + }); + + it('should ensure `new combo` is an instance of `func`', () => { + function Foo(a, b, c) { + return b === 0 && object; + } + + const combo = partial(partialRight(Foo, 3), 1); + var object = {}; + + expect(new combo(2) instanceof Foo) + expect(new combo(0)).toBe(object); + }); + + it('should work with combinations of functions with placeholders', () => { + const expected = [1, 2, 3, 4, 5, 6]; + const object = { fn: fn }; + + let a = bindKey(object, 'fn', ph2, 2); + let b = partialRight(a, ph4, 6); + let c = partial(b, 1, ph3, 4); + + expect(c(3, 5)).toEqual(expected); + + a = bind(fn, object, ph1, 2); + b = partialRight(a, ph4, 6); + c = partial(b, 1, ph3, 4); + + expect(c(3, 5)).toEqual(expected); + + a = partial(fn, ph3, 2); + b = bind(a, object, 1, ph1, 4); + c = partialRight(b, ph4, 6); + + expect(c(3, 5)).toEqual(expected); + }); + + it('should work with combinations of functions with overlapping placeholders', () => { + const expected = [1, 2, 3, 4]; + const object = { fn: fn }; + + let a = bindKey(object, 'fn', ph2, 2); + let b = partialRight(a, ph4, 4); + let c = partial(b, ph3, 3); + + expect(c(1)).toEqual(expected); + + a = bind(fn, object, ph1, 2); + b = partialRight(a, ph4, 4); + c = partial(b, ph3, 3); + + expect(c(1)).toEqual(expected); + + a = partial(fn, ph3, 2); + b = bind(a, object, ph1, 3); + c = partialRight(b, ph4, 4); + + expect(c(1)).toEqual(expected); + }); + + it('should work with recursively bound functions', () => { + const fn = function () { + return this.a; + }; + + const a = bind(fn, { a: 1 }); + const b = bind(a, { a: 2 }); + const c = bind(b, { a: 3 }); + + expect(c()).toBe(1); + }); + + it('should work when hot', () => { + lodashStable.times(2, (index) => { + const fn = function () { + const result = [this]; + push.apply(result, arguments); + return result; + }; + + const object = {}; + const bound1 = index ? bind(fn, object, 1) : bind(fn, object); + const expected = [object, 1, 2, 3]; + + let actual = last( + lodashStable.times(HOT_COUNT, () => { + const bound2 = index ? bind(bound1, null, 2) : bind(bound1); + return index ? bound2(3) : bound2(1, 2, 3); + }), + ); + + expect(actual).toEqual(expected); + + actual = last( + lodashStable.times(HOT_COUNT, () => { + const bound1 = index ? bind(fn, object, 1) : bind(fn, object); + const bound2 = index ? bind(bound1, null, 2) : bind(bound1); + + return index ? bound2(3) : bound2(1, 2, 3); + }), + ); + + expect(actual).toEqual(expected); + }); + + lodashStable.each(['curry', 'curryRight'], (methodName, index) => { + const fn = function (a, b, c) { + return [a, b, c]; + }; + const curried = _[methodName](fn); + const expected = index ? [3, 2, 1] : [1, 2, 3]; + + let actual = last(lodashStable.times(HOT_COUNT, () => curried(1)(2)(3))); + + expect(actual).toEqual(expected); + + actual = last( + lodashStable.times(HOT_COUNT, () => { + const curried = _[methodName](fn); + return curried(1)(2)(3); + }), + ); + + expect(actual).toEqual(expected); + }); + + lodashStable.each(['partial', 'partialRight'], (methodName, index) => { + const func = _[methodName]; + const fn = function () { + return slice.call(arguments); + }; + const par1 = func(fn, 1); + const expected = index ? [3, 2, 1] : [1, 2, 3]; + + let actual = last( + lodashStable.times(HOT_COUNT, () => { + const par2 = func(par1, 2); + return par2(3); + }), + ); + + expect(actual).toEqual(expected); + + actual = last( + lodashStable.times(HOT_COUNT, () => { + const par1 = func(fn, 1); + const par2 = func(par1, 2); + + return par2(3); + }), + ); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/min.js b/test/min.js deleted file mode 100644 index 271d444316..0000000000 --- a/test/min.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, noop } from './utils.js'; -import min from '../min.js'; - -describe('min', function() { - it('should return the smallest value from a collection', function() { - assert.strictEqual(min([1, 2, 3]), 1); - }); - - it('should return `undefined` for empty collections', function() { - var values = falsey.concat([[]]), - expected = lodashStable.map(values, noop); - - var actual = lodashStable.map(values, function(value, index) { - try { - return index ? min(value) : min(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with non-numeric collection values', function() { - assert.strictEqual(min(['a', 'b']), 'a'); - }); -}); diff --git a/test/min.spec.js b/test/min.spec.js new file mode 100644 index 0000000000..923bc7510e --- /dev/null +++ b/test/min.spec.js @@ -0,0 +1,26 @@ +import lodashStable from 'lodash'; +import { falsey, noop } from './utils'; +import min from '../src/min'; + +describe('min', () => { + it('should return the smallest value from a collection', () => { + expect(min([1, 2, 3])).toBe(1); + }); + + it('should return `undefined` for empty collections', () => { + const values = falsey.concat([[]]), + expected = lodashStable.map(values, noop); + + const actual = lodashStable.map(values, (value: false, index: number) => { + try { + return index ? min(value) : min(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should work with non-numeric collection values', () => { + expect(min(['a', 'b'])).toBe('a'); + }); +}); diff --git a/test/mixin.js b/test/mixin.js deleted file mode 100644 index 4b78cf8484..0000000000 --- a/test/mixin.js +++ /dev/null @@ -1,189 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, getUnwrappedValue, noop } from './utils.js'; -import has from '../has.js'; -import mixin from '../mixin.js'; -import prototype from '../prototype.js'; -import countBy from '../countBy.js'; -import filter from '../filter.js'; - -describe('mixin', function() { - function reset(wrapper) { - delete wrapper.a; - delete wrapper.prototype.a; - delete wrapper.b; - delete wrapper.prototype.b; - } - - function Wrapper(value) { - if (!(this instanceof Wrapper)) { - return new Wrapper(value); - } - if (has(value, '__wrapped__')) { - var actions = slice.call(value.__actions__), - chain = value.__chain__; - - value = value.__wrapped__; - } - this.__wrapped__ = value; - this.__actions__ = actions || []; - this.__chain__ = chain || false; - } - - Wrapper.prototype.value = function() { - return getUnwrappedValue(this); - }; - - var array = ['a'], - source = { 'a': function(array) { return array[0]; }, 'b': 'B' }; - - it('should mixin `source` methods into lodash', function() { - mixin(source); - - assert.strictEqual(_.a(array), 'a'); - assert.strictEqual(_(array).a().value(), 'a'); - assert.notOk('b' in _); - assert.notOk('b' in prototype); - - reset(_); - }); - - it('should mixin chaining methods by reference', function() { - mixin(source); - _.a = stubB; - - assert.strictEqual(_.a(array), 'b'); - assert.strictEqual(_(array).a().value(), 'a'); - - reset(_); - }); - - it('should use a default `object` of `this`', function() { - var object = lodashStable.create(_); - object.mixin(source); - - assert.strictEqual(object.a(array), 'a'); - assert.ok(!('a' in _)); - assert.ok(!('a' in prototype)); - - reset(_); - }); - - it('should accept an `object`', function() { - var object = {}; - mixin(object, source); - assert.strictEqual(object.a(array), 'a'); - }); - - it('should accept a function `object`', function() { - mixin(Wrapper, source); - - var wrapped = Wrapper(array), - actual = wrapped.a(); - - assert.strictEqual(actual.value(), 'a'); - assert.ok(actual instanceof Wrapper); - - reset(Wrapper); - }); - - it('should return `object`', function() { - var object = {}; - assert.strictEqual(mixin(object, source), object); - assert.strictEqual(mixin(Wrapper, source), Wrapper); - assert.strictEqual(mixin(), _); - - reset(Wrapper); - }); - - it('should not assign inherited `source` methods', function() { - function Foo() {} - Foo.prototype.a = noop; - - var object = {}; - assert.strictEqual(mixin(object, new Foo), object); - }); - - it('should accept an `options`', function() { - function message(func, chain) { - return (func === _ ? 'lodash' : 'given') + ' function should ' + (chain ? '' : 'not ') + 'chain'; - } - - lodashStable.each([_, Wrapper], function(func) { - lodashStable.each([{ 'chain': false }, { 'chain': true }], function(options) { - if (func === _) { - mixin(source, options); - } else { - mixin(func, source, options); - } - var wrapped = func(array), - actual = wrapped.a(); - - if (options.chain) { - assert.strictEqual(actual.value(), 'a', message(func, true)); - assert.ok(actual instanceof func, message(func, true)); - } else { - assert.strictEqual(actual, 'a', message(func, false)); - assert.notOk(actual instanceof func, message(func, false)); - } - reset(func); - }); - }); - }); - - it('should not extend lodash when an `object` is given with an empty `options` object', function() { - mixin({ 'a': noop }, {}); - assert.ok(!('a' in _)); - reset(_); - }); - - it('should not error for non-object `options` values', function() { - var pass = true; - - try { - mixin({}, source, 1); - } catch (e) { - pass = false; - } - assert.ok(pass); - - pass = true; - - try { - mixin(source, 1); - } catch (e) { - pass = false; - } - assert.ok(pass); - - reset(_); - }); - - it('should not return the existing wrapped value when chaining', function() { - lodashStable.each([_, Wrapper], function(func) { - if (func === _) { - var wrapped = _(source), - actual = wrapped.mixin(); - - assert.strictEqual(actual.value(), _); - } - else { - wrapped = _(func); - actual = wrapped.mixin(source); - assert.notStrictEqual(actual, wrapped); - } - reset(func); - }); - }); - - it('should produce methods that work in a lazy sequence', function() { - mixin({ 'a': countBy, 'b': filter }); - - var array = lodashStable.range(LARGE_ARRAY_SIZE), - actual = _(array).a().map(square).b(isEven).take().value(); - - assert.deepEqual(actual, _.take(_.b(_.map(_.a(array), square), isEven))); - - reset(_); - }); -}); diff --git a/test/multiply.spec.js b/test/multiply.spec.js new file mode 100644 index 0000000000..8b15bf2412 --- /dev/null +++ b/test/multiply.spec.js @@ -0,0 +1,14 @@ +import multiply from '../src/multiply'; + +describe('multiply', () => { + it('should multiply two numbers', () => { + expect(multiply(6, 4)).toBe(24); + expect(multiply(-6, 4)).toBe(-24); + expect(multiply(-6, -4)).toBe(24); + }); + + it('should coerce arguments to numbers', () => { + expect(multiply('6', '4')).toBe(24); + expect(multiply('x', 'y')).toEqual(NaN); + }); +}); diff --git a/test/multiply.test.js b/test/multiply.test.js deleted file mode 100644 index 23e966fb91..0000000000 --- a/test/multiply.test.js +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'assert'; -import multiply from '../multiply.js'; - -describe('multiply', function() { - it('should multiply two numbers', function() { - assert.strictEqual(multiply(6, 4), 24); - assert.strictEqual(multiply(-6, 4), -24); - assert.strictEqual(multiply(-6, -4), 24); - }); - - it('should coerce arguments to numbers', function() { - assert.strictEqual(multiply('6', '4'), 24); - assert.deepStrictEqual(multiply('x', 'y'), NaN); - }); -}); diff --git a/test/negate.js b/test/negate.js deleted file mode 100644 index 3f77255927..0000000000 --- a/test/negate.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, isEven, stubTrue } from './utils.js'; - -describe('negate', function() { - it('should create a function that negates the result of `func`', function() { - var negate = _.negate(isEven); - - assert.strictEqual(negate(1), true); - assert.strictEqual(negate(2), false); - }); - - it('should create a function that negates the result of `func`', function() { - var negate = _.negate(isEven); - - assert.strictEqual(negate(1), true); - assert.strictEqual(negate(2), false); - }); - - it('should create a function that accepts multiple arguments', function() { - var argCount, - count = 5, - negate = _.negate(function() { argCount = arguments.length; }), - expected = lodashStable.times(count, stubTrue); - - var actual = lodashStable.times(count, function(index) { - switch (index) { - case 0: negate(); break; - case 1: negate(1); break; - case 2: negate(1, 2); break; - case 3: negate(1, 2, 3); break; - case 4: negate(1, 2, 3, 4); - } - return argCount == index; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/negate.spec.js b/test/negate.spec.js new file mode 100644 index 0000000000..4965951c45 --- /dev/null +++ b/test/negate.spec.js @@ -0,0 +1,49 @@ +import lodashStable from 'lodash'; +import { _, isEven, stubTrue } from './utils'; + +describe('negate', () => { + it('should create a function that negates the result of `func`', () => { + const negate = _.negate(isEven); + + expect(negate(1)).toBe(true); + expect(negate(2)).toBe(false); + }); + + it('should create a function that negates the result of `func`', () => { + const negate = _.negate(isEven); + + expect(negate(1)).toBe(true); + expect(negate(2)).toBe(false); + }); + + it('should create a function that accepts multiple arguments', () => { + let argCount; + const count = 5; + const negate = _.negate(function () { + argCount = arguments.length; + }); + const expected = lodashStable.times(count, stubTrue); + + const actual = lodashStable.times(count, (index) => { + switch (index) { + case 0: + negate(); + break; + case 1: + negate(1); + break; + case 2: + negate(1, 2); + break; + case 3: + negate(1, 2, 3); + break; + case 4: + negate(1, 2, 3, 4); + } + return argCount === index; + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/noConflict.js b/test/noConflict.js deleted file mode 100644 index 419130d5b7..0000000000 --- a/test/noConflict.js +++ /dev/null @@ -1,33 +0,0 @@ -import assert from 'assert'; -import { oldDash, coverage, document, isModularize, realm, filePath } from './utils.js'; -import noConflict from '../noConflict.js'; - -describe('noConflict', function() { - it('should return the `lodash` function', function() { - assert.strictEqual(noConflict(), oldDash); - assert.notStrictEqual(root._, oldDash); - root._ = oldDash; - }); - - it('should restore `_` only if `lodash` is the current `_` value', function() { - var object = root._ = {}; - assert.strictEqual(noConflict(), oldDash); - assert.strictEqual(root._, object); - root._ = oldDash; - }); - - it('should work with a `root` of `this`', function() { - if (!coverage && !document && !isModularize && realm.object) { - var fs = require('fs'), - vm = require('vm'), - expected = {}, - context = vm.createContext({ '_': expected, 'console': console }), - source = fs.readFileSync(filePath, 'utf8'); - - vm.runInContext(source + '\nthis.lodash = this._.noConflict()', context); - - assert.strictEqual(context._, expected); - assert.ok(context.lodash); - } - }); -}); diff --git a/test/now.js b/test/now.js deleted file mode 100644 index 6b2febb249..0000000000 --- a/test/now.js +++ /dev/null @@ -1,26 +0,0 @@ -import assert from 'assert'; -import { _, stubA } from './utils.js'; - -describe('now', function() { - it('should return the number of milliseconds that have elapsed since the Unix epoch', function(done) { - var stamp = +new Date, - actual = _.now(); - - assert.ok(actual >= stamp); - - setTimeout(function() { - assert.ok(_.now() > actual); - done(); - }, 32); - }); - - it('should work with mocked `Date.now`', function() { - var now = Date.now; - Date.now = stubA; - - var actual = _.now(); - Date.now = now; - - assert.strictEqual(actual, 'a'); - }); -}); diff --git a/test/now.spec.js b/test/now.spec.js new file mode 100644 index 0000000000..09e5b3e73e --- /dev/null +++ b/test/now.spec.js @@ -0,0 +1,25 @@ +import { _, stubA } from './utils'; + +describe('now', () => { + it('should return the number of milliseconds that have elapsed since the Unix epoch', (done) => { + const stamp = +new Date(); + const actual = _.now(); + + expect(actual >= stamp); + + setTimeout(() => { + expect(_.now() > actual); + done(); + }, 32); + }); + + it('should work with mocked `Date.now`', () => { + const now = Date.now; + Date.now = stubA; + + const actual = _.now(); + Date.now = now; + + expect(actual).toBe('a'); + }); +}); diff --git a/test/nth.js b/test/nth.js deleted file mode 100644 index 49c0fcf2e5..0000000000 --- a/test/nth.js +++ /dev/null @@ -1,69 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubA, stubB, noop } from './utils.js'; -import nth from '../nth.js'; - -describe('nth', function() { - var array = ['a', 'b', 'c', 'd']; - - it('should get the nth element of `array`', function() { - var actual = lodashStable.map(array, function(value, index) { - return nth(array, index); - }); - - assert.deepStrictEqual(actual, array); - }); - - it('should work with a negative `n`', function() { - var actual = lodashStable.map(lodashStable.range(1, array.length + 1), function(n) { - return nth(array, -n); - }); - - assert.deepStrictEqual(actual, ['d', 'c', 'b', 'a']); - }); - - it('should coerce `n` to an integer', function() { - var values = falsey, - expected = lodashStable.map(values, stubA); - - var actual = lodashStable.map(values, function(n) { - return n ? nth(array, n) : nth(array); - }); - - assert.deepStrictEqual(actual, expected); - - values = ['1', 1.6]; - expected = lodashStable.map(values, stubB); - - actual = lodashStable.map(values, function(n) { - return nth(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `undefined` for empty arrays', function() { - var values = [null, undefined, []], - expected = lodashStable.map(values, noop); - - var actual = lodashStable.map(values, function(array) { - return nth(array, 1); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `undefined` for non-indexes', function() { - var array = [1, 2], - values = [Infinity, array.length], - expected = lodashStable.map(values, noop); - - array[-1] = 3; - - var actual = lodashStable.map(values, function(n) { - return nth(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/nth.spec.js b/test/nth.spec.js new file mode 100644 index 0000000000..56424be649 --- /dev/null +++ b/test/nth.spec.js @@ -0,0 +1,58 @@ +import lodashStable from 'lodash'; +import { falsey, stubA, stubB, noop } from './utils'; +import nth from '../src/nth'; + +describe('nth', () => { + const array = ['a', 'b', 'c', 'd']; + + it('should get the nth element of `array`', () => { + const actual = lodashStable.map(array, (value, index) => nth(array, index)); + + expect(actual).toEqual(array); + }); + + it('should work with a negative `n`', () => { + const actual = lodashStable.map(lodashStable.range(1, array.length + 1), (n) => + nth(array, -n), + ); + + expect(actual).toEqual(['d', 'c', 'b', 'a']); + }); + + it('should coerce `n` to an integer', () => { + let values = falsey; + let expected = lodashStable.map(values, stubA); + + let actual = lodashStable.map(values, (n) => (n ? nth(array, n) : nth(array))); + + expect(actual).toEqual(expected); + + values = ['1', 1.6]; + expected = lodashStable.map(values, stubB); + + actual = lodashStable.map(values, (n) => nth(array, n)); + + expect(actual).toEqual(expected); + }); + + it('should return `undefined` for empty arrays', () => { + const values = [null, undefined, []]; + const expected = lodashStable.map(values, noop); + + const actual = lodashStable.map(values, (array) => nth(array, 1)); + + expect(actual).toEqual(expected); + }); + + it('should return `undefined` for non-indexes', () => { + const array = [1, 2]; + const values = [Infinity, array.length]; + const expected = lodashStable.map(values, noop); + + array[-1] = 3; + + const actual = lodashStable.map(values, (n) => nth(array, n)); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/nthArg.js b/test/nthArg.js deleted file mode 100644 index 387e15f7d9..0000000000 --- a/test/nthArg.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, falsey, stubA, stubB, noop } from './utils.js'; -import nthArg from '../nthArg.js'; - -describe('nthArg', function() { - var args = ['a', 'b', 'c', 'd']; - - it('should create a function that returns its nth argument', function() { - var actual = lodashStable.map(args, function(value, index) { - var func = nthArg(index); - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, args); - }); - - it('should work with a negative `n`', function() { - var actual = lodashStable.map(lodashStable.range(1, args.length + 1), function(n) { - var func = nthArg(-n); - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, ['d', 'c', 'b', 'a']); - }); - - it('should coerce `n` to an integer', function() { - var values = falsey, - expected = lodashStable.map(values, stubA); - - var actual = lodashStable.map(values, function(n) { - var func = n ? nthArg(n) : nthArg(); - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, expected); - - values = ['1', 1.6]; - expected = lodashStable.map(values, stubB); - - actual = lodashStable.map(values, function(n) { - var func = nthArg(n); - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `undefined` for empty arrays', function() { - var func = nthArg(1); - assert.strictEqual(func(), undefined); - }); - - it('should return `undefined` for non-indexes', function() { - var values = [Infinity, args.length], - expected = lodashStable.map(values, noop); - - var actual = lodashStable.map(values, function(n) { - var func = nthArg(n); - return func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/nthArg.spec.js b/test/nthArg.spec.js new file mode 100644 index 0000000000..d12c2c13df --- /dev/null +++ b/test/nthArg.spec.js @@ -0,0 +1,64 @@ +import lodashStable from 'lodash'; +import { args, falsey, stubA, stubB, noop } from './utils'; +import nthArg from '../src/nthArg'; + +describe('nthArg', () => { + const args = ['a', 'b', 'c', 'd']; + + it('should create a function that returns its nth argument', () => { + const actual = lodashStable.map(args, (value, index) => { + const func = nthArg(index); + return func.apply(undefined, args); + }); + + expect(actual).toEqual(args); + }); + + it('should work with a negative `n`', () => { + const actual = lodashStable.map(lodashStable.range(1, args.length + 1), (n) => { + const func = nthArg(-n); + return func.apply(undefined, args); + }); + + expect(actual).toEqual(['d', 'c', 'b', 'a']); + }); + + it('should coerce `n` to an integer', () => { + let values = falsey; + let expected = lodashStable.map(values, stubA); + + let actual = lodashStable.map(values, (n) => { + const func = n ? nthArg(n) : nthArg(); + return func.apply(undefined, args); + }); + + expect(actual).toEqual(expected); + + values = ['1', 1.6]; + expected = lodashStable.map(values, stubB); + + actual = lodashStable.map(values, (n) => { + const func = nthArg(n); + return func.apply(undefined, args); + }); + + expect(actual).toEqual(expected); + }); + + it('should return `undefined` for empty arrays', () => { + const func = nthArg(1); + expect(func()).toBe(undefined); + }); + + it('should return `undefined` for non-indexes', () => { + const values = [Infinity, args.length]; + const expected = lodashStable.map(values, noop); + + const actual = lodashStable.map(values, (n) => { + const func = nthArg(n); + return func.apply(undefined, args); + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/number-coercion-methods.js b/test/number-coercion-methods.js deleted file mode 100644 index edf982d62d..0000000000 --- a/test/number-coercion-methods.js +++ /dev/null @@ -1,248 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - _, - identity, - whitespace, - MAX_SAFE_INTEGER, - MAX_INTEGER, - MAX_ARRAY_LENGTH, - symbol, - falsey, -} from './utils.js'; - -describe('number coercion methods', function() { - lodashStable.each(['toFinite', 'toInteger', 'toNumber', 'toSafeInteger'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var values = [0, '0', -0, '-0'], - expected = [[0, Infinity], [0, Infinity], [-0, -Infinity], [-0, -Infinity]]; - - lodashStable.times(2, function(index) { - var others = lodashStable.map(values, index ? Object : identity); - - var actual = lodashStable.map(others, function(value) { - var result = func(value); - return [result, 1 / result]; - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - }); - - lodashStable.each(['toFinite', 'toInteger', 'toLength', 'toNumber', 'toSafeInteger'], function(methodName) { - var func = _[methodName], - isToFinite = methodName == 'toFinite', - isToLength = methodName == 'toLength', - isToNumber = methodName == 'toNumber', - isToSafeInteger = methodName == 'toSafeInteger'; - - function negative(string) { - return '-' + string; - } - - function pad(string) { - return whitespace + string + whitespace; - } - - function positive(string) { - return '+' + string; - } - - it('`_.' + methodName + '` should pass thru primitive number values', function() { - var values = [0, 1, NaN]; - - var expected = lodashStable.map(values, function(value) { - return (!isToNumber && value !== value) ? 0 : value; - }); - - var actual = lodashStable.map(values, func); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should convert number primitives and objects to numbers', function() { - var values = [2, 1.2, MAX_SAFE_INTEGER, MAX_INTEGER, Infinity, NaN]; - - var expected = lodashStable.map(values, function(value) { - if (!isToNumber) { - if (!isToFinite && value == 1.2) { - value = 1; - } - else if (value == Infinity) { - value = MAX_INTEGER; - } - else if (value !== value) { - value = 0; - } - if (isToLength || isToSafeInteger) { - value = Math.min(value, isToLength ? MAX_ARRAY_LENGTH : MAX_SAFE_INTEGER); - } - } - var neg = isToLength ? 0 : -value; - return [value, value, neg, neg]; - }); - - var actual = lodashStable.map(values, function(value) { - return [func(value), func(Object(value)), func(-value), func(Object(-value))]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should convert string primitives and objects to numbers', function() { - var transforms = [identity, pad, positive, negative]; - - var values = [ - '10', '1.234567890', (MAX_SAFE_INTEGER + ''), - '1e+308', '1e308', '1E+308', '1E308', - '5e-324', '5E-324', - 'Infinity', 'NaN' - ]; - - var expected = lodashStable.map(values, function(value) { - var n = +value; - if (!isToNumber) { - if (!isToFinite && n == 1.234567890) { - n = 1; - } - else if (n == Infinity) { - n = MAX_INTEGER; - } - else if ((!isToFinite && n == Number.MIN_VALUE) || n !== n) { - n = 0; - } - if (isToLength || isToSafeInteger) { - n = Math.min(n, isToLength ? MAX_ARRAY_LENGTH : MAX_SAFE_INTEGER); - } - } - var neg = isToLength ? 0 : -n; - return [n, n, n, n, n, n, neg, neg]; - }); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.flatMap(transforms, function(mod) { - return [func(mod(value)), func(Object(mod(value)))]; - }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should convert binary/octal strings to numbers', function() { - var numbers = [42, 5349, 1715004], - transforms = [identity, pad], - values = ['0b101010', '0o12345', '0x1a2b3c']; - - var expected = lodashStable.map(numbers, function(n) { - return lodashStable.times(8, lodashStable.constant(n)); - }); - - var actual = lodashStable.map(values, function(value) { - var upper = value.toUpperCase(); - return lodashStable.flatMap(transforms, function(mod) { - return [func(mod(value)), func(Object(mod(value))), func(mod(upper)), func(Object(mod(upper)))]; - }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should convert invalid binary/octal strings to `' + (isToNumber ? 'NaN' : '0') + '`', function() { - var transforms = [identity, pad, positive, negative], - values = ['0b', '0o', '0x', '0b1010102', '0o123458', '0x1a2b3x']; - - var expected = lodashStable.map(values, function(n) { - return lodashStable.times(8, lodashStable.constant(isToNumber ? NaN : 0)); - }); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.flatMap(transforms, function(mod) { - return [func(mod(value)), func(Object(mod(value)))]; - }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should convert symbols to `' + (isToNumber ? 'NaN' : '0') + '`', function() { - if (Symbol) { - var object1 = Object(symbol), - object2 = Object(symbol), - values = [symbol, object1, object2], - expected = lodashStable.map(values, lodashStable.constant(isToNumber ? NaN : 0)); - - object2.valueOf = undefined; - var actual = lodashStable.map(values, func); - - assert.deepStrictEqual(actual, expected); - } - }); - - it('`_.' + methodName + '` should convert empty values to `0` or `NaN`', function() { - var values = falsey.concat(whitespace); - - var expected = lodashStable.map(values, function(value) { - return (isToNumber && value !== whitespace) ? Number(value) : 0; - }); - - var actual = lodashStable.map(values, function(value, index) { - return index ? func(value) : func(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should coerce objects to numbers', function() { - var values = [ - {}, - [], - [1], - [1, 2], - { 'valueOf': '1.1' }, - { 'valueOf': '1.1', 'toString': lodashStable.constant('2.2') }, - { 'valueOf': lodashStable.constant('1.1'), 'toString': '2.2' }, - { 'valueOf': lodashStable.constant('1.1'), 'toString': lodashStable.constant('2.2') }, - { 'valueOf': lodashStable.constant('-0x1a2b3c') }, - { 'toString': lodashStable.constant('-0x1a2b3c') }, - { 'valueOf': lodashStable.constant('0o12345') }, - { 'toString': lodashStable.constant('0o12345') }, - { 'valueOf': lodashStable.constant('0b101010') }, - { 'toString': lodashStable.constant('0b101010') } - ]; - - var expected = [ - NaN, 0, 1, NaN, - NaN, 2.2, 1.1, 1.1, - NaN, NaN, - 5349, 5349, - 42, 42 - ]; - - if (isToFinite) { - expected = [ - 0, 0, 1, 0, - 0, 2.2, 1.1, 1.1, - 0, 0, - 5349, 5349, - 42, 42 - ]; - } - else if (!isToNumber) { - expected = [ - 0, 0, 1, 0, - 0, 2, 1, 1, - 0, 0, - 5349, 5349, - 42, 42 - ]; - } - var actual = lodashStable.map(values, func); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/number-coercion-methods.spec.js b/test/number-coercion-methods.spec.js new file mode 100644 index 0000000000..af299e68d2 --- /dev/null +++ b/test/number-coercion-methods.spec.js @@ -0,0 +1,260 @@ +import lodashStable from 'lodash'; + +import { + _, + identity, + whitespace, + MAX_SAFE_INTEGER, + MAX_INTEGER, + MAX_ARRAY_LENGTH, + symbol, + falsey, +} from './utils'; + +describe('number coercion methods', () => { + lodashStable.each(['toFinite', 'toInteger', 'toNumber', 'toSafeInteger'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const values = [0, '0', -0, '-0']; + const expected = [ + [0, Infinity], + [0, Infinity], + [-0, -Infinity], + [-0, -Infinity], + ]; + + lodashStable.times(2, (index) => { + const others = lodashStable.map(values, index ? Object : identity); + + const actual = lodashStable.map(others, (value) => { + const result = func(value); + return [result, 1 / result]; + }); + + expect(actual).toEqual(expected); + }); + }); + }); + + lodashStable.each( + ['toFinite', 'toInteger', 'toLength', 'toNumber', 'toSafeInteger'], + (methodName) => { + const func = _[methodName]; + const isToFinite = methodName === 'toFinite'; + const isToLength = methodName === 'toLength'; + const isToNumber = methodName === 'toNumber'; + const isToSafeInteger = methodName === 'toSafeInteger'; + + function negative(string) { + return `-${string}`; + } + + function pad(string) { + return whitespace + string + whitespace; + } + + function positive(string) { + return `+${string}`; + } + + it(`\`_.${methodName}\` should pass thru primitive number values`, () => { + const values = [0, 1, NaN]; + + const expected = lodashStable.map(values, (value) => + !isToNumber && value !== value ? 0 : value, + ); + + const actual = lodashStable.map(values, func); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should convert number primitives and objects to numbers`, () => { + const values = [2, 1.2, MAX_SAFE_INTEGER, MAX_INTEGER, Infinity, NaN]; + + const expected = lodashStable.map(values, (value) => { + if (!isToNumber) { + if (!isToFinite && value === 1.2) { + value = 1; + } else if (value === Infinity) { + value = MAX_INTEGER; + } else if (value !== value) { + value = 0; + } + if (isToLength || isToSafeInteger) { + value = Math.min( + value, + isToLength ? MAX_ARRAY_LENGTH : MAX_SAFE_INTEGER, + ); + } + } + const neg = isToLength ? 0 : -value; + return [value, value, neg, neg]; + }); + + const actual = lodashStable.map(values, (value) => [ + func(value), + func(Object(value)), + func(-value), + func(Object(-value)), + ]); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should convert string primitives and objects to numbers`, () => { + const transforms = [identity, pad, positive, negative]; + + const values = [ + '10', + '1.234567890', + `${MAX_SAFE_INTEGER}`, + '1e+308', + '1e308', + '1E+308', + '1E308', + '5e-324', + '5E-324', + 'Infinity', + 'NaN', + ]; + + const expected = lodashStable.map(values, (value) => { + let n = +value; + if (!isToNumber) { + if (!isToFinite && n === 1.23456789) { + n = 1; + } else if (n === Infinity) { + n = MAX_INTEGER; + } else if ((!isToFinite && n === Number.MIN_VALUE) || n !== n) { + n = 0; + } + if (isToLength || isToSafeInteger) { + n = Math.min(n, isToLength ? MAX_ARRAY_LENGTH : MAX_SAFE_INTEGER); + } + } + const neg = isToLength ? 0 : -n; + return [n, n, n, n, n, n, neg, neg]; + }); + + const actual = lodashStable.map(values, (value) => + lodashStable.flatMap(transforms, (mod) => [ + func(mod(value)), + func(Object(mod(value))), + ]), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should convert binary/octal strings to numbers`, () => { + const numbers = [42, 5349, 1715004]; + const transforms = [identity, pad]; + const values = ['0b101010', '0o12345', '0x1a2b3c']; + + const expected = lodashStable.map(numbers, (n) => + lodashStable.times(8, lodashStable.constant(n)), + ); + + const actual = lodashStable.map(values, (value) => { + const upper = value.toUpperCase(); + return lodashStable.flatMap(transforms, (mod) => [ + func(mod(value)), + func(Object(mod(value))), + func(mod(upper)), + func(Object(mod(upper))), + ]); + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should convert invalid binary/octal strings to \`${ + isToNumber ? 'NaN' : '0' + }\``, () => { + const transforms = [identity, pad, positive, negative]; + const values = ['0b', '0o', '0x', '0b1010102', '0o123458', '0x1a2b3x']; + + const expected = lodashStable.map(values, (n) => + lodashStable.times(8, lodashStable.constant(isToNumber ? NaN : 0)), + ); + + const actual = lodashStable.map(values, (value) => + lodashStable.flatMap(transforms, (mod) => [ + func(mod(value)), + func(Object(mod(value))), + ]), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should convert symbols to \`${ + isToNumber ? 'NaN' : '0' + }\``, () => { + if (Symbol) { + const object1 = Object(symbol); + const object2 = Object(symbol); + const values = [symbol, object1, object2]; + const expected = lodashStable.map( + values, + lodashStable.constant(isToNumber ? NaN : 0), + ); + + object2.valueOf = undefined; + const actual = lodashStable.map(values, func); + + expect(actual).toEqual(expected); + } + }); + + it(`\`_.${methodName}\` should convert empty values to \`0\` or \`NaN\``, () => { + const values = falsey.concat(whitespace); + + const expected = lodashStable.map(values, (value) => + isToNumber && value !== whitespace ? Number(value) : 0, + ); + + const actual = lodashStable.map(values, (value, index) => + index ? func(value) : func(), + ); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should coerce objects to numbers`, () => { + const values = [ + {}, + [], + [1], + [1, 2], + { valueOf: '1.1' }, + { valueOf: '1.1', toString: lodashStable.constant('2.2') }, + { valueOf: lodashStable.constant('1.1'), toString: '2.2' }, + { + valueOf: lodashStable.constant('1.1'), + toString: lodashStable.constant('2.2'), + }, + { valueOf: lodashStable.constant('-0x1a2b3c') }, + { toString: lodashStable.constant('-0x1a2b3c') }, + { valueOf: lodashStable.constant('0o12345') }, + { toString: lodashStable.constant('0o12345') }, + { valueOf: lodashStable.constant('0b101010') }, + { toString: lodashStable.constant('0b101010') }, + ]; + + let expected = [NaN, 0, 1, NaN, NaN, 2.2, 1.1, 1.1, NaN, NaN, 5349, 5349, 42, 42]; + + if (isToFinite) { + expected = [0, 0, 1, 0, 0, 2.2, 1.1, 1.1, 0, 0, 5349, 5349, 42, 42]; + } else if (!isToNumber) { + expected = [0, 0, 1, 0, 0, 2, 1, 1, 0, 0, 5349, 5349, 42, 42]; + } + const actual = lodashStable.map(values, func); + + expect(actual).toEqual(expected); + }); + }, + ); +}); diff --git a/test/object-assignments.js b/test/object-assignments.js deleted file mode 100644 index a9cb8b0cb5..0000000000 --- a/test/object-assignments.js +++ /dev/null @@ -1,180 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, primitives, stubTrue, defineProperty, slice } from './utils.js'; -import has from '../has.js'; - -describe('object assignments', function() { - lodashStable.each(['assign', 'assignIn', 'defaults', 'defaultsDeep', 'merge'], function(methodName) { - var func = _[methodName], - isAssign = methodName == 'assign', - isDefaults = /^defaults/.test(methodName); - - it('`_.' + methodName + '` should coerce primitives to objects', function() { - var expected = lodashStable.map(primitives, function(value) { - var object = Object(value); - object.a = 1; - return object; - }); - - var actual = lodashStable.map(primitives, function(value) { - return func(value, { 'a': 1 }); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should assign own ' + (isAssign ? '' : 'and inherited ') + 'string keyed source properties', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var expected = isAssign ? { 'a': 1 } : { 'a': 1, 'b': 2 }; - assert.deepStrictEqual(func({}, new Foo), expected); - }); - - it('`_.' + methodName + '` should not skip a trailing function source', function() { - function fn() {} - fn.b = 2; - - assert.deepStrictEqual(func({}, { 'a': 1 }, fn), { 'a': 1, 'b': 2 }); - }); - - it('`_.' + methodName + '` should not error on nullish sources', function() { - try { - assert.deepStrictEqual(func({ 'a': 1 }, undefined, { 'b': 2 }, null), { 'a': 1, 'b': 2 }); - } catch (e) { - assert.ok(false, e.message); - } - }); - - it('`_.' + methodName + '` should create an object when `object` is nullish', function() { - var source = { 'a': 1 }, - values = [null, undefined], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - var object = func(value, source); - return object !== source && lodashStable.isEqual(object, source); - }); - - assert.deepStrictEqual(actual, expected); - - actual = lodashStable.map(values, function(value) { - return lodashStable.isEqual(func(value), {}); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work as an iteratee for methods like `_.reduce`', function() { - var array = [{ 'a': 1 }, { 'b': 2 }, { 'c': 3 }], - expected = { 'a': isDefaults ? 0 : 1, 'b': 2, 'c': 3 }; - - function fn() {} - fn.a = array[0]; - fn.b = array[1]; - fn.c = array[2]; - - assert.deepStrictEqual(lodashStable.reduce(array, func, { 'a': 0 }), expected); - assert.deepStrictEqual(lodashStable.reduce(fn, func, { 'a': 0 }), expected); - }); - - it('`_.' + methodName + '` should not return the existing wrapped value when chaining', function() { - var wrapped = _({ 'a': 1 }), - actual = wrapped[methodName]({ 'b': 2 }); - - assert.notStrictEqual(actual, wrapped); - }); - }); - - lodashStable.each(['assign', 'assignIn', 'merge'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should not treat `object` as `source`', function() { - function Foo() {} - Foo.prototype.a = 1; - - var actual = func(new Foo, { 'b': 2 }); - assert.ok(!has(actual, 'a')); - }); - }); - - lodashStable.each(['assign', 'assignIn', 'assignInWith', 'assignWith', 'defaults', 'defaultsDeep', 'merge', 'mergeWith'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should not assign values that are the same as their destinations', function() { - lodashStable.each(['a', ['a'], { 'a': 1 }, NaN], function(value) { - var object = {}, - pass = true; - - defineProperty(object, 'a', { - 'configurable': true, - 'enumerable': true, - 'get': lodashStable.constant(value), - 'set': function() { pass = false; } - }); - - func(object, { 'a': value }); - assert.ok(pass); - }); - }); - }); - - lodashStable.each(['assignWith', 'assignInWith', 'mergeWith'], function(methodName) { - var func = _[methodName], - isMergeWith = methodName == 'mergeWith'; - - it('`_.' + methodName + '` should provide correct `customizer` arguments', function() { - var args, - object = { 'a': 1 }, - source = { 'a': 2 }, - expected = lodashStable.map([1, 2, 'a', object, source], lodashStable.cloneDeep); - - func(object, source, function() { - args || (args = lodashStable.map(slice.call(arguments, 0, 5), lodashStable.cloneDeep)); - }); - - assert.deepStrictEqual(args, expected, 'primitive values'); - - var argsList = [], - objectValue = [1, 2], - sourceValue = { 'b': 2 }; - - object = { 'a': objectValue }; - source = { 'a': sourceValue }; - expected = [lodashStable.map([objectValue, sourceValue, 'a', object, source], lodashStable.cloneDeep)]; - - if (isMergeWith) { - expected.push(lodashStable.map([undefined, 2, 'b', objectValue, sourceValue], lodashStable.cloneDeep)); - } - func(object, source, function() { - argsList.push(lodashStable.map(slice.call(arguments, 0, 5), lodashStable.cloneDeep)); - }); - - assert.deepStrictEqual(argsList, expected, 'object values'); - - args = undefined; - object = { 'a': 1 }; - source = { 'b': 2 }; - expected = lodashStable.map([undefined, 2, 'b', object, source], lodashStable.cloneDeep); - - func(object, source, function() { - args || (args = lodashStable.map(slice.call(arguments, 0, 5), lodashStable.cloneDeep)); - }); - - assert.deepStrictEqual(args, expected, 'undefined properties'); - }); - - it('`_.' + methodName + '` should not treat the second argument as a `customizer` callback', function() { - function callback() {} - callback.b = 2; - - var actual = func({ 'a': 1 }, callback); - assert.deepStrictEqual(actual, { 'a': 1, 'b': 2 }); - - actual = func({ 'a': 1 }, callback, { 'c': 3 }); - assert.deepStrictEqual(actual, { 'a': 1, 'b': 2, 'c': 3 }); - }); - }); -}); diff --git a/test/object-assignments.spec.js b/test/object-assignments.spec.js new file mode 100644 index 0000000000..b55855cfbb --- /dev/null +++ b/test/object-assignments.spec.js @@ -0,0 +1,208 @@ +import lodashStable from 'lodash'; +import { _, primitives, stubTrue, defineProperty, slice } from './utils'; +import has from '../src/has'; + +describe('object assignments', () => { + lodashStable.each(['assign', 'assignIn', 'defaults', 'defaultsDeep', 'merge'], (methodName) => { + const func = _[methodName]; + const isAssign = methodName === 'assign'; + const isDefaults = /^defaults/.test(methodName); + + it(`\`_.${methodName}\` should coerce primitives to objects`, () => { + const expected = lodashStable.map(primitives, (value) => { + const object = Object(value); + object.a = 1; + return object; + }); + + const actual = lodashStable.map(primitives, (value) => func(value, { a: 1 })); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should assign own ${ + isAssign ? '' : 'and inherited ' + }string keyed source properties`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const expected = isAssign ? { a: 1 } : { a: 1, b: 2 }; + expect(func({}, new Foo())).toEqual(expected); + }); + + it(`\`_.${methodName}\` should not skip a trailing function source`, () => { + function fn() {} + fn.b = 2; + + expect(func({}, { a: 1 }, fn), { a: 1).toEqual(b: 2 }); + }); + + it(`\`_.${methodName}\` should not error on nullish sources`, () => { + try { + expect(func({ a: 1 }, undefined, { b: 2 }, null), { a: 1).toEqual(b: 2 }); + } catch (e) { + expect(false, e.message) + } + }); + + it(`\`_.${methodName}\` should create an object when \`object\` is nullish`, () => { + const source = { a: 1 }; + const values = [null, undefined]; + const expected = lodashStable.map(values, stubTrue); + + let actual = lodashStable.map(values, (value) => { + const object = func(value, source); + return object !== source && lodashStable.isEqual(object, source); + }); + + expect(actual).toEqual(expected); + + actual = lodashStable.map(values, (value) => lodashStable.isEqual(func(value), {})); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work as an iteratee for methods like \`_.reduce\``, () => { + const array = [{ a: 1 }, { b: 2 }, { c: 3 }]; + const expected = { a: isDefaults ? 0 : 1, b: 2, c: 3 }; + + function fn() {} + fn.a = array[0]; + fn.b = array[1]; + fn.c = array[2]; + + expect(lodashStable.reduce(array, func, { a: 0 })).toEqual(expected); + expect(lodashStable.reduce(fn, func, { a: 0 })).toEqual(expected); + }); + + it(`\`_.${methodName}\` should not return the existing wrapped value when chaining`, () => { + const wrapped = _({ a: 1 }); + const actual = wrapped[methodName]({ b: 2 }); + + assert.notStrictEqual(actual, wrapped); + }); + }); + + lodashStable.each(['assign', 'assignIn', 'merge'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should not treat \`object\` as \`source\``, () => { + function Foo() {} + Foo.prototype.a = 1; + + const actual = func(new Foo(), { b: 2 }); + expect(has(actual, 'a')).toBe(false) + }); + }); + + lodashStable.each( + [ + 'assign', + 'assignIn', + 'assignInWith', + 'assignWith', + 'defaults', + 'defaultsDeep', + 'merge', + 'mergeWith', + ], + (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should not assign values that are the same as their destinations`, () => { + lodashStable.each(['a', ['a'], { a: 1 }, NaN], (value) => { + const object = {}; + let pass = true; + + defineProperty(object, 'a', { + configurable: true, + enumerable: true, + get: lodashStable.constant(value), + set: function () { + pass = false; + }, + }); + + func(object, { a: value }); + expect(pass) + }); + }); + }, + ); + + lodashStable.each(['assignWith', 'assignInWith', 'mergeWith'], (methodName) => { + const func = _[methodName]; + const isMergeWith = methodName === 'mergeWith'; + + it(`\`_.${methodName}\` should provide correct \`customizer\` arguments`, () => { + let args; + let object = { a: 1 }; + let source = { a: 2 }; + let expected = lodashStable.map([1, 2, 'a', object, source], lodashStable.cloneDeep); + + func(object, source, function () { + args || + (args = lodashStable.map(slice.call(arguments, 0, 5), lodashStable.cloneDeep)); + }); + + expect(args, expected).toEqual('primitive values'); + + const argsList = []; + const objectValue = [1, 2]; + const sourceValue = { b: 2 }; + + object = { a: objectValue }; + source = { a: sourceValue }; + expected = [ + lodashStable.map( + [objectValue, sourceValue, 'a', object, source], + lodashStable.cloneDeep, + ), + ]; + + if (isMergeWith) { + expected.push( + lodashStable.map( + [undefined, 2, 'b', objectValue, sourceValue], + lodashStable.cloneDeep, + ), + ); + } + func(object, source, function () { + argsList.push( + lodashStable.map(slice.call(arguments, 0, 5), lodashStable.cloneDeep), + ); + }); + + expect(argsList, expected).toEqual('object values'); + + args = undefined; + object = { a: 1 }; + source = { b: 2 }; + expected = lodashStable.map( + [undefined, 2, 'b', object, source], + lodashStable.cloneDeep, + ); + + func(object, source, function () { + args || + (args = lodashStable.map(slice.call(arguments, 0, 5), lodashStable.cloneDeep)); + }); + + expect(args, expected).toEqual('undefined properties'); + }); + + it(`\`_.${methodName}\` should not treat the second argument as a \`customizer\` callback`, () => { + function callback() {} + callback.b = 2; + + let actual = func({ a: 1 }, callback); + expect(actual, { a: 1).toEqual(b: 2 }); + + actual = func({ a: 1 }, callback, { c: 3 }); + expect(actual, { a: 1, b: 2).toEqual(c: 3 }); + }); + }); +}); diff --git a/test/omit-methods.js b/test/omit-methods.js deleted file mode 100644 index 750cb9f34b..0000000000 --- a/test/omit-methods.js +++ /dev/null @@ -1,114 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, symbol, defineProperty } from './utils.js'; - -describe('omit methods', function() { - lodashStable.each(['omit', 'omitBy'], function(methodName) { - var expected = { 'b': 2, 'd': 4 }, - func = _[methodName], - object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, - resolve = lodashStable.nthArg(1); - - if (methodName == 'omitBy') { - resolve = function(object, props) { - props = lodashStable.castArray(props); - return function(value) { - return lodashStable.some(props, function(key) { - key = lodashStable.isSymbol(key) ? key : lodashStable.toString(key); - return object[key] === value; - }); - }; - }; - } - it('`_.' + methodName + '` should create an object with omitted string keyed properties', function() { - assert.deepStrictEqual(func(object, resolve(object, 'a')), { 'b': 2, 'c': 3, 'd': 4 }); - assert.deepStrictEqual(func(object, resolve(object, ['a', 'c'])), expected); - }); - - it('`_.' + methodName + '` should include inherited string keyed properties', function() { - function Foo() {} - Foo.prototype = object; - - assert.deepStrictEqual(func(new Foo, resolve(object, ['a', 'c'])), expected); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var object = { '-0': 'a', '0': 'b' }, - props = [-0, Object(-0), 0, Object(0)], - expected = [{ '0': 'b' }, { '0': 'b' }, { '-0': 'a' }, { '-0': 'a' }]; - - var actual = lodashStable.map(props, function(key) { - return func(object, resolve(object, key)); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should include symbols', function() { - function Foo() { - this.a = 0; - this[symbol] = 1; - } - - if (Symbol) { - var symbol2 = Symbol('b'); - Foo.prototype[symbol2] = 2; - - var symbol3 = Symbol('c'); - defineProperty(Foo.prototype, symbol3, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': 3 - }); - - var foo = new Foo, - actual = func(foo, resolve(foo, 'a')); - - assert.strictEqual(actual[symbol], 1); - assert.strictEqual(actual[symbol2], 2); - assert.ok(!(symbol3 in actual)); - } - }); - - it('`_.' + methodName + '` should create an object with omitted symbols', function() { - function Foo() { - this.a = 0; - this[symbol] = 1; - } - - if (Symbol) { - var symbol2 = Symbol('b'); - Foo.prototype[symbol2] = 2; - - var symbol3 = Symbol('c'); - defineProperty(Foo.prototype, symbol3, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': 3 - }); - - var foo = new Foo, - actual = func(foo, resolve(foo, symbol)); - - assert.strictEqual(actual.a, 0); - assert.ok(!(symbol in actual)); - assert.strictEqual(actual[symbol2], 2); - assert.ok(!(symbol3 in actual)); - - actual = func(foo, resolve(foo, symbol2)); - - assert.strictEqual(actual.a, 0); - assert.strictEqual(actual[symbol], 1); - assert.ok(!(symbol2 in actual)); - assert.ok(!(symbol3 in actual)); - } - }); - - it('`_.' + methodName + '` should work with an array `object`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(func(array, resolve(array, ['0', '2'])), { '1': 2 }); - }); - }); -}); diff --git a/test/omit-methods.spec.js b/test/omit-methods.spec.js new file mode 100644 index 0000000000..4b4f799620 --- /dev/null +++ b/test/omit-methods.spec.js @@ -0,0 +1,111 @@ +import lodashStable from 'lodash'; +import { _, symbol, defineProperty } from './utils'; + +describe('omit methods', () => { + lodashStable.each(['omit', 'omitBy'], (methodName) => { + const expected = { b: 2, d: 4 }; + const func = _[methodName]; + const object = { a: 1, b: 2, c: 3, d: 4 }; + let resolve = lodashStable.nthArg(1); + + if (methodName === 'omitBy') { + resolve = function (object, props) { + props = lodashStable.castArray(props); + return function (value) { + return lodashStable.some(props, (key) => { + key = lodashStable.isSymbol(key) ? key : lodashStable.toString(key); + return object[key] === value; + }); + }; + }; + } + it(`\`_.${methodName}\` should create an object with omitted string keyed properties`, () => { + expect(func(object, resolve(object, 'a'))).toEqual({ b: 2, c: 3, d: 4 }); + expect(func(object, resolve(object, ['a', 'c']))).toEqual(expected); + }); + + it(`\`_.${methodName}\` should include inherited string keyed properties`, () => { + function Foo() {} + Foo.prototype = object; + + expect(func(new Foo(), resolve(object, ['a', 'c']))).toEqual(expected); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const object = { '-0': 'a', 0: 'b' }; + const props = [-0, Object(-0), 0, Object(0)]; + const expected = [{ 0: 'b' }, { 0: 'b' }, { '-0': 'a' }, { '-0': 'a' }]; + + const actual = lodashStable.map(props, (key) => func(object, resolve(object, key))); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should include symbols`, () => { + function Foo() { + this.a = 0; + this[symbol] = 1; + } + + if (Symbol) { + const symbol2 = Symbol('b'); + Foo.prototype[symbol2] = 2; + + const symbol3 = Symbol('c'); + defineProperty(Foo.prototype, symbol3, { + configurable: true, + enumerable: false, + writable: true, + value: 3, + }); + + const foo = new Foo(); + const actual = func(foo, resolve(foo, 'a')); + + expect(actual[symbol]).toBe(1); + expect(actual[symbol2]).toBe(2); + expect(symbol3 in actual).toBeFalsy(); + } + }); + + it(`\`_.${methodName}\` should create an object with omitted symbols`, () => { + function Foo() { + this.a = 0; + this[symbol] = 1; + } + + if (Symbol) { + const symbol2 = Symbol('b'); + Foo.prototype[symbol2] = 2; + + const symbol3 = Symbol('c'); + defineProperty(Foo.prototype, symbol3, { + configurable: true, + enumerable: false, + writable: true, + value: 3, + }); + + const foo = new Foo(); + let actual = func(foo, resolve(foo, symbol)); + + expect(actual.a).toBe(0); + expect(symbol in actual).toBeFalsy(); + expect(actual[symbol2]).toBe(2); + expect(symbol3 in actual).toBeFalsy(); + + actual = func(foo, resolve(foo, symbol2)); + + expect(actual.a).toBe(0); + expect(actual[symbol]).toBe(1); + expect(symbol2 in actual).toBeFalsy(); + expect(symbol3 in actual).toBeFalsy(); + } + }); + + it(`\`_.${methodName}\` should work with an array \`object\``, () => { + const array = [1, 2, 3]; + expect(func(array, resolve(array, ['0', '2']))).toEqual({ 1: 2 }); + }); + }); +}); diff --git a/test/omit.js b/test/omit.js deleted file mode 100644 index 7bde8dc8d3..0000000000 --- a/test/omit.js +++ /dev/null @@ -1,69 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, toArgs, objectProto, stringProto } from './utils.js'; -import omit from '../omit.js'; - -describe('omit', function() { - var args = toArgs(['a', 'c']), - object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, - nested = { 'a': 1, 'b': { 'c': 2, 'd': 3 } }; - - it('should flatten `paths`', function() { - assert.deepStrictEqual(omit(object, 'a', 'c'), { 'b': 2, 'd': 4 }); - assert.deepStrictEqual(omit(object, ['a', 'd'], 'c'), { 'b': 2 }); - }); - - it('should support deep paths', function() { - assert.deepStrictEqual(omit(nested, 'b.c'), { 'a': 1, 'b': { 'd': 3} }); - }); - - it('should support path arrays', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }, - actual = omit(object, [['a.b']]); - - assert.deepStrictEqual(actual, { 'a': { 'b': 2 } }); - }); - - it('should omit a key over a path', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - assert.deepStrictEqual(omit(object, path), { 'a': { 'b': 2 } }); - }); - }); - - it('should coerce `paths` to strings', function() { - assert.deepStrictEqual(omit({ '0': 'a' }, 0), {}); - }); - - it('should return an empty object when `object` is nullish', function() { - lodashStable.each([null, undefined], function(value) { - objectProto.a = 1; - var actual = omit(value, 'valueOf'); - delete objectProto.a; - assert.deepStrictEqual(actual, {}); - }); - }); - - it('should work with a primitive `object`', function() { - stringProto.a = 1; - stringProto.b = 2; - - assert.deepStrictEqual(omit('', 'b'), { 'a': 1 }); - - delete stringProto.a; - delete stringProto.b; - }); - - it('should work with `arguments` object `paths`', function() { - assert.deepStrictEqual(omit(object, args), { 'b': 2, 'd': 4 }); - }); - - it('should not mutate `object`', function() { - lodashStable.each(['a', ['a'], 'a.b', ['a.b']], function(path) { - var object = { 'a': { 'b': 2 } }; - omit(object, path); - assert.deepStrictEqual(object, { 'a': { 'b': 2 } }); - }); - }); -}); diff --git a/test/omit.spec.js b/test/omit.spec.js new file mode 100644 index 0000000000..7a1b100673 --- /dev/null +++ b/test/omit.spec.js @@ -0,0 +1,68 @@ +import lodashStable from 'lodash'; +import { args, toArgs, objectProto, stringProto } from './utils'; +import omit from '../src/omit'; + +describe('omit', () => { + const args = toArgs(['a', 'c']); + const object = { a: 1, b: 2, c: 3, d: 4 }; + const nested = { a: 1, b: { c: 2, d: 3 } }; + + it('should flatten `paths`', () => { + expect(omit(object, 'a', 'c')).toEqual({ b: 2, d: 4 }); + expect(omit(object, ['a', 'd'], 'c')).toEqual({ b: 2 }); + }); + + it('should support deep paths', () => { + expect(omit(nested, 'b.c')).toEqual({ a: 1, b: { d: 3 } }); + }); + + it('should support path arrays', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + const actual = omit(object, [['a.b']]); + + expect(actual).toEqual({ a: { b: 2 } }); + }); + + it('should omit a key over a path', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + expect(omit(object, path)).toEqual({ a: { b: 2 } }); + }); + }); + + it('should coerce `paths` to strings', () => { + expect(omit({ 0: 'a' }, 0)).toEqual({}); + }); + + it('should return an empty object when `object` is nullish', () => { + lodashStable.each([null, undefined], (value) => { + objectProto.a = 1; + const actual = omit(value, 'valueOf'); + delete objectProto.a; + expect(actual).toEqual({}); + }); + }); + + it('should work with a primitive `object`', () => { + stringProto.a = 1; + stringProto.b = 2; + + expect(omit('', 'b')).toEqual({ a: 1 }); + + delete stringProto.a; + delete stringProto.b; + }); + + it('should work with `arguments` object `paths`', () => { + expect(omit(object, args)).toEqual({ b: 2, d: 4 }); + }); + + it('should not mutate `object`', () => { + lodashStable.each(['a', ['a'], 'a.b', ['a.b']], (path) => { + const object = { a: { b: 2 } }; + omit(object, path); + expect(object).toEqual({ a: { b: 2 } }); + }); + }); +}); diff --git a/test/omitBy.js b/test/omitBy.js deleted file mode 100644 index 28c6a9aff8..0000000000 --- a/test/omitBy.js +++ /dev/null @@ -1,14 +0,0 @@ -import assert from 'assert'; -import omitBy from '../omitBy.js'; - -describe('omitBy', function() { - it('should work with a predicate argument', function() { - var object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }; - - var actual = omitBy(object, function(n) { - return n != 2 && n != 4; - }); - - assert.deepStrictEqual(actual, { 'b': 2, 'd': 4 }); - }); -}); diff --git a/test/omitBy.spec.js b/test/omitBy.spec.js new file mode 100644 index 0000000000..e36a018d0e --- /dev/null +++ b/test/omitBy.spec.js @@ -0,0 +1,11 @@ +import omitBy from '../src/omitBy'; + +describe('omitBy', () => { + it('should work with a predicate argument', () => { + const object = { a: 1, b: 2, c: 3, d: 4 }; + + const actual = omitBy(object, (n) => n != 2 && n != 4); + + expect(actual, { b: 2).toEqual(d: 4 }); + }); +}); diff --git a/test/once.spec.js b/test/once.spec.js new file mode 100644 index 0000000000..254e3c4cb7 --- /dev/null +++ b/test/once.spec.js @@ -0,0 +1,33 @@ +import once from '../once'; + +describe('once', () => { + it('should invoke `func` once', () => { + let count = 0; + const resultFunc = once(() => ++count); + + once(); + expect(resultFunc()).toBe(1); + expect(count).toBe(1); + }); + + it('should ignore recursive calls', () => { + let count = 0; + + var resultFunc = once(() => { + resultFunc(); + return ++count; + }); + + expect(resultFunc()).toBe(1); + expect(count).toBe(1); + }); + + it('should not throw more than once', () => { + const resultFunc = once(() => { + throw new Error(); + }); + + expect(resultFunc).toThrow(); + expect(resultFunc).not.toThrow(); + }); +}); diff --git a/test/once.test.js b/test/once.test.js deleted file mode 100644 index 7279125e8b..0000000000 --- a/test/once.test.js +++ /dev/null @@ -1,36 +0,0 @@ -import assert from 'assert'; -import { _ } from './utils.js'; - -describe('once', function() { - it('should invoke `func` once', function() { - var count = 0, - once = _.once(function() { return ++count; }); - - once(); - assert.strictEqual(once(), 1); - assert.strictEqual(count, 1); - }); - - it('should ignore recursive calls', function() { - var count = 0; - - var once = _.once(function() { - once(); - return ++count; - }); - - assert.strictEqual(once(), 1); - assert.strictEqual(count, 1); - }); - - it('should not throw more than once', function() { - var once = _.once(function() { - throw new Error; - }); - - assert.throws(once); - - once(); - assert.ok(true); - }); -}); diff --git a/test/orderBy.js b/test/orderBy.js deleted file mode 100644 index 7aba5fe48d..0000000000 --- a/test/orderBy.js +++ /dev/null @@ -1,61 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey } from './utils.js'; -import orderBy from '../orderBy.js'; - -describe('orderBy', function() { - var objects = [ - { 'a': 'x', 'b': 3 }, - { 'a': 'y', 'b': 4 }, - { 'a': 'x', 'b': 1 }, - { 'a': 'y', 'b': 2 } - ]; - - var nestedObj = [ - { id: '4', address: { zipCode: 4, streetName: 'Beta' } }, - { id: '3', address: { zipCode: 3, streetName: 'Alpha' } }, - { id: '1', address: { zipCode: 1, streetName: 'Alpha' } }, - { id: '2', address: { zipCode: 2, streetName: 'Alpha' } }, - { id: '5', address: { zipCode: 4, streetName: 'Alpha' } }, - ]; - - - it('should sort by a single property by a specified order', function() { - var actual = orderBy(objects, 'a', 'desc'); - assert.deepStrictEqual(actual, [objects[1], objects[3], objects[0], objects[2]]); - }); - - it('should sort by nested key in array format', () => { - var actual = orderBy( - nestedObj, - [['address', 'zipCode'], ['address.streetName']], - ['asc', 'desc'], - ); - assert.deepStrictEqual(actual, [nestedObj[2], nestedObj[3], nestedObj[1], nestedObj[0], nestedObj[4]]); - }); - - it('should sort by multiple properties by specified orders', function() { - var actual = orderBy(objects, ['a', 'b'], ['desc', 'asc']); - assert.deepStrictEqual(actual, [objects[3], objects[1], objects[2], objects[0]]); - }); - - it('should sort by a property in ascending order when its order is not specified', function() { - var expected = [objects[2], objects[0], objects[3], objects[1]], - actual = orderBy(objects, ['a', 'b']); - - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(falsey, lodashStable.constant([objects[3], objects[1], objects[2], objects[0]])); - - actual = lodashStable.map(falsey, function(order, index) { - return orderBy(objects, ['a', 'b'], index ? ['desc', order] : ['desc']); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `orders` specified as string objects', function() { - var actual = orderBy(objects, ['a'], [Object('desc')]); - assert.deepStrictEqual(actual, [objects[1], objects[3], objects[0], objects[2]]); - }); -}); diff --git a/test/orderBy.spec.js b/test/orderBy.spec.js new file mode 100644 index 0000000000..d73a765d5d --- /dev/null +++ b/test/orderBy.spec.js @@ -0,0 +1,68 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import orderBy from '../src/orderBy'; + +describe('orderBy', () => { + const objects = [ + { a: 'x', b: 3 }, + { a: 'y', b: 4 }, + { a: 'x', b: 1 }, + { a: 'y', b: 2 }, + ]; + + const nestedObj = [ + { id: '4', address: { zipCode: 4, streetName: 'Beta' } }, + { id: '3', address: { zipCode: 3, streetName: 'Alpha' } }, + { id: '1', address: { zipCode: 1, streetName: 'Alpha' } }, + { id: '2', address: { zipCode: 2, streetName: 'Alpha' } }, + { id: '5', address: { zipCode: 4, streetName: 'Alpha' } }, + ]; + + it('should sort by a single property by a specified order', () => { + const actual = orderBy(objects, 'a', 'desc'); + expect(actual, [objects[1], objects[3], objects[0]).toEqual(objects[2]]); + }); + + it('should sort by nested key in array format', () => { + const actual = orderBy( + nestedObj, + [['address', 'zipCode'], ['address.streetName']], + ['asc', 'desc'], + ); + assert.deepStrictEqual(actual, [ + nestedObj[2], + nestedObj[3], + nestedObj[1], + nestedObj[0], + nestedObj[4], + ]); + }); + + it('should sort by multiple properties by specified orders', () => { + const actual = orderBy(objects, ['a', 'b'], ['desc', 'asc']); + expect(actual, [objects[3], objects[1], objects[2]).toEqual(objects[0]]); + }); + + it('should sort by a property in ascending order when its order is not specified', () => { + let expected = [objects[2], objects[0], objects[3], objects[1]]; + let actual = orderBy(objects, ['a', 'b']); + + expect(actual).toEqual(expected); + + expected = lodashStable.map( + falsey, + lodashStable.constant([objects[3], objects[1], objects[2], objects[0]]), + ); + + actual = lodashStable.map(falsey, (order, index) => + orderBy(objects, ['a', 'b'], index ? ['desc', order] : ['desc']), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with `orders` specified as string objects', () => { + const actual = orderBy(objects, ['a'], [Object('desc')]); + expect(actual, [objects[1], objects[3], objects[0]).toEqual(objects[2]]); + }); +}); diff --git a/test/over.js b/test/over.js deleted file mode 100644 index 93ce951861..0000000000 --- a/test/over.js +++ /dev/null @@ -1,58 +0,0 @@ -import assert from 'assert'; -import { _, slice } from './utils.js'; - -describe('over', function() { - it('should create a function that invokes `iteratees`', function() { - var over = _.over(Math.max, Math.min); - assert.deepStrictEqual(over(1, 2, 3, 4), [4, 1]); - }); - - it('should use `_.identity` when a predicate is nullish', function() { - var over = _.over(undefined, null); - assert.deepStrictEqual(over('a', 'b', 'c'), ['a', 'a']); - }); - - it('should work with `_.property` shorthands', function() { - var over = _.over('b', 'a'); - assert.deepStrictEqual(over({ 'a': 1, 'b': 2 }), [2, 1]); - }); - - it('should work with `_.matches` shorthands', function() { - var over = _.over({ 'b': 1 }, { 'a': 1 }); - assert.deepStrictEqual(over({ 'a': 1, 'b': 2 }), [false, true]); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - var over = _.over([['b', 2], ['a', 2]]); - - assert.deepStrictEqual(over({ 'a': 1, 'b': 2 }), [true, false]); - assert.deepStrictEqual(over({ 'a': 2, 'b': 1 }), [false, true]); - }); - - it('should differentiate between `_.property` and `_.matchesProperty` shorthands', function() { - var over = _.over(['a', 1]); - - assert.deepStrictEqual(over({ 'a': 1, '1': 2 }), [1, 2]); - assert.deepStrictEqual(over({ 'a': 2, '1': 1 }), [2, 1]); - - over = _.over([['a', 1]]); - - assert.deepStrictEqual(over({ 'a': 1 }), [true]); - assert.deepStrictEqual(over({ 'a': 2 }), [false]); - }); - - it('should provide arguments to predicates', function() { - var over = _.over(function() { - return slice.call(arguments); - }); - - assert.deepStrictEqual(over('a', 'b', 'c'), [['a', 'b', 'c']]); - }); - - it('should use `this` binding of function for `iteratees`', function() { - var over = _.over(function() { return this.b; }, function() { return this.a; }), - object = { 'over': over, 'a': 1, 'b': 2 }; - - assert.deepStrictEqual(object.over(), [2, 1]); - }); -}); diff --git a/test/over.spec.js b/test/over.spec.js new file mode 100644 index 0000000000..34610e921f --- /dev/null +++ b/test/over.spec.js @@ -0,0 +1,67 @@ +import { _, slice } from './utils'; + +describe('over', () => { + it('should create a function that invokes `iteratees`', () => { + const over = _.over(Math.max, Math.min); + expect(over(1, 2, 3, 4), [4).toEqual(1]); + }); + + it('should use `_.identity` when a predicate is nullish', () => { + const over = _.over(undefined, null); + expect(over('a', 'b', 'c'), ['a').toEqual('a']); + }); + + it('should work with `_.property` shorthands', () => { + const over = _.over('b', 'a'); + expect(over({ a: 1, b: 2 }), [2).toEqual(1]); + }); + + it('should work with `_.matches` shorthands', () => { + const over = _.over({ b: 1 }, { a: 1 }); + expect(over({ a: 1, b: 2 }), [false).toEqual(true]); + }); + + it('should work with `_.matchesProperty` shorthands', () => { + const over = _.over([ + ['b', 2], + ['a', 2], + ]); + + expect(over({ a: 1, b: 2 }), [true).toEqual(false]); + expect(over({ a: 2, b: 1 }), [false).toEqual(true]); + }); + + it('should differentiate between `_.property` and `_.matchesProperty` shorthands', () => { + let over = _.over(['a', 1]); + + expect(over({ a: 1, 1: 2 }), [1).toEqual(2]); + expect(over({ a: 2, 1: 1 }), [2).toEqual(1]); + + over = _.over([['a', 1]]); + + expect(over({ a: 1 })).toEqual([true]); + expect(over({ a: 2 })).toEqual([false]); + }); + + it('should provide arguments to predicates', () => { + const over = _.over(function () { + return slice.call(arguments); + }); + + expect(over('a', 'b', 'c'), [['a', 'b').toEqual('c']]); + }); + + it('should use `this` binding of function for `iteratees`', () => { + const over = _.over( + function () { + return this.b; + }, + function () { + return this.a; + }, + ); + const object = { over: over, a: 1, b: 2 }; + + expect(object.over(), [2).toEqual(1]); + }); +}); diff --git a/test/overArgs.js b/test/overArgs.js deleted file mode 100644 index f6e20db590..0000000000 --- a/test/overArgs.js +++ /dev/null @@ -1,82 +0,0 @@ -import assert from 'assert'; -import { slice, doubled, square, identity, noop } from './utils.js'; -import overArgs from '../overArgs.js'; - -describe('overArgs', function() { - function fn() { - return slice.call(arguments); - } - - it('should transform each argument', function() { - var over = overArgs(fn, doubled, square); - assert.deepStrictEqual(over(5, 10), [10, 100]); - }); - - it('should use `_.identity` when a predicate is nullish', function() { - var over = overArgs(fn, undefined, null); - assert.deepStrictEqual(over('a', 'b'), ['a', 'b']); - }); - - it('should work with `_.property` shorthands', function() { - var over = overArgs(fn, 'b', 'a'); - assert.deepStrictEqual(over({ 'b': 2 }, { 'a': 1 }), [2, 1]); - }); - - it('should work with `_.matches` shorthands', function() { - var over = overArgs(fn, { 'b': 1 }, { 'a': 1 }); - assert.deepStrictEqual(over({ 'b': 2 }, { 'a': 1 }), [false, true]); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - var over = overArgs(fn, [['b', 1], ['a', 1]]); - assert.deepStrictEqual(over({ 'b': 2 }, { 'a': 1 }), [false, true]); - }); - - it('should differentiate between `_.property` and `_.matchesProperty` shorthands', function() { - var over = overArgs(fn, ['a', 1]); - assert.deepStrictEqual(over({ 'a': 1 }, { '1': 2 }), [1, 2]); - - over = overArgs(fn, [['a', 1]]); - assert.deepStrictEqual(over({ 'a': 1 }), [true]); - }); - - it('should flatten `transforms`', function() { - var over = overArgs(fn, [doubled, square], String); - assert.deepStrictEqual(over(5, 10, 15), [10, 100, '15']); - }); - - it('should not transform any argument greater than the number of transforms', function() { - var over = overArgs(fn, doubled, square); - assert.deepStrictEqual(over(5, 10, 18), [10, 100, 18]); - }); - - it('should not transform any arguments if no transforms are given', function() { - var over = overArgs(fn); - assert.deepStrictEqual(over(5, 10, 18), [5, 10, 18]); - }); - - it('should not pass `undefined` if there are more transforms than arguments', function() { - var over = overArgs(fn, doubled, identity); - assert.deepStrictEqual(over(5), [10]); - }); - - it('should provide the correct argument to each transform', function() { - var argsList = [], - transform = function() { argsList.push(slice.call(arguments)); }, - over = overArgs(noop, transform, transform, transform); - - over('a', 'b'); - assert.deepStrictEqual(argsList, [['a'], ['b']]); - }); - - it('should use `this` binding of function for `transforms`', function() { - var over = overArgs(function(x) { - return this[x]; - }, function(x) { - return this === x; - }); - - var object = { 'over': over, 'true': 1 }; - assert.strictEqual(object.over(object), 1); - }); -}); diff --git a/test/overArgs.spec.js b/test/overArgs.spec.js new file mode 100644 index 0000000000..968a384471 --- /dev/null +++ b/test/overArgs.spec.js @@ -0,0 +1,89 @@ +import { slice, doubled, square, identity, noop } from './utils'; +import overArgs from '../src/overArgs'; + +describe('overArgs', () => { + function fn() { + return slice.call(arguments); + } + + it('should transform each argument', () => { + const over = overArgs(fn, doubled, square); + expect(over(5, 10), [10).toEqual(100]); + }); + + it('should use `_.identity` when a predicate is nullish', () => { + const over = overArgs(fn, undefined, null); + expect(over('a', 'b'), ['a').toEqual('b']); + }); + + it('should work with `_.property` shorthands', () => { + const over = overArgs(fn, 'b', 'a'); + expect(over({ b: 2 }, { a: 1 }), [2).toEqual(1]); + }); + + it('should work with `_.matches` shorthands', () => { + const over = overArgs(fn, { b: 1 }, { a: 1 }); + expect(over({ b: 2 }, { a: 1 }), [false).toEqual(true]); + }); + + it('should work with `_.matchesProperty` shorthands', () => { + const over = overArgs(fn, [ + ['b', 1], + ['a', 1], + ]); + expect(over({ b: 2 }, { a: 1 }), [false).toEqual(true]); + }); + + it('should differentiate between `_.property` and `_.matchesProperty` shorthands', () => { + let over = overArgs(fn, ['a', 1]); + expect(over({ a: 1 }, { 1: 2 }), [1).toEqual(2]); + + over = overArgs(fn, [['a', 1]]); + expect(over({ a: 1 })).toEqual([true]); + }); + + it('should flatten `transforms`', () => { + const over = overArgs(fn, [doubled, square], String); + expect(over(5, 10, 15), [10, 100).toEqual('15']); + }); + + it('should not transform any argument greater than the number of transforms', () => { + const over = overArgs(fn, doubled, square); + expect(over(5, 10, 18), [10, 100).toEqual(18]); + }); + + it('should not transform any arguments if no transforms are given', () => { + const over = overArgs(fn); + expect(over(5, 10, 18), [5, 10).toEqual(18]); + }); + + it('should not pass `undefined` if there are more transforms than arguments', () => { + const over = overArgs(fn, doubled, identity); + expect(over(5)).toEqual([10]); + }); + + it('should provide the correct argument to each transform', () => { + const argsList = []; + const transform = function () { + argsList.push(slice.call(arguments)); + }; + const over = overArgs(noop, transform, transform, transform); + + over('a', 'b'); + expect(argsList, [['a']).toEqual(['b']]); + }); + + it('should use `this` binding of function for `transforms`', () => { + const over = overArgs( + function (x) { + return this[x]; + }, + function (x) { + return this === x; + }, + ); + + const object = { over: over, true: 1 }; + expect(object.over(object)).toBe(1); + }); +}); diff --git a/test/overEvery.js b/test/overEvery.js deleted file mode 100644 index 7ce135cf9d..0000000000 --- a/test/overEvery.js +++ /dev/null @@ -1,87 +0,0 @@ -import assert from 'assert'; -import { stubTrue, stubOne, stubA, stubFalse, slice } from './utils.js'; -import overEvery from '../overEvery.js'; - -describe('overEvery', function() { - it('should create a function that returns `true` if all predicates return truthy', function() { - var over = overEvery(stubTrue, stubOne, stubA); - assert.strictEqual(over(), true); - }); - - it('should return `false` as soon as a predicate returns falsey', function() { - var count = 0, - countFalse = function() { count++; return false; }, - countTrue = function() { count++; return true; }, - over = overEvery(countTrue, countFalse, countTrue); - - assert.strictEqual(over(), false); - assert.strictEqual(count, 2); - }); - - it('should use `_.identity` when a predicate is nullish', function() { - var over = overEvery(undefined, null); - - assert.strictEqual(over(true), true); - assert.strictEqual(over(false), false); - }); - - it('should work with `_.property` shorthands', function() { - var over = overEvery('b', 'a'); - - assert.strictEqual(over({ 'a': 1, 'b': 1 }), true); - assert.strictEqual(over({ 'a': 0, 'b': 1 }), false); - }); - - it('should work with `_.matches` shorthands', function() { - var over = overEvery({ 'b': 2 }, { 'a': 1 }); - - assert.strictEqual(over({ 'a': 1, 'b': 2 }), true); - assert.strictEqual(over({ 'a': 0, 'b': 2 }), false); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - var over = overEvery([['b', 2], ['a', 1]]); - - assert.strictEqual(over({ 'a': 1, 'b': 2 }), true); - assert.strictEqual(over({ 'a': 0, 'b': 2 }), false); - }); - - it('should differentiate between `_.property` and `_.matchesProperty` shorthands', function() { - var over = overEvery(['a', 1]); - - assert.strictEqual(over({ 'a': 1, '1': 1 }), true); - assert.strictEqual(over({ 'a': 1, '1': 0 }), false); - assert.strictEqual(over({ 'a': 0, '1': 1 }), false); - - over = overEvery([['a', 1]]); - - assert.strictEqual(over({ 'a': 1 }), true); - assert.strictEqual(over({ 'a': 2 }), false); - }); - - it('should flatten `predicates`', function() { - var over = overEvery(stubTrue, [stubFalse]); - assert.strictEqual(over(), false); - }); - - it('should provide arguments to predicates', function() { - var args; - - var over = overEvery(function() { - args = slice.call(arguments); - }); - - over('a', 'b', 'c'); - assert.deepStrictEqual(args, ['a', 'b', 'c']); - }); - - it('should use `this` binding of function for `predicates`', function() { - var over = overEvery(function() { return this.b; }, function() { return this.a; }), - object = { 'over': over, 'a': 1, 'b': 2 }; - - assert.strictEqual(object.over(), true); - - object.a = 0; - assert.strictEqual(object.over(), false); - }); -}); diff --git a/test/overEvery.spec.js b/test/overEvery.spec.js new file mode 100644 index 0000000000..b611fe8bbc --- /dev/null +++ b/test/overEvery.spec.js @@ -0,0 +1,102 @@ +import { stubTrue, stubOne, stubA, stubFalse, slice } from './utils'; +import overEvery from '../src/overEvery'; + +describe('overEvery', () => { + it('should create a function that returns `true` if all predicates return truthy', () => { + const over = overEvery(stubTrue, stubOne, stubA); + expect(over()).toBe(true); + }); + + it('should return `false` as soon as a predicate returns falsey', () => { + let count = 0; + const countFalse = function () { + count++; + return false; + }; + const countTrue = function () { + count++; + return true; + }; + const over = overEvery(countTrue, countFalse, countTrue); + + expect(over()).toBe(false); + expect(count).toBe(2); + }); + + it('should use `_.identity` when a predicate is nullish', () => { + const over = overEvery(undefined, null); + + expect(over(true)).toBe(true); + expect(over(false)).toBe(false); + }); + + it('should work with `_.property` shorthands', () => { + const over = overEvery('b', 'a'); + + expect(over({ a: 1, b: 1 })).toBe(true); + expect(over({ a: 0, b: 1 })).toBe(false); + }); + + it('should work with `_.matches` shorthands', () => { + const over = overEvery({ b: 2 }, { a: 1 }); + + expect(over({ a: 1, b: 2 })).toBe(true); + expect(over({ a: 0, b: 2 })).toBe(false); + }); + + it('should work with `_.matchesProperty` shorthands', () => { + const over = overEvery([ + ['b', 2], + ['a', 1], + ]); + + expect(over({ a: 1, b: 2 })).toBe(true); + expect(over({ a: 0, b: 2 })).toBe(false); + }); + + it('should differentiate between `_.property` and `_.matchesProperty` shorthands', () => { + let over = overEvery(['a', 1]); + + expect(over({ a: 1, 1: 1 })).toBe(true); + expect(over({ a: 1, 1: 0 })).toBe(false); + expect(over({ a: 0, 1: 1 })).toBe(false); + + over = overEvery([['a', 1]]); + + expect(over({ a: 1 })).toBe(true); + expect(over({ a: 2 })).toBe(false); + }); + + it('should flatten `predicates`', () => { + const over = overEvery(stubTrue, [stubFalse]); + expect(over()).toBe(false); + }); + + it('should provide arguments to predicates', () => { + let args; + + const over = overEvery(function () { + args = slice.call(arguments); + }); + + over('a', 'b', 'c'); + expect(args).toEqual(['a', 'b', 'c']); + }); + + it('should use `this` binding of function for `predicates`', () => { + const over = overEvery( + function () { + return this.b; + }, + function () { + return this.a; + }, + ); + const object = { over: over, a: 1, b: 2 }; + + expect(object.over()).toBe(true); + + object.a = 0; + expect(object.over()).toBe(false); + }); +}); diff --git a/test/overSome.js b/test/overSome.js deleted file mode 100644 index 07ecaa22c1..0000000000 --- a/test/overSome.js +++ /dev/null @@ -1,98 +0,0 @@ -import assert from 'assert'; -import { stubFalse, stubOne, stubString, stubNull, stubA, stubZero, stubTrue, slice } from './utils.js'; -import overSome from '../overSome.js'; - -describe('overSome', function() { - it('should create a function that returns `true` if any predicates return truthy', function() { - var over = overSome(stubFalse, stubOne, stubString); - assert.strictEqual(over(), true); - - over = overSome(stubNull, stubA, stubZero); - assert.strictEqual(over(), true); - }); - - it('should return `true` as soon as `predicate` returns truthy', function() { - var count = 0, - countFalse = function() { count++; return false; }, - countTrue = function() { count++; return true; }, - over = overSome(countFalse, countTrue, countFalse); - - assert.strictEqual(over(), true); - assert.strictEqual(count, 2); - }); - - it('should return `false` if all predicates return falsey', function() { - var over = overSome(stubFalse, stubFalse, stubFalse); - assert.strictEqual(over(), false); - - over = overSome(stubNull, stubZero, stubString); - assert.strictEqual(over(), false); - }); - - it('should use `_.identity` when a predicate is nullish', function() { - var over = overSome(undefined, null); - - assert.strictEqual(over(true), true); - assert.strictEqual(over(false), false); - }); - - it('should work with `_.property` shorthands', function() { - var over = overSome('b', 'a'); - - assert.strictEqual(over({ 'a': 1, 'b': 0 }), true); - assert.strictEqual(over({ 'a': 0, 'b': 0 }), false); - }); - - it('should work with `_.matches` shorthands', function() { - var over = overSome({ 'b': 2 }, { 'a': 1 }); - - assert.strictEqual(over({ 'a': 0, 'b': 2 }), true); - assert.strictEqual(over({ 'a': 0, 'b': 0 }), false); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - var over = overSome([['b', 2], ['a', 1]]); - - assert.strictEqual(over({ 'a': 0, 'b': 2 }), true); - assert.strictEqual(over({ 'a': 0, 'b': 0 }), false); - }); - - it('should differentiate between `_.property` and `_.matchesProperty` shorthands', function() { - var over = overSome(['a', 1]); - - assert.strictEqual(over({ 'a': 0, '1': 0 }), false); - assert.strictEqual(over({ 'a': 1, '1': 0 }), true); - assert.strictEqual(over({ 'a': 0, '1': 1 }), true); - - over = overSome([['a', 1]]); - - assert.strictEqual(over({ 'a': 1 }), true); - assert.strictEqual(over({ 'a': 2 }), false); - }); - - it('should flatten `predicates`', function() { - var over = overSome(stubFalse, [stubTrue]); - assert.strictEqual(over(), true); - }); - - it('should provide arguments to predicates', function() { - var args; - - var over = overSome(function() { - args = slice.call(arguments); - }); - - over('a', 'b', 'c'); - assert.deepStrictEqual(args, ['a', 'b', 'c']); - }); - - it('should use `this` binding of function for `predicates`', function() { - var over = overSome(function() { return this.b; }, function() { return this.a; }), - object = { 'over': over, 'a': 1, 'b': 2 }; - - assert.strictEqual(object.over(), true); - - object.a = object.b = 0; - assert.strictEqual(object.over(), false); - }); -}); diff --git a/test/overSome.spec.js b/test/overSome.spec.js new file mode 100644 index 0000000000..7cc04a7107 --- /dev/null +++ b/test/overSome.spec.js @@ -0,0 +1,122 @@ +import { + stubFalse, + stubOne, + stubString, + stubNull, + stubA, + stubZero, + stubTrue, + slice, +} from './utils'; +import overSome from '../src/overSome'; + +describe('overSome', () => { + it('should create a function that returns `true` if any predicates return truthy', () => { + let over = overSome(stubFalse, stubOne, stubString); + expect(over()).toBe(true); + + over = overSome(stubNull, stubA, stubZero); + expect(over()).toBe(true); + }); + + it('should return `true` as soon as `predicate` returns truthy', () => { + let count = 0; + const countFalse = function () { + count++; + return false; + }; + const countTrue = function () { + count++; + return true; + }; + const over = overSome(countFalse, countTrue, countFalse); + + expect(over()).toBe(true); + expect(count).toBe(2); + }); + + it('should return `false` if all predicates return falsey', () => { + let over = overSome(stubFalse, stubFalse, stubFalse); + expect(over()).toBe(false); + + over = overSome(stubNull, stubZero, stubString); + expect(over()).toBe(false); + }); + + it('should use `_.identity` when a predicate is nullish', () => { + const over = overSome(undefined, null); + + expect(over(true)).toBe(true); + expect(over(false)).toBe(false); + }); + + it('should work with `_.property` shorthands', () => { + const over = overSome('b', 'a'); + + expect(over({ a: 1, b: 0 })).toBe(true); + expect(over({ a: 0, b: 0 })).toBe(false); + }); + + it('should work with `_.matches` shorthands', () => { + const over = overSome({ b: 2 }, { a: 1 }); + + expect(over({ a: 0, b: 2 })).toBe(true); + expect(over({ a: 0, b: 0 })).toBe(false); + }); + + it('should work with `_.matchesProperty` shorthands', () => { + const over = overSome([ + ['b', 2], + ['a', 1], + ]); + + expect(over({ a: 0, b: 2 })).toBe(true); + expect(over({ a: 0, b: 0 })).toBe(false); + }); + + it('should differentiate between `_.property` and `_.matchesProperty` shorthands', () => { + let over = overSome(['a', 1]); + + expect(over({ a: 0, 1: 0 })).toBe(false); + expect(over({ a: 1, 1: 0 })).toBe(true); + expect(over({ a: 0, 1: 1 })).toBe(true); + + over = overSome([['a', 1]]); + + expect(over({ a: 1 })).toBe(true); + expect(over({ a: 2 })).toBe(false); + }); + + it('should flatten `predicates`', () => { + const over = overSome(stubFalse, [stubTrue]); + expect(over()).toBe(true); + }); + + it('should provide arguments to predicates', () => { + let args; + + const over = overSome(function () { + args = slice.call(arguments); + }); + + over('a', 'b', 'c'); + expect(args, ['a', 'b').toEqual('c']); + }); + + it('should use `this` binding of function for `predicates`', () => { + const over = overSome( + function () { + return this.b; + }, + function () { + return this.a; + }, + ); + const object = { over: over, a: 1, b: 2 }; + + expect(object.over()).toBe(true); + + object.a = object.b = 0; + expect(object.over()).toBe(false); + }); +}); diff --git a/test/pad-methods.js b/test/pad-methods.js deleted file mode 100644 index 8d2e61a043..0000000000 --- a/test/pad-methods.js +++ /dev/null @@ -1,51 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; -import pad from '../pad.js'; - -describe('pad methods', function() { - lodashStable.each(['pad', 'padStart', 'padEnd'], function(methodName) { - var func = _[methodName], - isPad = methodName == 'pad', - isStart = methodName == 'padStart', - string = 'abc'; - - it('`_.' + methodName + '` should not pad if string is >= `length`', function() { - assert.strictEqual(func(string, 2), string); - assert.strictEqual(func(string, 3), string); - }); - - it('`_.' + methodName + '` should treat negative `length` as `0`', function() { - lodashStable.each([0, -2], function(length) { - assert.strictEqual(func(string, length), string); - }); - }); - - it('`_.' + methodName + '` should coerce `length` to a number', function() { - lodashStable.each(['', '4'], function(length) { - var actual = length ? (isStart ? ' abc' : 'abc ') : string; - assert.strictEqual(func(string, length), actual); - }); - }); - - it('`_.' + methodName + '` should treat nullish values as empty strings', function() { - lodashStable.each([undefined, '_-'], function(chars) { - var expected = chars ? (isPad ? '__' : chars) : ' '; - assert.strictEqual(func(null, 2, chars), expected); - assert.strictEqual(func(undefined, 2, chars), expected); - assert.strictEqual(func('', 2, chars), expected); - }); - }); - - it('`_.' + methodName + '` should return `string` when `chars` coerces to an empty string', function() { - var values = ['', Object('')], - expected = lodashStable.map(values, lodashStable.constant(string)); - - var actual = lodashStable.map(values, function(value) { - return pad(string, 6, value); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/pad-methods.spec.js b/test/pad-methods.spec.js new file mode 100644 index 0000000000..241381f457 --- /dev/null +++ b/test/pad-methods.spec.js @@ -0,0 +1,48 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; +import pad from '../src/pad'; + +describe('pad methods', () => { + lodashStable.each(['pad', 'padStart', 'padEnd'], (methodName) => { + const func = _[methodName]; + const isPad = methodName === 'pad'; + const isStart = methodName === 'padStart'; + const string = 'abc'; + + it(`\`_.${methodName}\` should not pad if string is >= \`length\``, () => { + expect(func(string, 2)).toBe(string); + expect(func(string, 3)).toBe(string); + }); + + it(`\`_.${methodName}\` should treat negative \`length\` as \`0\``, () => { + lodashStable.each([0, -2], (length) => { + expect(func(string, length)).toBe(string); + }); + }); + + it(`\`_.${methodName}\` should coerce \`length\` to a number`, () => { + lodashStable.each(['', '4'], (length) => { + const actual = length ? (isStart ? ' abc' : 'abc ') : string; + expect(func(string, length)).toBe(actual); + }); + }); + + it(`\`_.${methodName}\` should treat nullish values as empty strings`, () => { + lodashStable.each([undefined, '_-'], (chars) => { + const expected = chars ? (isPad ? '__' : chars) : ' '; + expect(func(null, 2, chars)).toBe(expected); + expect(func(undefined, 2, chars)).toBe(expected); + expect(func('', 2, chars)).toBe(expected); + }); + }); + + it(`\`_.${methodName}\` should return \`string\` when \`chars\` coerces to an empty string`, () => { + const values = ['', Object('')]; + const expected = lodashStable.map(values, lodashStable.constant(string)); + + const actual = lodashStable.map(values, (value) => pad(string, 6, value)); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/pad.js b/test/pad.js deleted file mode 100644 index 8a62fe036d..0000000000 --- a/test/pad.js +++ /dev/null @@ -1,35 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubTrue } from './utils.js'; -import pad from '../pad.js'; - -describe('pad', function() { - var string = 'abc'; - - it('should pad a string to a given length', function() { - var values = [, undefined], - expected = lodashStable.map(values, lodashStable.constant(' abc ')); - - var actual = lodashStable.map(values, function(value, index) { - return index ? pad(string, 6, value) : pad(string, 6); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should truncate pad characters to fit the pad length', function() { - assert.strictEqual(pad(string, 8), ' abc '); - assert.strictEqual(pad(string, 8, '_-'), '_-abc_-_'); - }); - - it('should coerce `string` to a string', function() { - var values = [Object(string), { 'toString': lodashStable.constant(string) }], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return pad(value, 6) === ' abc '; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/pad.spec.js b/test/pad.spec.js new file mode 100644 index 0000000000..fb0b64906d --- /dev/null +++ b/test/pad.spec.js @@ -0,0 +1,32 @@ +import lodashStable from 'lodash'; +import { stubTrue } from './utils'; +import pad from '../src/pad'; + +describe('pad', () => { + const string = 'abc'; + + it('should pad a string to a given length', () => { + const values = [, undefined]; + const expected = lodashStable.map(values, lodashStable.constant(' abc ')); + + const actual = lodashStable.map(values, (value, index) => + index ? pad(string, 6, value) : pad(string, 6), + ); + + expect(actual).toEqual(expected); + }); + + it('should truncate pad characters to fit the pad length', () => { + expect(pad(string, 8)).toBe(' abc '); + expect(pad(string, 8, '_-')).toBe('_-abc_-_'); + }); + + it('should coerce `string` to a string', () => { + const values = [Object(string), { toString: lodashStable.constant(string) }]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => pad(value, 6) === ' abc '); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/padEnd.js b/test/padEnd.js deleted file mode 100644 index 6edb5232c6..0000000000 --- a/test/padEnd.js +++ /dev/null @@ -1,34 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubTrue } from './utils.js'; -import padEnd from '../padEnd.js'; - -describe('padEnd', function() { - var string = 'abc'; - - it('should pad a string to a given length', function() { - var values = [, undefined], - expected = lodashStable.map(values, lodashStable.constant('abc ')); - - var actual = lodashStable.map(values, function(value, index) { - return index ? padEnd(string, 6, value) : padEnd(string, 6); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should truncate pad characters to fit the pad length', function() { - assert.strictEqual(padEnd(string, 6, '_-'), 'abc_-_'); - }); - - it('should coerce `string` to a string', function() { - var values = [Object(string), { 'toString': lodashStable.constant(string) }], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return padEnd(value, 6) === 'abc '; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/padEnd.spec.js b/test/padEnd.spec.js new file mode 100644 index 0000000000..f696f0d6c6 --- /dev/null +++ b/test/padEnd.spec.js @@ -0,0 +1,31 @@ +import lodashStable from 'lodash'; +import { stubTrue } from './utils'; +import padEnd from '../src/padEnd'; + +describe('padEnd', () => { + const string = 'abc'; + + it('should pad a string to a given length', () => { + const values = [, undefined]; + const expected = lodashStable.map(values, lodashStable.constant('abc ')); + + const actual = lodashStable.map(values, (value, index) => + index ? padEnd(string, 6, value) : padEnd(string, 6), + ); + + expect(actual).toEqual(expected); + }); + + it('should truncate pad characters to fit the pad length', () => { + expect(padEnd(string, 6, '_-')).toBe('abc_-_'); + }); + + it('should coerce `string` to a string', () => { + const values = [Object(string), { toString: lodashStable.constant(string) }]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => padEnd(value, 6) === 'abc '); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/padStart.js b/test/padStart.js deleted file mode 100644 index 9ec9988582..0000000000 --- a/test/padStart.js +++ /dev/null @@ -1,34 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubTrue } from './utils.js'; -import padStart from '../padStart.js'; - -describe('padStart', function() { - var string = 'abc'; - - it('should pad a string to a given length', function() { - var values = [, undefined], - expected = lodashStable.map(values, lodashStable.constant(' abc')); - - var actual = lodashStable.map(values, function(value, index) { - return index ? padStart(string, 6, value) : padStart(string, 6); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should truncate pad characters to fit the pad length', function() { - assert.strictEqual(padStart(string, 6, '_-'), '_-_abc'); - }); - - it('should coerce `string` to a string', function() { - var values = [Object(string), { 'toString': lodashStable.constant(string) }], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return padStart(value, 6) === ' abc'; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/padStart.spec.js b/test/padStart.spec.js new file mode 100644 index 0000000000..005666b20d --- /dev/null +++ b/test/padStart.spec.js @@ -0,0 +1,31 @@ +import lodashStable from 'lodash'; +import { stubTrue } from './utils'; +import padStart from '../src/padStart'; + +describe('padStart', () => { + const string = 'abc'; + + it('should pad a string to a given length', () => { + const values = [, undefined]; + const expected = lodashStable.map(values, lodashStable.constant(' abc')); + + const actual = lodashStable.map(values, (value, index) => + index ? padStart(string, 6, value) : padStart(string, 6), + ); + + expect(actual).toEqual(expected); + }); + + it('should truncate pad characters to fit the pad length', () => { + expect(padStart(string, 6, '_-')).toBe('_-_abc'); + }); + + it('should coerce `string` to a string', () => { + const values = [Object(string), { toString: lodashStable.constant(string) }]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => padStart(value, 6) === ' abc'); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/parseInt.js b/test/parseInt.js deleted file mode 100644 index d068cbfe4e..0000000000 --- a/test/parseInt.js +++ /dev/null @@ -1,81 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { lodashBizarro, whitespace, stubZero } from './utils.js'; -import parseInt from '../parseInt.js'; - -describe('parseInt', function() { - it('should accept a `radix`', function() { - var expected = lodashStable.range(2, 37); - - var actual = lodashStable.map(expected, function(radix) { - return parseInt('10', radix); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should use a radix of `10`, for non-hexadecimals, if `radix` is `undefined` or `0`', function() { - assert.strictEqual(parseInt('10'), 10); - assert.strictEqual(parseInt('10', 0), 10); - assert.strictEqual(parseInt('10', 10), 10); - assert.strictEqual(parseInt('10', undefined), 10); - }); - - it('should use a radix of `16`, for hexadecimals, if `radix` is `undefined` or `0`', function() { - lodashStable.each(['0x20', '0X20'], function(string) { - assert.strictEqual(parseInt(string), 32); - assert.strictEqual(parseInt(string, 0), 32); - assert.strictEqual(parseInt(string, 16), 32); - assert.strictEqual(parseInt(string, undefined), 32); - }); - }); - - it('should use a radix of `10` for string with leading zeros', function() { - assert.strictEqual(parseInt('08'), 8); - assert.strictEqual(parseInt('08', 10), 8); - }); - - it('should parse strings with leading whitespace', function() { - var expected = [8, 8, 10, 10, 32, 32, 32, 32]; - - lodashStable.times(2, function(index) { - var actual = [], - func = (index ? (lodashBizarro || {}) : _).parseInt; - - if (func) { - lodashStable.times(2, function(otherIndex) { - var string = otherIndex ? '10' : '08'; - actual.push( - func(whitespace + string, 10), - func(whitespace + string) - ); - }); - - lodashStable.each(['0x20', '0X20'], function(string) { - actual.push( - func(whitespace + string), - func(whitespace + string, 16) - ); - }); - - assert.deepStrictEqual(actual, expected); - } - }); - }); - - it('should coerce `radix` to a number', function() { - var object = { 'valueOf': stubZero }; - assert.strictEqual(parseInt('08', object), 8); - assert.strictEqual(parseInt('0x20', object), 32); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var strings = lodashStable.map(['6', '08', '10'], Object), - actual = lodashStable.map(strings, parseInt); - - assert.deepStrictEqual(actual, [6, 8, 10]); - - actual = lodashStable.map('123', parseInt); - assert.deepStrictEqual(actual, [1, 2, 3]); - }); -}); diff --git a/test/parseInt.spec.js b/test/parseInt.spec.js new file mode 100644 index 0000000000..d139e0964c --- /dev/null +++ b/test/parseInt.spec.js @@ -0,0 +1,72 @@ +import lodashStable from 'lodash'; +import { lodashBizarro, whitespace, stubZero } from './utils'; +import parseInt from '../src/parseInt'; + +describe('parseInt', () => { + it('should accept a `radix`', () => { + const expected = lodashStable.range(2, 37); + + const actual = lodashStable.map(expected, (radix) => parseInt('10', radix)); + + expect(actual).toEqual(expected); + }); + + it('should use a radix of `10`, for non-hexadecimals, if `radix` is `undefined` or `0`', () => { + expect(parseInt('10')).toBe(10); + expect(parseInt('10', 0)).toBe(10); + expect(parseInt('10', 10)).toBe(10); + expect(parseInt('10', undefined)).toBe(10); + }); + + it('should use a radix of `16`, for hexadecimals, if `radix` is `undefined` or `0`', () => { + lodashStable.each(['0x20', '0X20'], (string) => { + expect(parseInt(string)).toBe(32); + expect(parseInt(string, 0)).toBe(32); + expect(parseInt(string, 16)).toBe(32); + expect(parseInt(string, undefined)).toBe(32); + }); + }); + + it('should use a radix of `10` for string with leading zeros', () => { + expect(parseInt('08')).toBe(8); + expect(parseInt('08', 10)).toBe(8); + }); + + it('should parse strings with leading whitespace', () => { + const expected = [8, 8, 10, 10, 32, 32, 32, 32]; + + lodashStable.times(2, (index) => { + const actual = []; + const func = (index ? lodashBizarro || {} : _).parseInt; + + if (func) { + lodashStable.times(2, (otherIndex) => { + const string = otherIndex ? '10' : '08'; + actual.push(func(whitespace + string, 10), func(whitespace + string)); + }); + + lodashStable.each(['0x20', '0X20'], (string) => { + actual.push(func(whitespace + string), func(whitespace + string, 16)); + }); + + expect(actual).toEqual(expected); + } + }); + }); + + it('should coerce `radix` to a number', () => { + const object = { valueOf: stubZero }; + expect(parseInt('08', object)).toBe(8); + expect(parseInt('0x20', object)).toBe(32); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const strings = lodashStable.map(['6', '08', '10'], Object); + let actual = lodashStable.map(strings, parseInt); + + expect(actual, [6, 8).toEqual(10]); + + actual = lodashStable.map('123', parseInt); + expect(actual, [1, 2).toEqual(3]); + }); +}); diff --git a/test/partial-methods.js b/test/partial-methods.js deleted file mode 100644 index 4509c99163..0000000000 --- a/test/partial-methods.js +++ /dev/null @@ -1,113 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, identity, slice } from './utils.js'; -import placeholder from '../placeholder.js'; -import curry from '../curry.js'; - -describe('partial methods', function() { - lodashStable.each(['partial', 'partialRight'], function(methodName) { - var func = _[methodName], - isPartial = methodName == 'partial', - ph = func.placeholder; - - it('`_.' + methodName + '` partially applies arguments', function() { - var par = func(identity, 'a'); - assert.strictEqual(par(), 'a'); - }); - - it('`_.' + methodName + '` creates a function that can be invoked with additional arguments', function() { - var fn = function(a, b) { return [a, b]; }, - par = func(fn, 'a'), - expected = isPartial ? ['a', 'b'] : ['b', 'a']; - - assert.deepStrictEqual(par('b'), expected); - }); - - it('`_.' + methodName + '` works when there are no partially applied arguments and the created function is invoked without additional arguments', function() { - var fn = function() { return arguments.length; }, - par = func(fn); - - assert.strictEqual(par(), 0); - }); - - it('`_.' + methodName + '` works when there are no partially applied arguments and the created function is invoked with additional arguments', function() { - var par = func(identity); - assert.strictEqual(par('a'), 'a'); - }); - - it('`_.' + methodName + '` should support placeholders', function() { - var fn = function() { return slice.call(arguments); }, - par = func(fn, ph, 'b', ph); - - assert.deepStrictEqual(par('a', 'c'), ['a', 'b', 'c']); - assert.deepStrictEqual(par('a'), ['a', 'b', undefined]); - assert.deepStrictEqual(par(), [undefined, 'b', undefined]); - - if (isPartial) { - assert.deepStrictEqual(par('a', 'c', 'd'), ['a', 'b', 'c', 'd']); - } else { - par = func(fn, ph, 'c', ph); - assert.deepStrictEqual(par('a', 'b', 'd'), ['a', 'b', 'c', 'd']); - } - }); - - it('`_.' + methodName + '` should use `_.placeholder` when set', function() { - var _ph = placeholder = {}, - fn = function() { return slice.call(arguments); }, - par = func(fn, _ph, 'b', ph), - expected = isPartial ? ['a', 'b', ph, 'c'] : ['a', 'c', 'b', ph]; - - assert.deepEqual(par('a', 'c'), expected); - delete placeholder; - }); - - it('`_.' + methodName + '` creates a function with a `length` of `0`', function() { - var fn = function(a, b, c) {}, - par = func(fn, 'a'); - - assert.strictEqual(par.length, 0); - }); - - it('`_.' + methodName + '` should ensure `new par` is an instance of `func`', function() { - function Foo(value) { - return value && object; - } - - var object = {}, - par = func(Foo); - - assert.ok(new par instanceof Foo); - assert.strictEqual(new par(true), object); - }); - - it('`_.' + methodName + '` should clone metadata for created functions', function() { - function greet(greeting, name) { - return greeting + ' ' + name; - } - - var par1 = func(greet, 'hi'), - par2 = func(par1, 'barney'), - par3 = func(par1, 'pebbles'); - - assert.strictEqual(par1('fred'), isPartial ? 'hi fred' : 'fred hi'); - assert.strictEqual(par2(), isPartial ? 'hi barney' : 'barney hi'); - assert.strictEqual(par3(), isPartial ? 'hi pebbles' : 'pebbles hi'); - }); - - it('`_.' + methodName + '` should work with curried functions', function() { - var fn = function(a, b, c) { return a + b + c; }, - curried = curry(func(fn, 1), 2); - - assert.strictEqual(curried(2, 3), 6); - assert.strictEqual(curried(2)(3), 6); - }); - - it('should work with placeholders and curried functions', function() { - var fn = function() { return slice.call(arguments); }, - curried = curry(fn), - par = func(curried, ph, 'b', ph, 'd'); - - assert.deepStrictEqual(par('a', 'c'), ['a', 'b', 'c', 'd']); - }); - }); -}); diff --git a/test/partial-methods.spec.js b/test/partial-methods.spec.js new file mode 100644 index 0000000000..6e260ce78a --- /dev/null +++ b/test/partial-methods.spec.js @@ -0,0 +1,124 @@ +import lodashStable from 'lodash'; +import { _, identity, slice } from './utils'; +import placeholder from '../src/placeholder'; +import curry from '../src/curry'; + +describe('partial methods', () => { + lodashStable.each(['partial', 'partialRight'], (methodName) => { + const func = _[methodName], + isPartial = methodName === 'partial', + ph = func.placeholder; + + it(`\`_.${methodName}\` partially applies arguments`, () => { + const par = func(identity, 'a'); + expect(par()).toBe('a'); + }); + + it(`\`_.${methodName}\` creates a function that can be invoked with additional arguments`, () => { + const fn = function (a, b) { + return [a, b]; + }, + par = func(fn, 'a'), + expected = isPartial ? ['a', 'b'] : ['b', 'a']; + + expect(par('b')).toEqual(expected); + }); + + it(`\`_.${methodName}\` works when there are no partially applied arguments and the created function is invoked without additional arguments`, () => { + const fn = function () { + return arguments.length; + }, + par = func(fn); + + expect(par()).toBe(0); + }); + + it(`\`_.${methodName}\` works when there are no partially applied arguments and the created function is invoked with additional arguments`, () => { + const par = func(identity); + expect(par('a')).toBe('a'); + }); + + it(`\`_.${methodName}\` should support placeholders`, () => { + let fn = function () { + return slice.call(arguments); + }, + par = func(fn, ph, 'b', ph); + + expect(par('a', 'c')).toEqual(['a', 'b', 'c']); + expect(par('a')).toEqual(['a', 'b', undefined]); + expect(par()).toEqual([undefined, 'b', undefined]); + + if (isPartial) { + expect(par('a', 'c', 'd')).toEqual(['a', 'b', 'c', 'd']); + } else { + par = func(fn, ph, 'c', ph); + expect(par('a', 'b', 'd')).toEqual(['a', 'b', 'c', 'd']); + } + }); + + it(`\`_.${methodName}\` should use \`_.placeholder\` when set`, () => { + const _ph = (placeholder = {}), + fn = function () { + return slice.call(arguments); + }, + par = func(fn, _ph, 'b', ph), + expected = isPartial ? ['a', 'b', ph, 'c'] : ['a', 'c', 'b', ph]; + + expect(par('a', 'c')).toEqual(expected); + delete placeholder; + }); + + it(`\`_.${methodName}\` creates a function with a \`length\` of \`0\``, () => { + const fn = function (a, b, c) {}, + par = func(fn, 'a'); + + expect(par.length).toBe(0); + }); + + it(`\`_.${methodName}\` should ensure \`new par\` is an instance of \`func\``, () => { + function Foo(value) { + return value && object; + } + + var object = {}, + par = func(Foo); + + expect(new par() instanceof Foo) + expect(new par(true)).toBe(object); + }); + + it(`\`_.${methodName}\` should clone metadata for created functions`, () => { + function greet(greeting, name) { + return `${greeting} ${name}`; + } + + const par1 = func(greet, 'hi'), + par2 = func(par1, 'barney'), + par3 = func(par1, 'pebbles'); + + expect(par1('fred')).toBe(isPartial ? 'hi fred' : 'fred hi'); + expect(par2()).toBe(isPartial ? 'hi barney' : 'barney hi'); + expect(par3()).toBe(isPartial ? 'hi pebbles' : 'pebbles hi'); + }); + + it(`\`_.${methodName}\` should work with curried functions`, () => { + const fn = function (a, b, c) { + return a + b + c; + }, + curried = curry(func(fn, 1), 2); + + expect(curried(2, 3)).toBe(6); + expect(curried(2)(3)).toBe(6); + }); + + it('should work with placeholders and curried functions', () => { + const fn = function () { + return slice.call(arguments); + }, + curried = curry(fn), + par = func(curried, ph, 'b', ph, 'd'); + + expect(par('a', 'c')).toEqual(['a', 'b', 'c', 'd']); + }); + }); +}); diff --git a/test/partialRight.js b/test/partialRight.js deleted file mode 100644 index 87d48e4ccb..0000000000 --- a/test/partialRight.js +++ /dev/null @@ -1,18 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import partialRight from '../partialRight.js'; -import mergeWith from '../mergeWith.js'; - -describe('partialRight', function() { - it('should work as a deep `_.defaults`', function() { - var object = { 'a': { 'b': 2 } }, - source = { 'a': { 'b': 3, 'c': 3 } }, - expected = { 'a': { 'b': 2, 'c': 3 } }; - - var defaultsDeep = partialRight(mergeWith, function deep(value, other) { - return lodashStable.isObject(value) ? mergeWith(value, other, deep) : value; - }); - - assert.deepStrictEqual(defaultsDeep(object, source), expected); - }); -}); diff --git a/test/partialRight.spec.js b/test/partialRight.spec.js new file mode 100644 index 0000000000..51e9226f6e --- /dev/null +++ b/test/partialRight.spec.js @@ -0,0 +1,17 @@ +import lodashStable from 'lodash'; +import partialRight from '../src/partialRight'; +import mergeWith from '../src/mergeWith'; + +describe('partialRight', () => { + it('should work as a deep `_.defaults`', () => { + const object = { a: { b: 2 } }; + const source = { a: { b: 3, c: 3 } }; + const expected = { a: { b: 2, c: 3 } }; + + const defaultsDeep = partialRight(mergeWith, function deep(value, other) { + return lodashStable.isObject(value) ? mergeWith(value, other, deep) : value; + }); + + expect(defaultsDeep(object, source)).toEqual(expected); + }); +}); diff --git a/test/partition.js b/test/partition.js deleted file mode 100644 index 1767548a10..0000000000 --- a/test/partition.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, stubTrue, stubFalse } from './utils.js'; -import partition from '../partition.js'; - -describe('partition', function() { - var array = [1, 0, 1]; - - it('should split elements into two groups by `predicate`', function() { - assert.deepStrictEqual(partition([], identity), [[], []]); - assert.deepStrictEqual(partition(array, stubTrue), [array, []]); - assert.deepStrictEqual(partition(array, stubFalse), [[], array]); - }); - - it('should use `_.identity` when `predicate` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([[1, 1], [0]])); - - var actual = lodashStable.map(values, function(value, index) { - return index ? partition(array, value) : partition(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var objects = [{ 'a': 1 }, { 'a': 1 }, { 'b': 2 }], - actual = partition(objects, 'a'); - - assert.deepStrictEqual(actual, [objects.slice(0, 2), objects.slice(2)]); - }); - - it('should work with a number for `predicate`', function() { - var array = [ - [1, 0], - [0, 1], - [1, 0] - ]; - - assert.deepStrictEqual(partition(array, 0), [[array[0], array[2]], [array[1]]]); - assert.deepStrictEqual(partition(array, 1), [[array[1]], [array[0], array[2]]]); - }); - - it('should work with an object for `collection`', function() { - var actual = partition({ 'a': 1.1, 'b': 0.2, 'c': 1.3 }, Math.floor); - assert.deepStrictEqual(actual, [[1.1, 1.3], [0.2]]); - }); -}); diff --git a/test/partition.spec.js b/test/partition.spec.js new file mode 100644 index 0000000000..87b456f5e6 --- /dev/null +++ b/test/partition.spec.js @@ -0,0 +1,47 @@ +import lodashStable from 'lodash'; +import { identity, stubTrue, stubFalse } from './utils'; +import partition from '../src/partition'; + +describe('partition', () => { + const array = [1, 0, 1]; + + it('should split elements into two groups by `predicate`', () => { + expect(partition([], identity), [[]).toEqual([]]); + expect(partition(array, stubTrue), [array).toEqual([]]); + expect(partition(array, stubFalse), [[]).toEqual(array]); + }); + + it('should use `_.identity` when `predicate` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([[1, 1], [0]])); + + const actual = lodashStable.map(values, (value, index) => + index ? partition(array, value) : partition(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const objects = [{ a: 1 }, { a: 1 }, { b: 2 }]; + const actual = partition(objects, 'a'); + + expect(actual, [objects.slice(0, 2)).toEqual(objects.slice(2)]); + }); + + it('should work with a number for `predicate`', () => { + const array = [ + [1, 0], + [0, 1], + [1, 0], + ]; + + expect(partition(array, 0), [[array[0], array[2]]).toEqual([array[1]]]); + expect(partition(array, 1), [[array[1]], [array[0]).toEqual(array[2]]]); + }); + + it('should work with an object for `collection`', () => { + const actual = partition({ a: 1.1, b: 0.2, c: 1.3 }, Math.floor); + expect(actual, [[1.1, 1.3]).toEqual([0.2]]); + }); +}); diff --git a/test/pick-methods.js b/test/pick-methods.js deleted file mode 100644 index 50eb1188da..0000000000 --- a/test/pick-methods.js +++ /dev/null @@ -1,85 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, symbol, defineProperty } from './utils.js'; - -describe('pick methods', function() { - lodashStable.each(['pick', 'pickBy'], function(methodName) { - var expected = { 'a': 1, 'c': 3 }, - func = _[methodName], - isPick = methodName == 'pick', - object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, - resolve = lodashStable.nthArg(1); - - if (methodName == 'pickBy') { - resolve = function(object, props) { - props = lodashStable.castArray(props); - return function(value) { - return lodashStable.some(props, function(key) { - key = lodashStable.isSymbol(key) ? key : lodashStable.toString(key); - return object[key] === value; - }); - }; - }; - } - it('`_.' + methodName + '` should create an object of picked string keyed properties', function() { - assert.deepStrictEqual(func(object, resolve(object, 'a')), { 'a': 1 }); - assert.deepStrictEqual(func(object, resolve(object, ['a', 'c'])), expected); - }); - - it('`_.' + methodName + '` should pick inherited string keyed properties', function() { - function Foo() {} - Foo.prototype = object; - - var foo = new Foo; - assert.deepStrictEqual(func(foo, resolve(foo, ['a', 'c'])), expected); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var object = { '-0': 'a', '0': 'b' }, - props = [-0, Object(-0), 0, Object(0)], - expected = [{ '-0': 'a' }, { '-0': 'a' }, { '0': 'b' }, { '0': 'b' }]; - - var actual = lodashStable.map(props, function(key) { - return func(object, resolve(object, key)); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should pick symbols', function() { - function Foo() { - this[symbol] = 1; - } - - if (Symbol) { - var symbol2 = Symbol('b'); - Foo.prototype[symbol2] = 2; - - var symbol3 = Symbol('c'); - defineProperty(Foo.prototype, symbol3, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': 3 - }); - - var foo = new Foo, - actual = func(foo, resolve(foo, [symbol, symbol2, symbol3])); - - assert.strictEqual(actual[symbol], 1); - assert.strictEqual(actual[symbol2], 2); - - if (isPick) { - assert.strictEqual(actual[symbol3], 3); - } else { - assert.ok(!(symbol3 in actual)); - } - } - }); - - it('`_.' + methodName + '` should work with an array `object`', function() { - var array = [1, 2, 3]; - assert.deepStrictEqual(func(array, resolve(array, '1')), { '1': 2 }); - }); - }); -}); diff --git a/test/pick-methods.spec.js b/test/pick-methods.spec.js new file mode 100644 index 0000000000..77690a58d2 --- /dev/null +++ b/test/pick-methods.spec.js @@ -0,0 +1,82 @@ +import lodashStable from 'lodash'; +import { _, symbol, defineProperty } from './utils'; + +describe('pick methods', () => { + lodashStable.each(['pick', 'pickBy'], (methodName) => { + const expected = { a: 1, c: 3 }; + const func = _[methodName]; + const isPick = methodName === 'pick'; + const object = { a: 1, b: 2, c: 3, d: 4 }; + let resolve = lodashStable.nthArg(1); + + if (methodName === 'pickBy') { + resolve = function (object, props) { + props = lodashStable.castArray(props); + return function (value) { + return lodashStable.some(props, (key) => { + key = lodashStable.isSymbol(key) ? key : lodashStable.toString(key); + return object[key] === value; + }); + }; + }; + } + it(`\`_.${methodName}\` should create an object of picked string keyed properties`, () => { + expect(func(object, resolve(object, 'a'))).toEqual({ a: 1 }); + expect(func(object, resolve(object, ['a', 'c']))).toEqual(expected); + }); + + it(`\`_.${methodName}\` should pick inherited string keyed properties`, () => { + function Foo() {} + Foo.prototype = object; + + const foo = new Foo(); + expect(func(foo, resolve(foo, ['a', 'c']))).toEqual(expected); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const object = { '-0': 'a', 0: 'b' }; + const props = [-0, Object(-0), 0, Object(0)]; + const expected = [{ '-0': 'a' }, { '-0': 'a' }, { 0: 'b' }, { 0: 'b' }]; + + const actual = lodashStable.map(props, (key) => func(object, resolve(object, key))); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should pick symbols`, () => { + function Foo() { + this[symbol] = 1; + } + + if (Symbol) { + const symbol2 = Symbol('b'); + Foo.prototype[symbol2] = 2; + + const symbol3 = Symbol('c'); + defineProperty(Foo.prototype, symbol3, { + configurable: true, + enumerable: false, + writable: true, + value: 3, + }); + + const foo = new Foo(); + const actual = func(foo, resolve(foo, [symbol, symbol2, symbol3])); + + expect(actual[symbol]).toBe(1); + expect(actual[symbol2]).toBe(2); + + if (isPick) { + expect(actual[symbol3]).toBe(3); + } else { + expect(symbol3 in actual).toBe(false); + } + } + }); + + it(`\`_.${methodName}\` should work with an array \`object\``, () => { + const array = [1, 2, 3]; + expect(func(array, resolve(array, '1'))).toEqual({ 1: 2 }); + }); + }); +}); diff --git a/test/pick.js b/test/pick.js deleted file mode 100644 index 08d62911da..0000000000 --- a/test/pick.js +++ /dev/null @@ -1,52 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args, toArgs } from './utils.js'; -import pick from '../pick.js'; - -describe('pick', function() { - var args = toArgs(['a', 'c']), - object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, - nested = { 'a': 1, 'b': { 'c': 2, 'd': 3 } }; - - it('should flatten `paths`', function() { - assert.deepStrictEqual(pick(object, 'a', 'c'), { 'a': 1, 'c': 3 }); - assert.deepStrictEqual(pick(object, ['a', 'd'], 'c'), { 'a': 1, 'c': 3, 'd': 4 }); - }); - - it('should support deep paths', function() { - assert.deepStrictEqual(pick(nested, 'b.c'), { 'b': { 'c': 2 } }); - }); - - it('should support path arrays', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }, - actual = pick(object, [['a.b']]); - - assert.deepStrictEqual(actual, { 'a.b': 1 }); - }); - - it('should pick a key over a path', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - assert.deepStrictEqual(pick(object, path), { 'a.b': 1 }); - }); - }); - - it('should coerce `paths` to strings', function() { - assert.deepStrictEqual(pick({ '0': 'a', '1': 'b' }, 0), { '0': 'a' }); - }); - - it('should return an empty object when `object` is nullish', function() { - lodashStable.each([null, undefined], function(value) { - assert.deepStrictEqual(pick(value, 'valueOf'), {}); - }); - }); - - it('should work with a primitive `object`', function() { - assert.deepStrictEqual(pick('', 'slice'), { 'slice': ''.slice }); - }); - - it('should work with `arguments` object `paths`', function() { - assert.deepStrictEqual(pick(object, args), { 'a': 1, 'c': 3 }); - }); -}); diff --git a/test/pick.spec.js b/test/pick.spec.js new file mode 100644 index 0000000000..7fed9ad644 --- /dev/null +++ b/test/pick.spec.js @@ -0,0 +1,51 @@ +import lodashStable from 'lodash'; +import { args, toArgs } from './utils'; +import pick from '../src/pick'; + +describe('pick', () => { + const args = toArgs(['a', 'c']); + const object = { a: 1, b: 2, c: 3, d: 4 }; + const nested = { a: 1, b: { c: 2, d: 3 } }; + + it('should flatten `paths`', () => { + expect(pick(object, 'a', 'c'), { a: 1).toEqual(c: 3 }); + expect(pick(object, ['a', 'd'], 'c'), { a: 1, c: 3).toEqual(d: 4 }); + }); + + it('should support deep paths', () => { + expect(pick(nested, 'b.c')).toEqual({ b: { c: 2 } }); + }); + + it('should support path arrays', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + const actual = pick(object, [['a.b']]); + + expect(actual).toEqual({ 'a.b': 1 }); + }); + + it('should pick a key over a path', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + expect(pick(object, path)).toEqual({ 'a.b': 1 }); + }); + }); + + it('should coerce `paths` to strings', () => { + expect(pick({ 0: 'a', 1: 'b' }, 0)).toEqual({ 0: 'a' }); + }); + + it('should return an empty object when `object` is nullish', () => { + lodashStable.each([null, undefined], (value) => { + expect(pick(value, 'valueOf')).toEqual({}); + }); + }); + + it('should work with a primitive `object`', () => { + expect(pick('', 'slice')).toEqual({ slice: ''.slice }); + }); + + it('should work with `arguments` object `paths`', () => { + expect(pick(object, args), { a: 1).toEqual(c: 3 }); + }); +}); diff --git a/test/pickBy.spec.js b/test/pickBy.spec.js new file mode 100644 index 0000000000..7ee002d151 --- /dev/null +++ b/test/pickBy.spec.js @@ -0,0 +1,19 @@ +import { stubTrue } from './utils'; +import pickBy from '../src/pickBy'; + +describe('pickBy', () => { + it('should work with a predicate argument', () => { + const object = { a: 1, b: 2, c: 3, d: 4 }; + + const actual = pickBy(object, (n) => n === 1 || n === 3); + + expect(actual).toEqual({ a: 1, c: 3 }); + }); + + it('should not treat keys with dots as deep paths', () => { + const object = { 'a.b.c': 1 }; + const actual = pickBy(object, stubTrue); + + expect(actual).toEqual({ 'a.b.c': 1 }); + }); +}); diff --git a/test/pickBy.test.js b/test/pickBy.test.js deleted file mode 100644 index aa074daeee..0000000000 --- a/test/pickBy.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import { stubTrue } from './utils.js'; -import pickBy from '../pickBy.js'; - -describe('pickBy', function() { - it('should work with a predicate argument', function() { - var object = { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }; - - var actual = pickBy(object, function(n) { - return n == 1 || n == 3; - }); - - assert.deepStrictEqual(actual, { 'a': 1, 'c': 3 }); - }); - - it('should not treat keys with dots as deep paths', function() { - var object = { 'a.b.c': 1 }, - actual = pickBy(object, stubTrue); - - assert.deepStrictEqual(actual, { 'a.b.c': 1 }); - }); -}); diff --git a/test/property.spec.js b/test/property.spec.js new file mode 100644 index 0000000000..06126f88a1 --- /dev/null +++ b/test/property.spec.js @@ -0,0 +1,124 @@ +import lodashStable from 'lodash'; +import { noop } from './utils'; +import property from '../src/property'; + +describe('property', () => { + it('should create a function that plucks a property value of a given object', () => { + const object = { a: 1 }; + + lodashStable.each(['a', ['a']], (path) => { + const prop = property(path); + expect(prop.length).toBe(1); + expect(prop(object)).toBe(1); + }); + }); + + it('should pluck deep property values', () => { + const object = { a: { b: 2 } }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const prop = property(path); + expect(prop(object)).toBe(2); + }); + }); + + it('should pluck inherited property values', () => { + function Foo() {} + Foo.prototype.a = 1; + + lodashStable.each(['a', ['a']], (path) => { + const prop = property(path); + expect(prop(new Foo())).toBe(1); + }); + }); + + it('should work with a non-string `path`', () => { + const array = [1, 2, 3]; + + lodashStable.each([1, [1]], (path) => { + const prop = property(path); + expect(prop(array)).toBe(2); + }); + }); + + it('should preserve the sign of `0`', () => { + const object = { '-0': 'a', 0: 'b' }; + const props = [-0, Object(-0), 0, Object(0)]; + + const actual = lodashStable.map(props, (key) => { + const prop = property(key); + return prop(object); + }); + + expect(actual).toEqual(['a', 'a', 'b', 'b']); + }); + + it('should coerce `path` to a string', () => { + function fn() {} + fn.toString = lodashStable.constant('fn'); + + const expected = [1, 2, 3, 4]; + const object = { null: 1, undefined: 2, fn: 3, '[object Object]': 4 }; + const paths = [null, undefined, fn, {}]; + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(paths, (path) => { + const prop = property(index ? [path] : path); + return prop(object); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should pluck a key over a path', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + + lodashStable.each(['a.b', ['a.b']], (path) => { + const prop = property(path); + expect(prop(object)).toBe(1); + }); + }); + + it('should return `undefined` when `object` is nullish', () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each(['constructor', ['constructor']], (path) => { + const prop = property(path); + + const actual = lodashStable.map(values, (value, index) => + index ? prop(value) : prop(), + ); + + expect(actual).toEqual(expected); + }); + }); + + it('should return `undefined` for deep paths when `object` is nullish', () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each( + ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], + (path) => { + const prop = property(path); + + const actual = lodashStable.map(values, (value, index) => + index ? prop(value) : prop(), + ); + + expect(actual).toEqual(expected); + }, + ); + }); + + it('should return `undefined` if parts of `path` are missing', () => { + const object = {}; + + lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], (path) => { + const prop = property(path); + expect(prop(object)).toBe(undefined); + }); + }); +}); diff --git a/test/property.test.js b/test/property.test.js deleted file mode 100644 index a846e1e104..0000000000 --- a/test/property.test.js +++ /dev/null @@ -1,122 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop } from './utils.js'; -import property from '../property.js'; - -describe('property', function() { - it('should create a function that plucks a property value of a given object', function() { - var object = { 'a': 1 }; - - lodashStable.each(['a', ['a']], function(path) { - var prop = property(path); - assert.strictEqual(prop.length, 1); - assert.strictEqual(prop(object), 1); - }); - }); - - it('should pluck deep property values', function() { - var object = { 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var prop = property(path); - assert.strictEqual(prop(object), 2); - }); - }); - - it('should pluck inherited property values', function() { - function Foo() {} - Foo.prototype.a = 1; - - lodashStable.each(['a', ['a']], function(path) { - var prop = property(path); - assert.strictEqual(prop(new Foo), 1); - }); - }); - - it('should work with a non-string `path`', function() { - var array = [1, 2, 3]; - - lodashStable.each([1, [1]], function(path) { - var prop = property(path); - assert.strictEqual(prop(array), 2); - }); - }); - - it('should preserve the sign of `0`', function() { - var object = { '-0': 'a', '0': 'b' }, - props = [-0, Object(-0), 0, Object(0)]; - - var actual = lodashStable.map(props, function(key) { - var prop = property(key); - return prop(object); - }); - - assert.deepStrictEqual(actual, ['a', 'a', 'b', 'b']); - }); - - it('should coerce `path` to a string', function() { - function fn() {} - fn.toString = lodashStable.constant('fn'); - - var expected = [1, 2, 3, 4], - object = { 'null': 1, 'undefined': 2, 'fn': 3, '[object Object]': 4 }, - paths = [null, undefined, fn, {}]; - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(paths, function(path) { - var prop = property(index ? [path] : path); - return prop(object); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should pluck a key over a path', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }; - - lodashStable.each(['a.b', ['a.b']], function(path) { - var prop = property(path); - assert.strictEqual(prop(object), 1); - }); - }); - - it('should return `undefined` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor', ['constructor']], function(path) { - var prop = property(path); - - var actual = lodashStable.map(values, function(value, index) { - return index ? prop(value) : prop(); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` for deep paths when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) { - var prop = property(path); - - var actual = lodashStable.map(values, function(value, index) { - return index ? prop(value) : prop(); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` if parts of `path` are missing', function() { - var object = {}; - - lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) { - var prop = property(path); - assert.strictEqual(prop(object), undefined); - }); - }); -}); diff --git a/test/propertyOf.spec.js b/test/propertyOf.spec.js new file mode 100644 index 0000000000..d82e0a0551 --- /dev/null +++ b/test/propertyOf.spec.js @@ -0,0 +1,124 @@ +import lodashStable from 'lodash'; +import { noop } from './utils'; +import propertyOf from '../src/propertyOf'; + +describe('propertyOf', () => { + it('should create a function that plucks a property value of a given key', () => { + const object = { a: 1 }; + const propOf = propertyOf(object); + + expect(propOf.length).toBe(1); + lodashStable.each(['a', ['a']], (path) => { + expect(propOf(path)).toBe(1); + }); + }); + + it('should pluck deep property values', () => { + const object = { a: { b: 2 } }; + const propOf = propertyOf(object); + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(propOf(path)).toBe(2); + }); + }); + + it('should pluck inherited property values', () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const propOf = propertyOf(new Foo()); + + lodashStable.each(['b', ['b']], (path) => { + expect(propOf(path)).toBe(2); + }); + }); + + it('should work with a non-string `path`', () => { + const array = [1, 2, 3]; + const propOf = propertyOf(array); + + lodashStable.each([1, [1]], (path) => { + expect(propOf(path)).toBe(2); + }); + }); + + it('should preserve the sign of `0`', () => { + const object = { '-0': 'a', 0: 'b' }; + const props = [-0, Object(-0), 0, Object(0)]; + + const actual = lodashStable.map(props, (key) => { + const propOf = propertyOf(object); + return propOf(key); + }); + + expect(actual, ['a', 'a', 'b').toEqual('b']); + }); + + it('should coerce `path` to a string', () => { + function fn() {} + fn.toString = lodashStable.constant('fn'); + + const expected = [1, 2, 3, 4]; + const object = { null: 1, undefined: 2, fn: 3, '[object Object]': 4 }; + const paths = [null, undefined, fn, {}]; + + lodashStable.times(2, (index) => { + const actual = lodashStable.map(paths, (path) => { + const propOf = propertyOf(object); + return propOf(index ? [path] : path); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should pluck a key over a path', () => { + const object = { 'a.b': 1, a: { b: 2 } }; + const propOf = propertyOf(object); + + lodashStable.each(['a.b', ['a.b']], (path) => { + expect(propOf(path)).toBe(1); + }); + }); + + it('should return `undefined` when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each(['constructor', ['constructor']], (path) => { + const actual = lodashStable.map(values, (value, index) => { + const propOf = index ? propertyOf(value) : propertyOf(); + return propOf(path); + }); + + expect(actual).toEqual(expected); + }); + }); + + it('should return `undefined` for deep paths when `object` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, noop); + + lodashStable.each( + ['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], + (path) => { + const actual = lodashStable.map(values, (value, index) => { + const propOf = index ? propertyOf(value) : propertyOf(); + return propOf(path); + }); + + expect(actual).toEqual(expected); + }, + ); + }); + + it('should return `undefined` if parts of `path` are missing', () => { + const propOf = propertyOf({}); + + lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], (path) => { + expect(propOf(path)).toBe(undefined); + }); + }); +}); diff --git a/test/propertyOf.test.js b/test/propertyOf.test.js deleted file mode 100644 index 0fe43b7b39..0000000000 --- a/test/propertyOf.test.js +++ /dev/null @@ -1,122 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop } from './utils.js'; -import propertyOf from '../propertyOf.js'; - -describe('propertyOf', function() { - it('should create a function that plucks a property value of a given key', function() { - var object = { 'a': 1 }, - propOf = propertyOf(object); - - assert.strictEqual(propOf.length, 1); - lodashStable.each(['a', ['a']], function(path) { - assert.strictEqual(propOf(path), 1); - }); - }); - - it('should pluck deep property values', function() { - var object = { 'a': { 'b': 2 } }, - propOf = propertyOf(object); - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(propOf(path), 2); - }); - }); - - it('should pluck inherited property values', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var propOf = propertyOf(new Foo); - - lodashStable.each(['b', ['b']], function(path) { - assert.strictEqual(propOf(path), 2); - }); - }); - - it('should work with a non-string `path`', function() { - var array = [1, 2, 3], - propOf = propertyOf(array); - - lodashStable.each([1, [1]], function(path) { - assert.strictEqual(propOf(path), 2); - }); - }); - - it('should preserve the sign of `0`', function() { - var object = { '-0': 'a', '0': 'b' }, - props = [-0, Object(-0), 0, Object(0)]; - - var actual = lodashStable.map(props, function(key) { - var propOf = propertyOf(object); - return propOf(key); - }); - - assert.deepStrictEqual(actual, ['a', 'a', 'b', 'b']); - }); - - it('should coerce `path` to a string', function() { - function fn() {} - fn.toString = lodashStable.constant('fn'); - - var expected = [1, 2, 3, 4], - object = { 'null': 1, 'undefined': 2, 'fn': 3, '[object Object]': 4 }, - paths = [null, undefined, fn, {}]; - - lodashStable.times(2, function(index) { - var actual = lodashStable.map(paths, function(path) { - var propOf = propertyOf(object); - return propOf(index ? [path] : path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should pluck a key over a path', function() { - var object = { 'a.b': 1, 'a': { 'b': 2 } }, - propOf = propertyOf(object); - - lodashStable.each(['a.b', ['a.b']], function(path) { - assert.strictEqual(propOf(path), 1); - }); - }); - - it('should return `undefined` when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor', ['constructor']], function(path) { - var actual = lodashStable.map(values, function(value, index) { - var propOf = index ? propertyOf(value) : propertyOf(); - return propOf(path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` for deep paths when `object` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, noop); - - lodashStable.each(['constructor.prototype.valueOf', ['constructor', 'prototype', 'valueOf']], function(path) { - var actual = lodashStable.map(values, function(value, index) { - var propOf = index ? propertyOf(value) : propertyOf(); - return propOf(path); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should return `undefined` if parts of `path` are missing', function() { - var propOf = propertyOf({}); - - lodashStable.each(['a', 'a[1].b.c', ['a'], ['a', '1', 'b', 'c']], function(path) { - assert.strictEqual(propOf(path), undefined); - }); - }); -}); diff --git a/test/pull-methods.js b/test/pull-methods.js deleted file mode 100644 index 4336b48e87..0000000000 --- a/test/pull-methods.js +++ /dev/null @@ -1,49 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('pull methods', function() { - lodashStable.each(['pull', 'pullAll', 'pullAllWith'], function(methodName) { - var func = _[methodName], - isPull = methodName == 'pull'; - - function pull(array, values) { - return isPull - ? func.apply(undefined, [array].concat(values)) - : func(array, values); - } - - it('`_.' + methodName + '` should modify and return the array', function() { - var array = [1, 2, 3], - actual = pull(array, [1, 3]); - - assert.strictEqual(actual, array); - assert.deepStrictEqual(array, [2]); - }); - - it('`_.' + methodName + '` should preserve holes in arrays', function() { - var array = [1, 2, 3, 4]; - delete array[1]; - delete array[3]; - - pull(array, [1]); - assert.ok(!('0' in array)); - assert.ok(!('2' in array)); - }); - - it('`_.' + methodName + '` should treat holes as `undefined`', function() { - var array = [1, 2, 3]; - delete array[1]; - - pull(array, [undefined]); - assert.deepStrictEqual(array, [1, 3]); - }); - - it('`_.' + methodName + '` should match `NaN`', function() { - var array = [1, NaN, 3, NaN]; - - pull(array, [NaN]); - assert.deepStrictEqual(array, [1, 3]); - }); - }); -}); diff --git a/test/pull-methods.spec.js b/test/pull-methods.spec.js new file mode 100644 index 0000000000..50cb6e4cc5 --- /dev/null +++ b/test/pull-methods.spec.js @@ -0,0 +1,46 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('pull methods', () => { + lodashStable.each(['pull', 'pullAll', 'pullAllWith'], (methodName) => { + const func = _[methodName]; + const isPull = methodName === 'pull'; + + function pull(array, values) { + return isPull ? func.apply(undefined, [array].concat(values)) : func(array, values); + } + + it(`\`_.${methodName}\` should modify and return the array`, () => { + const array = [1, 2, 3]; + const actual = pull(array, [1, 3]); + + expect(actual).toBe(array); + expect(array).toEqual([2]); + }); + + it(`\`_.${methodName}\` should preserve holes in arrays`, () => { + const array = [1, 2, 3, 4]; + delete array[1]; + delete array[3]; + + pull(array, [1]); + expect(('0' in array)).toBe(false) + expect(('2' in array)).toBe(false) + }); + + it(`\`_.${methodName}\` should treat holes as \`undefined\``, () => { + const array = [1, 2, 3]; + delete array[1]; + + pull(array, [undefined]); + expect(array, [1).toEqual(3]); + }); + + it(`\`_.${methodName}\` should match \`NaN\``, () => { + const array = [1, NaN, 3, NaN]; + + pull(array, [NaN]); + expect(array, [1).toEqual(3]); + }); + }); +}); diff --git a/test/pullAll.spec.js b/test/pullAll.spec.js new file mode 100644 index 0000000000..568d130a53 --- /dev/null +++ b/test/pullAll.spec.js @@ -0,0 +1,10 @@ +import pullAll from '../src/pullAll'; + +describe('pullAll', () => { + it('should work with the same value for `array` and `values`', () => { + const array = [{ a: 1 }, { b: 2 }]; + const actual = pullAll(array, array); + + expect(actual).toEqual([]); + }); +}); diff --git a/test/pullAll.test.js b/test/pullAll.test.js deleted file mode 100644 index 7579e6a0fc..0000000000 --- a/test/pullAll.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import assert from 'assert'; -import pullAll from '../pullAll.js'; - -describe('pullAll', function() { - it('should work with the same value for `array` and `values`', function() { - var array = [{ 'a': 1 }, { 'b': 2 }], - actual = pullAll(array, array); - - assert.deepStrictEqual(actual, []); - }); -}); diff --git a/test/pullAllBy.spec.js b/test/pullAllBy.spec.js new file mode 100644 index 0000000000..fa6a459357 --- /dev/null +++ b/test/pullAllBy.spec.js @@ -0,0 +1,23 @@ +import { slice } from './utils'; +import pullAllBy from '../src/pullAllBy'; + +describe('pullAllBy', () => { + it('should accept an `iteratee`', () => { + const array = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }]; + + const actual = pullAllBy(array, [{ x: 1 }, { x: 3 }], (object) => object.x); + + expect(actual).toEqual([{ x: 2 }]); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + const array = [{ x: 1 }, { x: 2 }, { x: 3 }, { x: 1 }]; + + pullAllBy(array, [{ x: 1 }, { x: 3 }], function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([{ x: 1 }]); + }); +}); diff --git a/test/pullAllBy.test.js b/test/pullAllBy.test.js deleted file mode 100644 index a7fb64f617..0000000000 --- a/test/pullAllBy.test.js +++ /dev/null @@ -1,26 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import pullAllBy from '../pullAllBy.js'; - -describe('pullAllBy', function() { - it('should accept an `iteratee`', function() { - var array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; - - var actual = pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], function(object) { - return object.x; - }); - - assert.deepStrictEqual(actual, [{ 'x': 2 }]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args, - array = [{ 'x': 1 }, { 'x': 2 }, { 'x': 3 }, { 'x': 1 }]; - - pullAllBy(array, [{ 'x': 1 }, { 'x': 3 }], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [{ 'x': 1 }]); - }); -}); diff --git a/test/pullAllWith.spec.js b/test/pullAllWith.spec.js new file mode 100644 index 0000000000..330310ba1e --- /dev/null +++ b/test/pullAllWith.spec.js @@ -0,0 +1,16 @@ +import lodashStable from 'lodash'; +import pullAllWith from '../src/pullAllWith'; + +describe('pullAllWith', () => { + it('should work with a `comparator`', () => { + const objects = [ + { x: 1, y: 1 }, + { x: 2, y: 2 }, + { x: 3, y: 3 }, + ]; + const expected = [objects[0], objects[2]]; + const actual = pullAllWith(objects, [{ x: 2, y: 2 }], lodashStable.isEqual); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/pullAllWith.test.js b/test/pullAllWith.test.js deleted file mode 100644 index 3a47ebaae4..0000000000 --- a/test/pullAllWith.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import pullAllWith from '../pullAllWith.js'; - -describe('pullAllWith', function() { - it('should work with a `comparator`', function() { - var objects = [{ 'x': 1, 'y': 1 }, { 'x': 2, 'y': 2 }, { 'x': 3, 'y': 3 }], - expected = [objects[0], objects[2]], - actual = pullAllWith(objects, [{ 'x': 2, 'y': 2 }], lodashStable.isEqual); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/pullAt.js b/test/pullAt.js deleted file mode 100644 index c70020eb28..0000000000 --- a/test/pullAt.js +++ /dev/null @@ -1,122 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, stubOne, noop, falsey } from './utils.js'; -import pullAt from '../pullAt.js'; - -describe('pullAt', function() { - it('should modify the array and return removed elements', function() { - var array = [1, 2, 3], - actual = pullAt(array, [0, 1]); - - assert.deepStrictEqual(array, [3]); - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('should work with unsorted indexes', function() { - var array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12], - actual = pullAt(array, [1, 3, 11, 7, 5, 9]); - - assert.deepStrictEqual(array, [1, 3, 5, 7, 9, 11]); - assert.deepStrictEqual(actual, [2, 4, 12, 8, 6, 10]); - }); - - it('should work with repeated indexes', function() { - var array = [1, 2, 3, 4], - actual = pullAt(array, [0, 2, 0, 1, 0, 2]); - - assert.deepStrictEqual(array, [4]); - assert.deepStrictEqual(actual, [1, 3, 1, 2, 1, 3]); - }); - - it('should use `undefined` for nonexistent indexes', function() { - var array = ['a', 'b', 'c'], - actual = pullAt(array, [2, 4, 0]); - - assert.deepStrictEqual(array, ['b']); - assert.deepStrictEqual(actual, ['c', undefined, 'a']); - }); - - it('should flatten `indexes`', function() { - var array = ['a', 'b', 'c']; - assert.deepStrictEqual(pullAt(array, 2, 0), ['c', 'a']); - assert.deepStrictEqual(array, ['b']); - - array = ['a', 'b', 'c', 'd']; - assert.deepStrictEqual(pullAt(array, [3, 0], 2), ['d', 'a', 'c']); - assert.deepStrictEqual(array, ['b']); - }); - - it('should return an empty array when no indexes are given', function() { - var array = ['a', 'b', 'c'], - actual = pullAt(array); - - assert.deepStrictEqual(array, ['a', 'b', 'c']); - assert.deepStrictEqual(actual, []); - - actual = pullAt(array, [], []); - - assert.deepStrictEqual(array, ['a', 'b', 'c']); - assert.deepStrictEqual(actual, []); - }); - - it('should work with non-index paths', function() { - var values = lodashStable.reject(empties, function(value) { - return (value === 0) || lodashStable.isArray(value); - }).concat(-1, 1.1); - - var array = lodashStable.transform(values, function(result, value) { - result[value] = 1; - }, []); - - var expected = lodashStable.map(values, stubOne), - actual = pullAt(array, values); - - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(values, noop); - actual = lodashStable.at(array, values); - - assert.deepStrictEqual(actual, expected); - }); - - it('should preserve the sign of `0`', function() { - var props = [-0, Object(-0), 0, Object(0)]; - - var actual = lodashStable.map(props, function(key) { - var array = [-1]; - array['-0'] = -2; - return pullAt(array, key); - }); - - assert.deepStrictEqual(actual, [[-2], [-2], [-1], [-1]]); - }); - - it('should support deep paths', function() { - var array = []; - array.a = { 'b': 2 }; - - var actual = pullAt(array, 'a.b'); - - assert.deepStrictEqual(actual, [2]); - assert.deepStrictEqual(array.a, {}); - - try { - actual = pullAt(array, 'a.b.c'); - } catch (e) {} - - assert.deepStrictEqual(actual, [undefined]); - }); - - it('should work with a falsey `array` when keys are given', function() { - var values = falsey.slice(), - expected = lodashStable.map(values, lodashStable.constant(Array(4))); - - var actual = lodashStable.map(values, function(array) { - try { - return pullAt(array, 0, 1, 'pop', 'push'); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/pullAt.spec.js b/test/pullAt.spec.js new file mode 100644 index 0000000000..2e12f89fa7 --- /dev/null +++ b/test/pullAt.spec.js @@ -0,0 +1,125 @@ +import lodashStable from 'lodash'; +import { empties, stubOne, noop, falsey } from './utils'; +import pullAt from '../src/pullAt'; + +describe('pullAt', () => { + it('should modify the array and return removed elements', () => { + const array = [1, 2, 3]; + const actual = pullAt(array, [0, 1]); + + expect(array).toEqual([3]); + expect(actual).toEqual([1, 2]); + }); + + it('should work with unsorted indexes', () => { + const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; + const actual = pullAt(array, [1, 3, 11, 7, 5, 9]); + + expect(array).toEqual([1, 3, 5, 7, 9, 11]); + expect(actual).toEqual([2, 4, 12, 8, 6, 10]); + }); + + it('should work with repeated indexes', () => { + const array = [1, 2, 3, 4]; + const actual = pullAt(array, [0, 2, 0, 1, 0, 2]); + + expect(array).toEqual([4]); + expect(actual).toEqual([1, 3, 1, 2, 1, 3]); + }); + + it('should use `undefined` for nonexistent indexes', () => { + const array = ['a', 'b', 'c']; + const actual = pullAt(array, [2, 4, 0]); + + expect(array).toEqual(['b']); + expect(actual).toEqual(['c', undefined, 'a']); + }); + + it('should flatten `indexes`', () => { + let array = ['a', 'b', 'c']; + expect(pullAt(array, 2, 0)).toEqual(['c', 'a']); + expect(array).toEqual(['b']); + + array = ['a', 'b', 'c', 'd']; + expect(pullAt(array, [3, 0], 2)).toEqual(['d', 'a', 'c']); + expect(array).toEqual(['b']); + }); + + it('should return an empty array when no indexes are given', () => { + const array = ['a', 'b', 'c']; + let actual = pullAt(array); + + expect(array).toEqual(['a', 'b', 'c']); + expect(actual).toEqual([]); + + actual = pullAt(array, [], []); + + expect(array).toEqual(['a', 'b', 'c']); + expect(actual).toEqual([]); + }); + + it('should work with non-index paths', () => { + const values = lodashStable + .reject(empties, (value) => value === 0 || lodashStable.isArray(value)) + .concat(-1, 1.1); + + const array = lodashStable.transform( + values, + (result, value) => { + result[value] = 1; + }, + [], + ); + + let expected = lodashStable.map(values, stubOne); + let actual = pullAt(array, values); + + expect(actual).toEqual(expected); + + expected = lodashStable.map(values, noop); + actual = lodashStable.at(array, values); + + expect(actual).toEqual(expected); + }); + + it('should preserve the sign of `0`', () => { + const props = [-0, Object(-0), 0, Object(0)]; + + const actual = lodashStable.map(props, (key) => { + const array = [-1]; + array['-0'] = -2; + return pullAt(array, key); + }); + + expect(actual).toEqual([[-2], [-2], [-1], [-1]]); + }); + + it('should support deep paths', () => { + const array = []; + array.a = { b: 2 }; + + let actual = pullAt(array, 'a.b'); + + expect(actual).toEqual([2]); + expect(array.a).toEqual({}); + + try { + actual = pullAt(array, 'a.b.c'); + } catch (e) {} + + expect(actual).toEqual([undefined]); + }); + + it('should work with a falsey `array` when keys are given', () => { + const values = falsey.slice(); + const expected = lodashStable.map(values, lodashStable.constant(Array(4))); + + const actual = lodashStable.map(values, (array) => { + try { + return pullAt(array, 0, 1, 'pop', 'push'); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/random.js b/test/random.js deleted file mode 100644 index a111dc84f3..0000000000 --- a/test/random.js +++ /dev/null @@ -1,104 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { MAX_INTEGER, stubTrue } from './utils.js'; -import random from '../random.js'; - -describe('random', function() { - var array = Array(1000); - - it('should return `0` or `1` when no arguments are given', function() { - var actual = lodashStable.uniq(lodashStable.map(array, function() { - return random(); - })).sort(); - - assert.deepStrictEqual(actual, [0, 1]); - }); - - it('should support a `min` and `max`', function() { - var min = 5, - max = 10; - - assert.ok(lodashStable.some(array, function() { - var result = random(min, max); - return result >= min && result <= max; - })); - }); - - it('should support not providing a `max`', function() { - var min = 0, - max = 5; - - assert.ok(lodashStable.some(array, function() { - var result = random(max); - return result >= min && result <= max; - })); - }); - - it('should swap `min` and `max` when `min` > `max`', function() { - var min = 4, - max = 2, - expected = [2, 3, 4]; - - var actual = lodashStable.uniq(lodashStable.map(array, function() { - return random(min, max); - })).sort(); - - assert.deepStrictEqual(actual, expected); - }); - - it('should support large integer values', function() { - var min = Math.pow(2, 31), - max = Math.pow(2, 62); - - assert.ok(lodashStable.every(array, function() { - var result = random(min, max); - return result >= min && result <= max; - })); - - assert.ok(lodashStable.some(array, function() { - return random(MAX_INTEGER); - })); - }); - - it('should coerce arguments to finite numbers', function() { - var actual = [ - random(NaN, NaN), - random('1', '1'), - random(Infinity, Infinity) - ]; - - assert.deepStrictEqual(actual, [0, 1, MAX_INTEGER]); - }); - - it('should support floats', function() { - var min = 1.5, - max = 1.6, - actual = random(min, max); - - assert.ok(actual % 1); - assert.ok(actual >= min && actual <= max); - }); - - it('should support providing a `floating`', function() { - var actual = random(true); - assert.ok(actual % 1 && actual >= 0 && actual <= 1); - - actual = random(2, true); - assert.ok(actual % 1 && actual >= 0 && actual <= 2); - - actual = random(2, 4, true); - assert.ok(actual % 1 && actual >= 2 && actual <= 4); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [1, 2, 3], - expected = lodashStable.map(array, stubTrue), - randoms = lodashStable.map(array, random); - - var actual = lodashStable.map(randoms, function(result, index) { - return result >= 0 && result <= array[index] && (result % 1) == 0; - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/random.spec.js b/test/random.spec.js new file mode 100644 index 0000000000..eeba5d1be7 --- /dev/null +++ b/test/random.spec.js @@ -0,0 +1,100 @@ +import lodashStable from 'lodash'; +import { MAX_INTEGER, stubTrue } from './utils'; +import random from '../src/random'; + +describe('random', () => { + const array = Array(1000); + + it('should return `0` or `1` when no arguments are given', () => { + const actual = lodashStable.uniq(lodashStable.map(array, () => random())).sort(); + + expect(actual).toEqual([0, 1]); + }); + + it('should support a `min` and `max`', () => { + const min = 5; + const max = 10; + + expect( + lodashStable.some(array, () => { + const result = random(min, max); + return result >= min && result <= max; + }), + ); + }); + + it('should support not providing a `max`', () => { + const min = 0; + const max = 5; + + expect( + lodashStable.some(array, () => { + const result = random(max); + return result >= min && result <= max; + }), + ); + }); + + it('should swap `min` and `max` when `min` > `max`', () => { + const min = 4; + const max = 2; + const expected = [2, 3, 4]; + + const actual = lodashStable.uniq(lodashStable.map(array, () => random(min, max))).sort(); + + expect(actual).toEqual(expected); + }); + + it('should support large integer values', () => { + const min = 2 ** 31; + const max = 2 ** 62; + + expect( + lodashStable.every(array, () => { + const result = random(min, max); + return result >= min && result <= max; + }), + ); + + expect(lodashStable.some(array, () => random(MAX_INTEGER))) + }); + + it('should coerce arguments to finite numbers', () => { + const actual = [random(NaN, NaN), random('1', '1'), random(Infinity, Infinity)]; + + expect(actual).toEqual([0, 1, MAX_INTEGER]); + }); + + it('should support floats', () => { + const min = 1.5; + const max = 1.6; + const actual = random(min, max); + + expect(actual % 1) + expect(actual >= min && actual <= max) + }); + + it('should support providing a `floating`', () => { + let actual = random(true); + expect(actual % 1 && actual >= 0 && actual <= 1) + + actual = random(2, true); + expect(actual % 1 && actual >= 0 && actual <= 2) + + actual = random(2, 4, true); + expect(actual % 1 && actual >= 2 && actual <= 4) + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [1, 2, 3]; + const expected = lodashStable.map(array, stubTrue); + const randoms = lodashStable.map(array, random); + + const actual = lodashStable.map( + randoms, + (result, index) => result >= 0 && result <= array[index] && result % 1 === 0, + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/range-methods.js b/test/range-methods.js deleted file mode 100644 index 7f011e9d0b..0000000000 --- a/test/range-methods.js +++ /dev/null @@ -1,82 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, falsey } from './utils.js'; - -describe('range methods', function() { - lodashStable.each(['range', 'rangeRight'], function(methodName) { - var func = _[methodName], - isRange = methodName == 'range'; - - function resolve(range) { - return isRange ? range : range.reverse(); - } - - it('`_.' + methodName + '` should infer the sign of `step` when only `end` is given', function() { - assert.deepStrictEqual(func(4), resolve([0, 1, 2, 3])); - assert.deepStrictEqual(func(-4), resolve([0, -1, -2, -3])); - }); - - it('`_.' + methodName + '` should infer the sign of `step` when only `start` and `end` are given', function() { - assert.deepStrictEqual(func(1, 5), resolve([1, 2, 3, 4])); - assert.deepStrictEqual(func(5, 1), resolve([5, 4, 3, 2])); - }); - - it('`_.' + methodName + '` should work with a `start`, `end`, and `step`', function() { - assert.deepStrictEqual(func(0, -4, -1), resolve([0, -1, -2, -3])); - assert.deepStrictEqual(func(5, 1, -1), resolve([5, 4, 3, 2])); - assert.deepStrictEqual(func(0, 20, 5), resolve([0, 5, 10, 15])); - }); - - it('`_.' + methodName + '` should support a `step` of `0`', function() { - assert.deepStrictEqual(func(1, 4, 0), [1, 1, 1]); - }); - - it('`_.' + methodName + '` should work with a `step` larger than `end`', function() { - assert.deepStrictEqual(func(1, 5, 20), [1]); - }); - - it('`_.' + methodName + '` should work with a negative `step`', function() { - assert.deepStrictEqual(func(0, -4, -1), resolve([0, -1, -2, -3])); - assert.deepStrictEqual(func(21, 10, -3), resolve([21, 18, 15, 12])); - }); - - it('`_.' + methodName + '` should support `start` of `-0`', function() { - var actual = func(-0, 1); - assert.strictEqual(1 / actual[0], -Infinity); - }); - - it('`_.' + methodName + '` should treat falsey `start` as `0`', function() { - lodashStable.each(falsey, function(value, index) { - if (index) { - assert.deepStrictEqual(func(value), []); - assert.deepStrictEqual(func(value, 1), [0]); - } else { - assert.deepStrictEqual(func(), []); - } - }); - }); - - it('`_.' + methodName + '` should coerce arguments to finite numbers', function() { - var actual = [ - func('1'), - func('0', 1), - func(0, 1, '1'), - func(NaN), - func(NaN, NaN) - ]; - - assert.deepStrictEqual(actual, [[0], [0], [0], [], []]); - }); - - it('`_.' + methodName + '` should work as an iteratee for methods like `_.map`', function() { - var array = [1, 2, 3], - object = { 'a': 1, 'b': 2, 'c': 3 }, - expected = lodashStable.map([[0], [0, 1], [0, 1, 2]], resolve); - - lodashStable.each([array, object], function(collection) { - var actual = lodashStable.map(collection, func); - assert.deepStrictEqual(actual, expected); - }); - }); - }); -}); diff --git a/test/range-methods.spec.js b/test/range-methods.spec.js new file mode 100644 index 0000000000..e723f5d8bd --- /dev/null +++ b/test/range-methods.spec.js @@ -0,0 +1,75 @@ +import lodashStable from 'lodash'; +import { _, falsey } from './utils'; + +describe('range methods', () => { + lodashStable.each(['range', 'rangeRight'], (methodName) => { + const func = _[methodName]; + const isRange = methodName === 'range'; + + function resolve(range) { + return isRange ? range : range.reverse(); + } + + it(`\`_.${methodName}\` should infer the sign of \`step\` when only \`end\` is given`, () => { + expect(func(4), resolve([0, 1, 2).toEqual(3])); + expect(func(-4), resolve([0, -1, -2).toEqual(-3])); + }); + + it(`\`_.${methodName}\` should infer the sign of \`step\` when only \`start\` and \`end\` are given`, () => { + expect(func(1, 5), resolve([1, 2, 3).toEqual(4])); + expect(func(5, 1), resolve([5, 4, 3).toEqual(2])); + }); + + it(`\`_.${methodName}\` should work with a \`start\`, \`end\`, and \`step\``, () => { + expect(func(0, -4, -1), resolve([0, -1, -2).toEqual(-3])); + expect(func(5, 1, -1), resolve([5, 4, 3).toEqual(2])); + expect(func(0, 20, 5), resolve([0, 5, 10).toEqual(15])); + }); + + it(`\`_.${methodName}\` should support a \`step\` of \`0\``, () => { + expect(func(1, 4, 0), [1, 1).toEqual(1]); + }); + + it(`\`_.${methodName}\` should work with a \`step\` larger than \`end\``, () => { + expect(func(1, 5, 20)).toEqual([1]); + }); + + it(`\`_.${methodName}\` should work with a negative \`step\``, () => { + expect(func(0, -4, -1), resolve([0, -1, -2).toEqual(-3])); + expect(func(21, 10, -3), resolve([21, 18, 15).toEqual(12])); + }); + + it(`\`_.${methodName}\` should support \`start\` of \`-0\``, () => { + const actual = func(-0, 1); + expect(1 / actual[0]).toBe(-Infinity); + }); + + it(`\`_.${methodName}\` should treat falsey \`start\` as \`0\``, () => { + lodashStable.each(falsey, (value, index) => { + if (index) { + expect(func(value)).toEqual([]); + expect(func(value, 1)).toEqual([0]); + } else { + expect(func()).toEqual([]); + } + }); + }); + + it(`\`_.${methodName}\` should coerce arguments to finite numbers`, () => { + const actual = [func('1'), func('0', 1), func(0, 1, '1'), func(NaN), func(NaN, NaN)]; + + expect(actual, [[0], [0], [0], []).toEqual([]]); + }); + + it(`\`_.${methodName}\` should work as an iteratee for methods like \`_.map\``, () => { + const array = [1, 2, 3]; + const object = { a: 1, b: 2, c: 3 }; + const expected = lodashStable.map([[0], [0, 1], [0, 1, 2]], resolve); + + lodashStable.each([array, object], (collection) => { + const actual = lodashStable.map(collection, func); + expect(actual).toEqual(expected); + }); + }); + }); +}); diff --git a/test/rearg.js b/test/rearg.js deleted file mode 100644 index 1e76fdb517..0000000000 --- a/test/rearg.js +++ /dev/null @@ -1,70 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, empties } from './utils.js'; -import rearg from '../rearg.js'; - -describe('rearg', function() { - function fn() { - return slice.call(arguments); - } - - it('should reorder arguments provided to `func`', function() { - var rearged = rearg(fn, [2, 0, 1]); - assert.deepStrictEqual(rearged('b', 'c', 'a'), ['a', 'b', 'c']); - }); - - it('should work with repeated indexes', function() { - var rearged = rearg(fn, [1, 1, 1]); - assert.deepStrictEqual(rearged('c', 'a', 'b'), ['a', 'a', 'a']); - }); - - it('should use `undefined` for nonexistent indexes', function() { - var rearged = rearg(fn, [1, 4]); - assert.deepStrictEqual(rearged('b', 'a', 'c'), ['a', undefined, 'c']); - }); - - it('should use `undefined` for non-index values', function() { - var values = lodashStable.reject(empties, function(value) { - return (value === 0) || lodashStable.isArray(value); - }).concat(-1, 1.1); - - var expected = lodashStable.map(values, lodashStable.constant([undefined, 'b', 'c'])); - - var actual = lodashStable.map(values, function(value) { - var rearged = rearg(fn, [value]); - return rearged('a', 'b', 'c'); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should not rearrange arguments when no indexes are given', function() { - var rearged = rearg(fn); - assert.deepStrictEqual(rearged('a', 'b', 'c'), ['a', 'b', 'c']); - - rearged = rearg(fn, [], []); - assert.deepStrictEqual(rearged('a', 'b', 'c'), ['a', 'b', 'c']); - }); - - it('should accept multiple index arguments', function() { - var rearged = rearg(fn, 2, 0, 1); - assert.deepStrictEqual(rearged('b', 'c', 'a'), ['a', 'b', 'c']); - }); - - it('should accept multiple arrays of indexes', function() { - var rearged = rearg(fn, [2], [0, 1]); - assert.deepStrictEqual(rearged('b', 'c', 'a'), ['a', 'b', 'c']); - }); - - it('should work with fewer indexes than arguments', function() { - var rearged = rearg(fn, [1, 0]); - assert.deepStrictEqual(rearged('b', 'a', 'c'), ['a', 'b', 'c']); - }); - - it('should work on functions that have been rearged', function() { - var rearged1 = rearg(fn, 2, 1, 0), - rearged2 = rearg(rearged1, 1, 0, 2); - - assert.deepStrictEqual(rearged2('b', 'c', 'a'), ['a', 'b', 'c']); - }); -}); diff --git a/test/reduce-methods.js b/test/reduce-methods.js deleted file mode 100644 index 34f4b34c34..0000000000 --- a/test/reduce-methods.js +++ /dev/null @@ -1,68 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, empties, noop, add } from './utils.js'; - -describe('reduce methods', function() { - lodashStable.each(['reduce', 'reduceRight'], function(methodName) { - var func = _[methodName], - array = [1, 2, 3], - isReduce = methodName == 'reduce'; - - it('`_.' + methodName + '` should reduce a collection to a single value', function() { - var actual = func(['a', 'b', 'c'], function(accumulator, value) { - return accumulator + value; - }, ''); - - assert.strictEqual(actual, isReduce ? 'abc' : 'cba'); - }); - - it('`_.' + methodName + '` should support empty collections without an initial `accumulator` value', function() { - var actual = [], - expected = lodashStable.map(empties, noop); - - lodashStable.each(empties, function(value) { - try { - actual.push(func(value, noop)); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should support empty collections with an initial `accumulator` value', function() { - var expected = lodashStable.map(empties, lodashStable.constant('x')); - - var actual = lodashStable.map(empties, function(value) { - try { - return func(value, noop, 'x'); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should handle an initial `accumulator` value of `undefined`', function() { - var actual = func([], noop, undefined); - assert.strictEqual(actual, undefined); - }); - - it('`_.' + methodName + '` should return `undefined` for empty collections when no `accumulator` is given (test in IE > 9 and modern browsers)', function() { - var array = [], - object = { '0': 1, 'length': 0 }; - - if ('__proto__' in array) { - array.__proto__ = object; - assert.strictEqual(func(array, noop), undefined); - } - assert.strictEqual(func(object, noop), undefined); - }); - - it('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - assert.strictEqual(_(array)[methodName](add), 6); - }); - - it('`_.' + methodName + '` should return a wrapped value when explicitly chaining', function() { - assert.ok(_(array).chain()[methodName](add) instanceof _); - }); - }); -}); diff --git a/test/reduce-methods.spec.js b/test/reduce-methods.spec.js new file mode 100644 index 0000000000..a0ab23cca6 --- /dev/null +++ b/test/reduce-methods.spec.js @@ -0,0 +1,65 @@ +import lodashStable from 'lodash'; +import { _, empties, noop, add } from './utils'; + +describe('reduce methods', () => { + lodashStable.each(['reduce', 'reduceRight'], (methodName) => { + const func = _[methodName]; + const array = [1, 2, 3]; + const isReduce = methodName === 'reduce'; + + it(`\`_.${methodName}\` should reduce a collection to a single value`, () => { + const actual = func(['a', 'b', 'c'], (accumulator, value) => accumulator + value, ''); + + expect(actual).toBe(isReduce ? 'abc' : 'cba'); + }); + + it(`\`_.${methodName}\` should support empty collections without an initial \`accumulator\` value`, () => { + const actual = []; + const expected = lodashStable.map(empties, noop); + + lodashStable.each(empties, (value) => { + try { + actual.push(func(value, noop)); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should support empty collections with an initial \`accumulator\` value`, () => { + const expected = lodashStable.map(empties, lodashStable.constant('x')); + + const actual = lodashStable.map(empties, (value) => { + try { + return func(value, noop, 'x'); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should handle an initial \`accumulator\` value of \`undefined\``, () => { + const actual = func([], noop, undefined); + expect(actual).toBe(undefined); + }); + + it(`\`_.${methodName}\` should return \`undefined\` for empty collections when no \`accumulator\` is given (test in IE > 9 and modern browsers)`, () => { + const array = []; + const object = { 0: 1, length: 0 }; + + if ('__proto__' in array) { + array.__proto__ = object; + expect(func(array, noop)).toBe(undefined); + } + expect(func(object, noop)).toBe(undefined); + }); + + it(`\`_.${methodName}\` should return an unwrapped value when implicitly chaining`, () => { + expect(_(array)[methodName](add)).toBe(6); + }); + + it(`\`_.${methodName}\` should return a wrapped value when explicitly chaining`, () => { + expect(_(array).chain()[methodName](add) instanceof _); + }); + }); +}); diff --git a/test/reduce.js b/test/reduce.js deleted file mode 100644 index bae1647506..0000000000 --- a/test/reduce.js +++ /dev/null @@ -1,57 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import reduce from '../reduce.js'; -import head from '../head.js'; -import keys from '../keys.js'; - -describe('reduce', function() { - var array = [1, 2, 3]; - - it('should use the first element of a collection as the default `accumulator`', function() { - assert.strictEqual(reduce(array), 1); - }); - - it('should provide correct `iteratee` arguments when iterating an array', function() { - var args; - - reduce(array, function() { - args || (args = slice.call(arguments)); - }, 0); - - assert.deepStrictEqual(args, [0, 1, 0, array]); - - args = undefined; - reduce(array, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [1, 2, 1, array]); - }); - - it('should provide correct `iteratee` arguments when iterating an object', function() { - var args, - object = { 'a': 1, 'b': 2 }, - firstKey = head(keys(object)); - - var expected = firstKey == 'a' - ? [0, 1, 'a', object] - : [0, 2, 'b', object]; - - reduce(object, function() { - args || (args = slice.call(arguments)); - }, 0); - - assert.deepStrictEqual(args, expected); - - args = undefined; - expected = firstKey == 'a' - ? [1, 2, 'b', object] - : [2, 1, 'a', object]; - - reduce(object, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, expected); - }); -}); diff --git a/test/reduce.spec.js b/test/reduce.spec.js new file mode 100644 index 0000000000..eccd7f37e3 --- /dev/null +++ b/test/reduce.spec.js @@ -0,0 +1,60 @@ +import { slice } from './utils'; +import reduce from '../src/reduce'; +import head from '../src/head'; +import keys from '../src/keys'; + +describe('reduce', () => { + const array = [1, 2, 3]; + + it('should use the first element of a collection as the default `accumulator`', () => { + expect(reduce(array)).toBe(1); + }); + + it('should provide correct `iteratee` arguments when iterating an array', () => { + let args; + + reduce( + array, + function () { + args || (args = slice.call(arguments)); + }, + 0, + ); + + expect(args, [0, 1, 0).toEqual(array]); + + args = undefined; + reduce(array, function () { + args || (args = slice.call(arguments)); + }); + + expect(args, [1, 2, 1).toEqual(array]); + }); + + it('should provide correct `iteratee` arguments when iterating an object', () => { + let args; + const object = { a: 1, b: 2 }; + const firstKey = head(keys(object)); + + let expected = firstKey === 'a' ? [0, 1, 'a', object] : [0, 2, 'b', object]; + + reduce( + object, + function () { + args || (args = slice.call(arguments)); + }, + 0, + ); + + expect(args).toEqual(expected); + + args = undefined; + expected = firstKey === 'a' ? [1, 2, 'b', object] : [2, 1, 'a', object]; + + reduce(object, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual(expected); + }); +}); diff --git a/test/reduceRight.js b/test/reduceRight.js deleted file mode 100644 index ea2beae027..0000000000 --- a/test/reduceRight.js +++ /dev/null @@ -1,56 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice } from './utils.js'; -import reduceRight from '../reduceRight.js'; - -describe('reduceRight', function() { - var array = [1, 2, 3]; - - it('should use the last element of a collection as the default `accumulator`', function() { - assert.strictEqual(reduceRight(array), 3); - }); - - it('should provide correct `iteratee` arguments when iterating an array', function() { - var args; - - reduceRight(array, function() { - args || (args = slice.call(arguments)); - }, 0); - - assert.deepStrictEqual(args, [0, 3, 2, array]); - - args = undefined; - reduceRight(array, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [3, 2, 1, array]); - }); - - it('should provide correct `iteratee` arguments when iterating an object', function() { - var args, - object = { 'a': 1, 'b': 2 }, - isFIFO = lodashStable.keys(object)[0] == 'a'; - - var expected = isFIFO - ? [0, 2, 'b', object] - : [0, 1, 'a', object]; - - reduceRight(object, function() { - args || (args = slice.call(arguments)); - }, 0); - - assert.deepStrictEqual(args, expected); - - args = undefined; - expected = isFIFO - ? [2, 1, 'a', object] - : [1, 2, 'b', object]; - - reduceRight(object, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, expected); - }); -}); diff --git a/test/reduceRight.spec.js b/test/reduceRight.spec.js new file mode 100644 index 0000000000..f634ca6717 --- /dev/null +++ b/test/reduceRight.spec.js @@ -0,0 +1,59 @@ +import lodashStable from 'lodash'; +import { slice } from './utils'; +import reduceRight from '../src/reduceRight'; + +describe('reduceRight', () => { + const array = [1, 2, 3]; + + it('should use the last element of a collection as the default `accumulator`', () => { + expect(reduceRight(array)).toBe(3); + }); + + it('should provide correct `iteratee` arguments when iterating an array', () => { + let args; + + reduceRight( + array, + function () { + args || (args = slice.call(arguments)); + }, + 0, + ); + + expect(args, [0, 3, 2).toEqual(array]); + + args = undefined; + reduceRight(array, function () { + args || (args = slice.call(arguments)); + }); + + expect(args, [3, 2, 1).toEqual(array]); + }); + + it('should provide correct `iteratee` arguments when iterating an object', () => { + let args; + const object = { a: 1, b: 2 }; + const isFIFO = lodashStable.keys(object)[0] === 'a'; + + let expected = isFIFO ? [0, 2, 'b', object] : [0, 1, 'a', object]; + + reduceRight( + object, + function () { + args || (args = slice.call(arguments)); + }, + 0, + ); + + expect(args).toEqual(expected); + + args = undefined; + expected = isFIFO ? [2, 1, 'a', object] : [1, 2, 'b', object]; + + reduceRight(object, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual(expected); + }); +}); diff --git a/test/reject.spec.js b/test/reject.spec.js new file mode 100644 index 0000000000..ced7d65782 --- /dev/null +++ b/test/reject.spec.js @@ -0,0 +1,10 @@ +import { isEven } from './utils'; +import reject from '../src/reject'; + +describe('reject', () => { + const array = [1, 2, 3]; + + it('should return elements the `predicate` returns falsey for', () => { + expect(reject(array, isEven)).toEqual([1, 3]); + }); +}); diff --git a/test/reject.test.js b/test/reject.test.js deleted file mode 100644 index fcf305041c..0000000000 --- a/test/reject.test.js +++ /dev/null @@ -1,11 +0,0 @@ -import assert from 'assert'; -import { isEven } from './utils.js'; -import reject from '../reject.js'; - -describe('reject', function() { - var array = [1, 2, 3]; - - it('should return elements the `predicate` returns falsey for', function() { - assert.deepStrictEqual(reject(array, isEven), [1, 3]); - }); -}); diff --git a/test/remove.js b/test/remove.js deleted file mode 100644 index 92b0251511..0000000000 --- a/test/remove.js +++ /dev/null @@ -1,80 +0,0 @@ -import assert from 'assert'; -import { isEven, slice } from './utils.js'; -import remove from '../remove.js'; - -describe('remove', function() { - it('should modify the array and return removed elements', function() { - var array = [1, 2, 3, 4], - actual = remove(array, isEven); - - assert.deepStrictEqual(array, [1, 3]); - assert.deepStrictEqual(actual, [2, 4]); - }); - - it('should provide correct `predicate` arguments', function() { - var argsList = [], - array = [1, 2, 3], - clone = array.slice(); - - remove(array, function(n, index) { - var args = slice.call(arguments); - args[2] = args[2].slice(); - argsList.push(args); - return isEven(index); - }); - - assert.deepStrictEqual(argsList, [[1, 0, clone], [2, 1, clone], [3, 2, clone]]); - }); - - it('should work with `_.matches` shorthands', function() { - var objects = [{ 'a': 0, 'b': 1 }, { 'a': 1, 'b': 2 }]; - remove(objects, { 'a': 1 }); - assert.deepStrictEqual(objects, [{ 'a': 0, 'b': 1 }]); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - var objects = [{ 'a': 0, 'b': 1 }, { 'a': 1, 'b': 2 }]; - remove(objects, ['a', 1]); - assert.deepStrictEqual(objects, [{ 'a': 0, 'b': 1 }]); - }); - - it('should work with `_.property` shorthands', function() { - var objects = [{ 'a': 0 }, { 'a': 1 }]; - remove(objects, 'a'); - assert.deepStrictEqual(objects, [{ 'a': 0 }]); - }); - - it('should preserve holes in arrays', function() { - var array = [1, 2, 3, 4]; - delete array[1]; - delete array[3]; - - remove(array, function(n) { - return n === 1; - }); - - assert.ok(!('0' in array)); - assert.ok(!('2' in array)); - }); - - it('should treat holes as `undefined`', function() { - var array = [1, 2, 3]; - delete array[1]; - - remove(array, function(n) { - return n == null; - }); - - assert.deepStrictEqual(array, [1, 3]); - }); - - it('should not mutate the array until all elements to remove are determined', function() { - var array = [1, 2, 3]; - - remove(array, function(n, index) { - return isEven(index); - }); - - assert.deepStrictEqual(array, [2]); - }); -}); diff --git a/test/remove.spec.js b/test/remove.spec.js new file mode 100644 index 0000000000..1771475ace --- /dev/null +++ b/test/remove.spec.js @@ -0,0 +1,83 @@ +import { isEven, slice } from './utils'; +import remove from '../src/remove'; + +describe('remove', () => { + it('should modify the array and return removed elements', () => { + const array = [1, 2, 3, 4]; + const actual = remove(array, isEven); + + expect(array, [1).toEqual(3]); + expect(actual, [2).toEqual(4]); + }); + + it('should provide correct `predicate` arguments', () => { + const argsList = []; + const array = [1, 2, 3]; + const clone = array.slice(); + + remove(array, function (n, index) { + const args = slice.call(arguments); + args[2] = args[2].slice(); + argsList.push(args); + return isEven(index); + }); + + expect(argsList).toEqual([ + [1, 0, clone], + [2, 1, clone], + [3, 2, clone], + ]); + }); + + it('should work with `_.matches` shorthands', () => { + const objects = [ + { a: 0, b: 1 }, + { a: 1, b: 2 }, + ]; + remove(objects, { a: 1 }); + expect(objects, [{ a: 0).toEqual(b: 1 }]); + }); + + it('should work with `_.matchesProperty` shorthands', () => { + const objects = [ + { a: 0, b: 1 }, + { a: 1, b: 2 }, + ]; + remove(objects, ['a', 1]); + expect(objects, [{ a: 0).toEqual(b: 1 }]); + }); + + it('should work with `_.property` shorthands', () => { + const objects = [{ a: 0 }, { a: 1 }]; + remove(objects, 'a'); + expect(objects).toEqual([{ a: 0 }]); + }); + + it('should preserve holes in arrays', () => { + const array = [1, 2, 3, 4]; + delete array[1]; + delete array[3]; + + remove(array, (n) => n === 1); + + expect(('0' in array)).toBe(false) + expect(('2' in array)).toBe(false) + }); + + it('should treat holes as `undefined`', () => { + const array = [1, 2, 3]; + delete array[1]; + + remove(array, (n) => n === null); + + expect(array, [1).toEqual(3]); + }); + + it('should not mutate the array until all elements to remove are determined', () => { + const array = [1, 2, 3]; + + remove(array, (n, index) => isEven(index)); + + expect(array).toEqual([2]); + }); +}); diff --git a/test/repeat.js b/test/repeat.js deleted file mode 100644 index caa4e2e606..0000000000 --- a/test/repeat.js +++ /dev/null @@ -1,46 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubThree } from './utils.js'; -import repeat from '../repeat.js'; - -describe('repeat', function() { - var string = 'abc'; - - it('should repeat a string `n` times', function() { - assert.strictEqual(repeat('*', 3), '***'); - assert.strictEqual(repeat(string, 2), 'abcabc'); - }); - - it('should treat falsey `n` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? string : ''; - }); - - var actual = lodashStable.map(falsey, function(n, index) { - return index ? repeat(string, n) : repeat(string); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an empty string if `n` is <= `0`', function() { - assert.strictEqual(repeat(string, 0), ''); - assert.strictEqual(repeat(string, -2), ''); - }); - - it('should coerce `n` to an integer', function() { - assert.strictEqual(repeat(string, '2'), 'abcabc'); - assert.strictEqual(repeat(string, 2.6), 'abcabc'); - assert.strictEqual(repeat('*', { 'valueOf': stubThree }), '***'); - }); - - it('should coerce `string` to a string', function() { - assert.strictEqual(repeat(Object(string), 2), 'abcabc'); - assert.strictEqual(repeat({ 'toString': lodashStable.constant('*') }, 3), '***'); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var actual = lodashStable.map(['a', 'b', 'c'], repeat); - assert.deepStrictEqual(actual, ['a', 'b', 'c']); - }); -}); diff --git a/test/repeat.spec.js b/test/repeat.spec.js new file mode 100644 index 0000000000..c42e8d8592 --- /dev/null +++ b/test/repeat.spec.js @@ -0,0 +1,43 @@ +import lodashStable from 'lodash'; +import { falsey, stubThree } from './utils'; +import repeat from '../src/repeat'; + +describe('repeat', () => { + const string = 'abc'; + + it('should repeat a string `n` times', () => { + expect(repeat('*', 3)).toBe('***'); + expect(repeat(string, 2)).toBe('abcabc'); + }); + + it('should treat falsey `n` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => (value === undefined ? string : '')); + + const actual = lodashStable.map(falsey, (n, index) => + index ? repeat(string, n) : repeat(string), + ); + + expect(actual).toEqual(expected); + }); + + it('should return an empty string if `n` is <= `0`', () => { + expect(repeat(string, 0)).toBe(''); + expect(repeat(string, -2)).toBe(''); + }); + + it('should coerce `n` to an integer', () => { + expect(repeat(string, '2')).toBe('abcabc'); + expect(repeat(string, 2.6)).toBe('abcabc'); + expect(repeat('*', { valueOf: stubThree })).toBe('***'); + }); + + it('should coerce `string` to a string', () => { + expect(repeat(Object(string), 2)).toBe('abcabc'); + expect(repeat({ toString: lodashStable.constant('*') }, 3)).toBe('***'); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const actual = lodashStable.map(['a', 'b', 'c'], repeat); + expect(actual, ['a', 'b').toEqual('c']); + }); +}); diff --git a/test/replace.spec.js b/test/replace.spec.js new file mode 100644 index 0000000000..a2f51bbd8e --- /dev/null +++ b/test/replace.spec.js @@ -0,0 +1,9 @@ +import replace from '../src/replace'; + +describe('replace', () => { + it('should replace the matched pattern', () => { + const string = 'abcde'; + expect(replace(string, 'de', '123')).toBe('abc123'); + expect(replace(string, /[bd]/g, '-')).toBe('a-c-e'); + }); +}); diff --git a/test/replace.test.js b/test/replace.test.js deleted file mode 100644 index f28a9f03c8..0000000000 --- a/test/replace.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import replace from '../replace.js'; - -describe('replace', function() { - it('should replace the matched pattern', function() { - var string = 'abcde'; - assert.strictEqual(replace(string, 'de', '123'), 'abc123'); - assert.strictEqual(replace(string, /[bd]/g, '-'), 'a-c-e'); - }); -}); diff --git a/test/rest.js b/test/rest.js deleted file mode 100644 index d7b98de46b..0000000000 --- a/test/rest.js +++ /dev/null @@ -1,49 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, _ } from './utils.js'; - -describe('rest', function() { - function fn(a, b, c) { - return slice.call(arguments); - } - - it('should apply a rest parameter to `func`', function() { - var rest = _.rest(fn); - assert.deepStrictEqual(rest(1, 2, 3, 4), [1, 2, [3, 4]]); - }); - - it('should work with `start`', function() { - var rest = _.rest(fn, 1); - assert.deepStrictEqual(rest(1, 2, 3, 4), [1, [2, 3, 4]]); - }); - - it('should treat `start` as `0` for `NaN` or negative values', function() { - var values = [-1, NaN, 'a'], - expected = lodashStable.map(values, lodashStable.constant([[1, 2, 3, 4]])); - - var actual = lodashStable.map(values, function(value) { - var rest = _.rest(fn, value); - return rest(1, 2, 3, 4); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should coerce `start` to an integer', function() { - var rest = _.rest(fn, 1.6); - assert.deepStrictEqual(rest(1, 2, 3), [1, [2, 3]]); - }); - - it('should use an empty array when `start` is not reached', function() { - var rest = _.rest(fn); - assert.deepStrictEqual(rest(1), [1, undefined, []]); - }); - - it('should work on functions with more than three parameters', function() { - var rest = _.rest(function(a, b, c, d) { - return slice.call(arguments); - }); - - assert.deepStrictEqual(rest(1, 2, 3, 4, 5), [1, 2, 3, [4, 5]]); - }); -}); diff --git a/test/rest.spec.js b/test/rest.spec.js new file mode 100644 index 0000000000..4476b9ab2b --- /dev/null +++ b/test/rest.spec.js @@ -0,0 +1,48 @@ +import lodashStable from 'lodash'; +import { slice, _ } from './utils'; + +describe('rest', () => { + function fn(a, b, c) { + return slice.call(arguments); + } + + it('should apply a rest parameter to `func`', () => { + const rest = _.rest(fn); + expect(rest(1, 2, 3, 4)).toEqual([1, 2, [3, 4]]); + }); + + it('should work with `start`', () => { + const rest = _.rest(fn, 1); + expect(rest(1, 2, 3, 4)).toEqual([1, [2, 3, 4]]); + }); + + it('should treat `start` as `0` for `NaN` or negative values', () => { + const values = [-1, NaN, 'a']; + const expected = lodashStable.map(values, lodashStable.constant([[1, 2, 3, 4]])); + + const actual = lodashStable.map(values, (value) => { + const rest = _.rest(fn, value); + return rest(1, 2, 3, 4); + }); + + expect(actual).toEqual(expected); + }); + + it('should coerce `start` to an integer', () => { + const rest = _.rest(fn, 1.6); + expect(rest(1, 2, 3)).toEqual([1, [2, 3]]); + }); + + it('should use an empty array when `start` is not reached', () => { + const rest = _.rest(fn); + expect(rest(1)).toEqual([1, undefined, []]); + }); + + it('should work on functions with more than three parameters', () => { + const rest = _.rest(function (a, b, c, d) { + return slice.call(arguments); + }); + + expect(rest(1, 2, 3, 4, 5)).toEqual([1, 2, 3, [4, 5]]); + }); +}); diff --git a/test/result.spec.js b/test/result.spec.js new file mode 100644 index 0000000000..81aa986fd7 --- /dev/null +++ b/test/result.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import { stubB } from './utils'; +import result from '../src/result'; + +describe('result', () => { + const object = { a: 1, b: stubB }; + + it('should invoke function values', () => { + expect(result(object, 'b')).toBe('b'); + }); + + it('should invoke default function values', () => { + const actual = result(object, 'c', object.b); + expect(actual).toBe('b'); + }); + + it('should invoke nested function values', () => { + const value = { a: lodashStable.constant({ b: stubB }) }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(result(value, path)).toBe('b'); + }); + }); + + it('should invoke deep property methods with the correct `this` binding', () => { + const value = { + a: { + b: function () { + return this.c; + }, + c: 1, + }, + }; + + lodashStable.each(['a.b', ['a', 'b']], (path) => { + expect(result(value, path)).toBe(1); + }); + }); +}); diff --git a/test/result.test.js b/test/result.test.js deleted file mode 100644 index 3087ac0c64..0000000000 --- a/test/result.test.js +++ /dev/null @@ -1,33 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubB } from './utils.js'; -import result from '../result.js'; - -describe('result', function() { - var object = { 'a': 1, 'b': stubB }; - - it('should invoke function values', function() { - assert.strictEqual(result(object, 'b'), 'b'); - }); - - it('should invoke default function values', function() { - var actual = result(object, 'c', object.b); - assert.strictEqual(actual, 'b'); - }); - - it('should invoke nested function values', function() { - var value = { 'a': lodashStable.constant({ 'b': stubB }) }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(result(value, path), 'b'); - }); - }); - - it('should invoke deep property methods with the correct `this` binding', function() { - var value = { 'a': { 'b': function() { return this.c; }, 'c': 1 } }; - - lodashStable.each(['a.b', ['a', 'b']], function(path) { - assert.strictEqual(result(value, path), 1); - }); - }); -}); diff --git a/test/reverse.js b/test/reverse.js deleted file mode 100644 index f6c80ca590..0000000000 --- a/test/reverse.js +++ /dev/null @@ -1,94 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, identity } from './utils.js'; -import reverse from '../reverse.js'; -import compact from '../compact.js'; -import head from '../head.js'; - -describe('reverse', function() { - var largeArray = lodashStable.range(LARGE_ARRAY_SIZE).concat(null), - smallArray = [0, 1, 2, null]; - - it('should reverse `array`', function() { - var array = [1, 2, 3], - actual = reverse(array); - - assert.strictEqual(actual, array); - assert.deepStrictEqual(array, [3, 2, 1]); - }); - - it('should return the wrapped reversed `array`', function() { - lodashStable.times(2, function(index) { - var array = (index ? largeArray : smallArray).slice(), - clone = array.slice(), - wrapped = _(array).reverse(), - actual = wrapped.value(); - - assert.ok(wrapped instanceof _); - assert.strictEqual(actual, array); - assert.deepStrictEqual(actual, clone.slice().reverse()); - }); - }); - - it('should work in a lazy sequence', function() { - lodashStable.times(2, function(index) { - var array = (index ? largeArray : smallArray).slice(), - expected = array.slice(), - actual = _(array).slice(1).reverse().value(); - - assert.deepStrictEqual(actual, expected.slice(1).reverse()); - assert.deepStrictEqual(array, expected); - }); - }); - - it('should be lazy when in a lazy sequence', function() { - var spy = { - 'toString': function() { - throw new Error('spy was revealed'); - } - }; - - var array = largeArray.concat(spy), - expected = array.slice(); - - try { - var wrapped = _(array).slice(1).map(String).reverse(), - actual = wrapped.last(); - } catch (e) {} - - assert.ok(wrapped instanceof _); - assert.strictEqual(actual, '1'); - assert.deepEqual(array, expected); - }); - - it('should work in a hybrid sequence', function() { - lodashStable.times(2, function(index) { - var clone = (index ? largeArray : smallArray).slice(); - - lodashStable.each(['map', 'filter'], function(methodName) { - var array = clone.slice(), - expected = clone.slice(1, -1).reverse(), - actual = _(array)[methodName](identity).thru(compact).reverse().value(); - - assert.deepStrictEqual(actual, expected); - - array = clone.slice(); - actual = _(array).thru(compact)[methodName](identity).pull(1).push(3).reverse().value(); - - assert.deepStrictEqual(actual, [3].concat(expected.slice(0, -1))); - }); - }); - }); - - it('should track the `__chain__` value of a wrapper', function() { - lodashStable.times(2, function(index) { - var array = (index ? largeArray : smallArray).slice(), - expected = array.slice().reverse(), - wrapped = _(array).chain().reverse().head(); - - assert.ok(wrapped instanceof _); - assert.strictEqual(wrapped.value(), head(expected)); - assert.deepStrictEqual(array, expected); - }); - }); -}); diff --git a/test/reverse.spec.js b/test/reverse.spec.js new file mode 100644 index 0000000000..feca690c6b --- /dev/null +++ b/test/reverse.spec.js @@ -0,0 +1,31 @@ +import lodashStable from 'lodash'; +import { LARGE_ARRAY_SIZE, identity } from './utils'; +import reverse from '../src/reverse'; +import compact from '../src/compact'; +import head from '../src/head'; + +describe('reverse', () => { + const largeArray = lodashStable.range(LARGE_ARRAY_SIZE).concat(null); + const smallArray = [0, 1, 2, null]; + + it('should reverse `array`', () => { + const array = [1, 2, 3]; + const actual = reverse(array); + + expect(actual).toBe(array); + expect(array, [3, 2).toEqual(1]); + }); + + it('should return the wrapped reversed `array`', () => { + lodashStable.times(2, (index) => { + const array = (index ? largeArray : smallArray).slice(); + const clone = array.slice(); + const wrapped = _(array).reverse(); + const actual = wrapped.value(); + + expect(wrapped instanceof _) + expect(actual).toBe(array); + expect(actual).toEqual(clone.slice().reverse()); + }); + }); +}); diff --git a/test/round-methods.js b/test/round-methods.js deleted file mode 100644 index be0a92498f..0000000000 --- a/test/round-methods.js +++ /dev/null @@ -1,82 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, MAX_SAFE_INTEGER, stubFalse } from './utils.js'; -import round from '../round.js'; - -describe('round methods', function() { - lodashStable.each(['ceil', 'floor', 'round'], function(methodName) { - var func = _[methodName], - isCeil = methodName == 'ceil', - isFloor = methodName == 'floor'; - - it('`_.' + methodName + '` should return a rounded number without a precision', function() { - var actual = func(4.006); - assert.strictEqual(actual, isCeil ? 5 : 4); - }); - - it('`_.' + methodName + '` should work with a precision of `0`', function() { - var actual = func(4.006, 0); - assert.strictEqual(actual, isCeil ? 5 : 4); - }); - - it('`_.' + methodName + '` should work with a positive precision', function() { - var actual = func(4.016, 2); - assert.strictEqual(actual, isFloor ? 4.01 : 4.02); - - actual = func(4.1, 2); - assert.strictEqual(actual, 4.1); - }); - - it('`_.' + methodName + '` should work with a negative precision', function() { - var actual = func(4160, -2); - assert.strictEqual(actual, isFloor ? 4100 : 4200); - }); - - it('`_.' + methodName + '` should coerce `precision` to an integer', function() { - var actual = func(4.006, NaN); - assert.strictEqual(actual, isCeil ? 5 : 4); - - var expected = isFloor ? 4.01 : 4.02; - - actual = func(4.016, 2.6); - assert.strictEqual(actual, expected); - - actual = func(4.016, '+2'); - assert.strictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with exponential notation and `precision`', function() { - var actual = func(5e1, 2); - assert.deepStrictEqual(actual, 50); - - actual = func('5e', 1); - assert.deepStrictEqual(actual, NaN); - - actual = func('5e1e1', 1); - assert.deepStrictEqual(actual, NaN); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var values = [[0], [-0], ['0'], ['-0'], [0, 1], [-0, 1], ['0', 1], ['-0', 1]], - expected = [Infinity, -Infinity, Infinity, -Infinity, Infinity, -Infinity, Infinity, -Infinity]; - - var actual = lodashStable.map(values, function(args) { - return 1 / func.apply(undefined, args); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should not return `NaN` for large `precision` values', function() { - var results = [ - round(10.0000001, 1000), - round(MAX_SAFE_INTEGER, 293) - ]; - - var expected = lodashStable.map(results, stubFalse), - actual = lodashStable.map(results, lodashStable.isNaN); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/round-methods.spec.js b/test/round-methods.spec.js new file mode 100644 index 0000000000..72a336ac52 --- /dev/null +++ b/test/round-methods.spec.js @@ -0,0 +1,85 @@ +import lodashStable from 'lodash'; +import { _, MAX_SAFE_INTEGER, stubFalse } from './utils'; +import round from '../src/round'; + +describe('round methods', () => { + lodashStable.each(['ceil', 'floor', 'round'], (methodName) => { + const func = _[methodName]; + const isCeil = methodName === 'ceil'; + const isFloor = methodName === 'floor'; + + it(`\`_.${methodName}\` should return a rounded number without a precision`, () => { + const actual = func(4.006); + expect(actual).toBe(isCeil ? 5 : 4); + }); + + it(`\`_.${methodName}\` should work with a precision of \`0\``, () => { + const actual = func(4.006, 0); + expect(actual).toBe(isCeil ? 5 : 4); + }); + + it(`\`_.${methodName}\` should work with a positive precision`, () => { + let actual = func(4.016, 2); + expect(actual).toBe(isFloor ? 4.01 : 4.02); + + actual = func(4.1, 2); + expect(actual).toBe(4.1); + }); + + it(`\`_.${methodName}\` should work with a negative precision`, () => { + const actual = func(4160, -2); + expect(actual).toBe(isFloor ? 4100 : 4200); + }); + + it(`\`_.${methodName}\` should coerce \`precision\` to an integer`, () => { + let actual = func(4.006, NaN); + expect(actual).toBe(isCeil ? 5 : 4); + + const expected = isFloor ? 4.01 : 4.02; + + actual = func(4.016, 2.6); + expect(actual).toBe(expected); + + actual = func(4.016, '+2'); + expect(actual).toBe(expected); + }); + + it(`\`_.${methodName}\` should work with exponential notation and \`precision\``, () => { + let actual = func(5e1, 2); + expect(actual).toEqual(50); + + actual = func('5e', 1); + expect(actual).toEqual(NaN); + + actual = func('5e1e1', 1); + expect(actual).toEqual(NaN); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const values = [[0], [-0], ['0'], ['-0'], [0, 1], [-0, 1], ['0', 1], ['-0', 1]]; + const expected = [ + Infinity, + -Infinity, + Infinity, + -Infinity, + Infinity, + -Infinity, + Infinity, + -Infinity, + ]; + + const actual = lodashStable.map(values, (args) => 1 / func.apply(undefined, args)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should not return \`NaN\` for large \`precision\` values`, () => { + const results = [round(10.0000001, 1000), round(MAX_SAFE_INTEGER, 293)]; + + const expected = lodashStable.map(results, stubFalse); + const actual = lodashStable.map(results, lodashStable.isNaN); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/runInContext.js b/test/runInContext.js deleted file mode 100644 index 402ddac66d..0000000000 --- a/test/runInContext.js +++ /dev/null @@ -1,29 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import runInContext from '../runInContext.js'; -import uniqueId from '../uniqueId.js'; - -describe('runInContext', function() { - it('should not require a fully populated `context` object', function() { - var lodash = runInContext({ - 'setTimeout': function(func) { func(); } - }); - - var pass = false; - lodash.delay(function() { pass = true; }, 32); - assert.ok(pass); - }); - - it('should use a zeroed `_.uniqueId` counter', function() { - lodashStable.times(2, uniqueId); - - var oldId = Number(uniqueId()), - lodash = runInContext(); - - assert.ok(uniqueId() > oldId); - - var id = lodash.uniqueId(); - assert.strictEqual(id, '1'); - assert.ok(id < oldId); - }); -}); diff --git a/test/sample.js b/test/sample.js deleted file mode 100644 index b29634a42f..0000000000 --- a/test/sample.js +++ /dev/null @@ -1,32 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { empties, noop } from './utils.js'; -import sample from '../sample.js'; - -describe('sample', function() { - var array = [1, 2, 3]; - - it('should return a random element', function() { - var actual = sample(array); - assert.ok(lodashStable.includes(array, actual)); - }); - - it('should return `undefined` when sampling empty collections', function() { - var expected = lodashStable.map(empties, noop); - - var actual = lodashStable.transform(empties, function(result, value) { - try { - result.push(sample(value)); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should sample an object', function() { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - actual = sample(object); - - assert.ok(lodashStable.includes(array, actual)); - }); -}); diff --git a/test/sample.spec.js b/test/sample.spec.js new file mode 100644 index 0000000000..adb7165fb6 --- /dev/null +++ b/test/sample.spec.js @@ -0,0 +1,31 @@ +import lodashStable from 'lodash'; +import { empties, noop } from './utils'; +import sample from '../src/sample'; + +describe('sample', () => { + const array = [1, 2, 3]; + + it('should return a random element', () => { + const actual = sample(array); + expect(lodashStable.includes(array, actual)); + }); + + it('should return `undefined` when sampling empty collections', () => { + const expected = lodashStable.map(empties, noop); + + const actual = lodashStable.transform(empties, (result, value) => { + try { + result.push(sample(value)); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should sample an object', () => { + const object = { a: 1, b: 2, c: 3 }; + const actual = sample(object); + + expect(lodashStable.includes(array, actual)); + }); +}); diff --git a/test/sampleSize.js b/test/sampleSize.js deleted file mode 100644 index e394c00c52..0000000000 --- a/test/sampleSize.js +++ /dev/null @@ -1,76 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, empties, stubArray } from './utils.js'; -import sampleSize from '../sampleSize.js'; - -describe('sampleSize', function() { - var array = [1, 2, 3]; - - it('should return an array of random elements', function() { - var actual = sampleSize(array, 2); - - assert.strictEqual(actual.length, 2); - assert.deepStrictEqual(lodashStable.difference(actual, array), []); - }); - - it('should contain elements of the collection', function() { - var actual = sampleSize(array, array.length).sort(); - - assert.deepStrictEqual(actual, array); - }); - - it('should treat falsey `size` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? ['a'] : []; - }); - - var actual = lodashStable.map(falsey, function(size, index) { - return index ? sampleSize(['a'], size) : sampleSize(['a']); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an empty array when `n` < `1` or `NaN`', function() { - lodashStable.each([0, -1, -Infinity], function(n) { - assert.deepStrictEqual(sampleSize(array, n), []); - }); - }); - - it('should return all elements when `n` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(n) { - var actual = sampleSize(array, n).sort(); - assert.deepStrictEqual(actual, array); - }); - }); - - it('should coerce `n` to an integer', function() { - var actual = sampleSize(array, 1.6); - assert.strictEqual(actual.length, 1); - }); - - it('should return an empty array for empty collections', function() { - var expected = lodashStable.map(empties, stubArray); - - var actual = lodashStable.transform(empties, function(result, value) { - try { - result.push(sampleSize(value, 1)); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should sample an object', function() { - var object = { 'a': 1, 'b': 2, 'c': 3 }, - actual = sampleSize(object, 2); - - assert.strictEqual(actual.length, 2); - assert.deepStrictEqual(lodashStable.difference(actual, lodashStable.values(object)), []); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var actual = lodashStable.map([['a']], sampleSize); - assert.deepStrictEqual(actual, [['a']]); - }); -}); diff --git a/test/sampleSize.spec.js b/test/sampleSize.spec.js new file mode 100644 index 0000000000..4a940f5f41 --- /dev/null +++ b/test/sampleSize.spec.js @@ -0,0 +1,73 @@ +import lodashStable from 'lodash'; +import { falsey, empties, stubArray } from './utils'; +import sampleSize from '../src/sampleSize'; + +describe('sampleSize', () => { + const array = [1, 2, 3]; + + it('should return an array of random elements', () => { + const actual = sampleSize(array, 2); + + expect(actual.length).toBe(2); + expect(lodashStable.difference(actual, array)).toEqual([]); + }); + + it('should contain elements of the collection', () => { + const actual = sampleSize(array, array.length).sort(); + + expect(actual).toEqual(array); + }); + + it('should treat falsey `size` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => (value === undefined ? ['a'] : [])); + + const actual = lodashStable.map(falsey, (size, index) => + index ? sampleSize(['a'], size) : sampleSize(['a']), + ); + + expect(actual).toEqual(expected); + }); + + it('should return an empty array when `n` < `1` or `NaN`', () => { + lodashStable.each([0, -1, -Infinity], (n) => { + expect(sampleSize(array, n)).toEqual([]); + }); + }); + + it('should return all elements when `n` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (n) => { + const actual = sampleSize(array, n).sort(); + expect(actual).toEqual(array); + }); + }); + + it('should coerce `n` to an integer', () => { + const actual = sampleSize(array, 1.6); + expect(actual.length).toBe(1); + }); + + it('should return an empty array for empty collections', () => { + const expected = lodashStable.map(empties, stubArray); + + const actual = lodashStable.transform(empties, (result, value) => { + try { + result.push(sampleSize(value, 1)); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should sample an object', () => { + const object = { a: 1, b: 2, c: 3 }; + const actual = sampleSize(object, 2); + + expect(actual.length).toBe(2); + expect(lodashStable.difference(actual, lodashStable.values(object))).toEqual([]); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const actual = lodashStable.map([['a']], sampleSize); + expect(actual).toEqual([['a']]); + }); +}); diff --git a/test/set-methods.js b/test/set-methods.js deleted file mode 100644 index c7c69a349d..0000000000 --- a/test/set-methods.js +++ /dev/null @@ -1,172 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, symbol, defineProperty } from './utils.js'; -import unset from '../unset.js'; - -describe('set methods', function() { - lodashStable.each(['update', 'updateWith', 'set', 'setWith'], function(methodName) { - var func = _[methodName], - isUpdate = /^update/.test(methodName); - - var oldValue = 1, - value = 2, - updater = isUpdate ? lodashStable.constant(value) : value; - - it('`_.' + methodName + '` should set property values', function() { - lodashStable.each(['a', ['a']], function(path) { - var object = { 'a': oldValue }, - actual = func(object, path, updater); - - assert.strictEqual(actual, object); - assert.strictEqual(object.a, value); - }); - }); - - it('`_.' + methodName + '` should preserve the sign of `0`', function() { - var props = [-0, Object(-0), 0, Object(0)], - expected = lodashStable.map(props, lodashStable.constant(value)); - - var actual = lodashStable.map(props, function(key) { - var object = { '-0': 'a', '0': 'b' }; - func(object, key, updater); - return object[lodashStable.toString(key)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should unset symbol keyed property values', function() { - if (Symbol) { - var object = {}; - object[symbol] = 1; - - assert.strictEqual(unset(object, symbol), true); - assert.ok(!(symbol in object)); - } - }); - - it('`_.' + methodName + '` should set deep property values', function() { - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var object = { 'a': { 'b': oldValue } }, - actual = func(object, path, updater); - - assert.strictEqual(actual, object); - assert.strictEqual(object.a.b, value); - }); - }); - - it('`_.' + methodName + '` should set a key over a path', function() { - lodashStable.each(['a.b', ['a.b']], function(path) { - var object = { 'a.b': oldValue }, - actual = func(object, path, updater); - - assert.strictEqual(actual, object); - assert.deepStrictEqual(object, { 'a.b': value }); - }); - }); - - it('`_.' + methodName + '` should not coerce array paths to strings', function() { - var object = { 'a,b,c': 1, 'a': { 'b': { 'c': 1 } } }; - - func(object, ['a', 'b', 'c'], updater); - assert.strictEqual(object.a.b.c, value); - }); - - it('`_.' + methodName + '` should not ignore empty brackets', function() { - var object = {}; - - func(object, 'a[]', updater); - assert.deepStrictEqual(object, { 'a': { '': value } }); - }); - - it('`_.' + methodName + '` should handle empty paths', function() { - lodashStable.each([['', ''], [[], ['']]], function(pair, index) { - var object = {}; - - func(object, pair[0], updater); - assert.deepStrictEqual(object, index ? {} : { '': value }); - - func(object, pair[1], updater); - assert.deepStrictEqual(object, { '': value }); - }); - }); - - it('`_.' + methodName + '` should handle complex paths', function() { - var object = { 'a': { '1.23': { '["b"]': { 'c': { "['d']": { '\ne\n': { 'f': { 'g': oldValue } } } } } } } }; - - var paths = [ - 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', - ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'] - ]; - - lodashStable.each(paths, function(path) { - func(object, path, updater); - assert.strictEqual(object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f.g, value); - object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f.g = oldValue; - }); - }); - - it('`_.' + methodName + '` should create parts of `path` that are missing', function() { - var object = {}; - - lodashStable.each(['a[1].b.c', ['a', '1', 'b', 'c']], function(path) { - var actual = func(object, path, updater); - - assert.strictEqual(actual, object); - assert.deepStrictEqual(actual, { 'a': [undefined, { 'b': { 'c': value } }] }); - assert.ok(!('0' in object.a)); - - delete object.a; - }); - }); - - it('`_.' + methodName + '` should not error when `object` is nullish', function() { - var values = [null, undefined], - expected = [[null, null], [undefined, undefined]]; - - var actual = lodashStable.map(values, function(value) { - try { - return [func(value, 'a.b', updater), func(value, ['a', 'b'], updater)]; - } catch (e) { - return e.message; - } - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should overwrite primitives in the path', function() { - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var object = { 'a': '' }; - - func(object, path, updater); - assert.deepStrictEqual(object, { 'a': { 'b': 2 } }); - }); - }); - - it('`_.' + methodName + '` should not create an array for missing non-index property names that start with numbers', function() { - var object = {}; - - func(object, ['1a', '2b', '3c'], updater); - assert.deepStrictEqual(object, { '1a': { '2b': { '3c': value } } }); - }); - - it('`_.' + methodName + '` should not assign values that are the same as their destinations', function() { - lodashStable.each(['a', ['a'], { 'a': 1 }, NaN], function(value) { - var object = {}, - pass = true, - updater = isUpdate ? lodashStable.constant(value) : value; - - defineProperty(object, 'a', { - 'configurable': true, - 'enumerable': true, - 'get': lodashStable.constant(value), - 'set': function() { pass = false; } - }); - - func(object, 'a', updater); - assert.ok(pass); - }); - }); - }); -}); diff --git a/test/set-methods.spec.js b/test/set-methods.spec.js new file mode 100644 index 0000000000..2c6390e3f7 --- /dev/null +++ b/test/set-methods.spec.js @@ -0,0 +1,184 @@ +import lodashStable from 'lodash'; +import { _, symbol, defineProperty } from './utils'; +import unset from '../src/unset'; + +describe('set methods', () => { + lodashStable.each(['update', 'updateWith', 'set', 'setWith'], (methodName) => { + const func = _[methodName]; + const isUpdate = /^update/.test(methodName); + + const oldValue = 1; + const value = 2; + const updater = isUpdate ? lodashStable.constant(value) : value; + + it(`\`_.${methodName}\` should set property values`, () => { + lodashStable.each(['a', ['a']], (path) => { + const object = { a: oldValue }; + const actual = func(object, path, updater); + + expect(actual).toBe(object); + expect(object.a).toBe(value); + }); + }); + + it(`\`_.${methodName}\` should preserve the sign of \`0\``, () => { + const props = [-0, Object(-0), 0, Object(0)]; + const expected = lodashStable.map(props, lodashStable.constant(value)); + + const actual = lodashStable.map(props, (key) => { + const object = { '-0': 'a', 0: 'b' }; + func(object, key, updater); + return object[lodashStable.toString(key)]; + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should unset symbol keyed property values`, () => { + if (Symbol) { + const object = {}; + object[symbol] = 1; + + expect(unset(object, symbol)).toBe(true); + expect((symbol in object)).toBe(false) + } + }); + + it(`\`_.${methodName}\` should set deep property values`, () => { + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const object = { a: { b: oldValue } }; + const actual = func(object, path, updater); + + expect(actual).toBe(object); + expect(object.a.b).toBe(value); + }); + }); + + it(`\`_.${methodName}\` should set a key over a path`, () => { + lodashStable.each(['a.b', ['a.b']], (path) => { + const object = { 'a.b': oldValue }; + const actual = func(object, path, updater); + + expect(actual).toBe(object); + expect(object).toEqual({ 'a.b': value }); + }); + }); + + it(`\`_.${methodName}\` should not coerce array paths to strings`, () => { + const object = { 'a,b,c': 1, a: { b: { c: 1 } } }; + + func(object, ['a', 'b', 'c'], updater); + expect(object.a.b.c).toBe(value); + }); + + it(`\`_.${methodName}\` should not ignore empty brackets`, () => { + const object = {}; + + func(object, 'a[]', updater); + expect(object).toEqual({ a: { '': value } }); + }); + + it(`\`_.${methodName}\` should handle empty paths`, () => { + lodashStable.each( + [ + ['', ''], + [[], ['']], + ], + (pair, index) => { + const object = {}; + + func(object, pair[0], updater); + expect(object).toEqual(index ? {} : { '': value }); + + func(object, pair[1], updater); + expect(object).toEqual({ '': value }); + }, + ); + }); + + it(`\`_.${methodName}\` should handle complex paths`, () => { + const object = { + a: { 1.23: { '["b"]': { c: { "['d']": { '\ne\n': { f: { g: oldValue } } } } } } }, + }; + + const paths = [ + 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', + ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'], + ]; + + lodashStable.each(paths, (path) => { + func(object, path, updater); + expect(object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f.g).toBe(value); + object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f.g = oldValue; + }); + }); + + it(`\`_.${methodName}\` should create parts of \`path\` that are missing`, () => { + const object = {}; + + lodashStable.each(['a[1].b.c', ['a', '1', 'b', 'c']], (path) => { + const actual = func(object, path, updater); + + expect(actual).toBe(object); + expect(actual, { a: [undefined).toEqual({ b: { c: value } }] }); + expect(('0' in object.a)).toBe(false) + + delete object.a; + }); + }); + + it(`\`_.${methodName}\` should not error when \`object\` is nullish`, () => { + const values = [null, undefined]; + const expected = [ + [null, null], + [undefined, undefined], + ]; + + const actual = lodashStable.map(values, (value) => { + try { + return [func(value, 'a.b', updater), func(value, ['a', 'b'], updater)]; + } catch (e) { + return e.message; + } + }); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should overwrite primitives in the path`, () => { + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const object = { a: '' }; + + func(object, path, updater); + expect(object).toEqual({ a: { b: 2 } }); + }); + }); + + it(`\`_.${methodName}\` should not create an array for missing non-index property names that start with numbers`, () => { + const object = {}; + + func(object, ['1a', '2b', '3c'], updater); + expect(object).toEqual({ '1a': { '2b': { '3c': value } } }); + }); + + it(`\`_.${methodName}\` should not assign values that are the same as their destinations`, () => { + lodashStable.each(['a', ['a'], { a: 1 }, NaN], (value) => { + const object = {}; + let pass = true; + const updater = isUpdate ? lodashStable.constant(value) : value; + + defineProperty(object, 'a', { + configurable: true, + enumerable: true, + get: lodashStable.constant(value), + set: function () { + pass = false; + }, + }); + + func(object, 'a', updater); + expect(pass) + }); + }); + }); +}); diff --git a/test/setWith.js b/test/setWith.js deleted file mode 100644 index 87cc2a2a10..0000000000 --- a/test/setWith.js +++ /dev/null @@ -1,19 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop } from './utils.js'; -import setWith from '../setWith.js'; - -describe('setWith', function() { - it('should work with a `customizer` callback', function() { - var actual = setWith({ '0': {} }, '[0][1][2]', 3, function(value) { - return lodashStable.isObject(value) ? undefined : {}; - }); - - assert.deepStrictEqual(actual, { '0': { '1': { '2': 3 } } }); - }); - - it('should work with a `customizer` that returns `undefined`', function() { - var actual = setWith({}, 'a[0].b.c', 4, noop); - assert.deepStrictEqual(actual, { 'a': [{ 'b': { 'c': 4 } }] }); - }); -}); diff --git a/test/setWith.spec.js b/test/setWith.spec.js new file mode 100644 index 0000000000..13e7a4b6d2 --- /dev/null +++ b/test/setWith.spec.js @@ -0,0 +1,18 @@ +import lodashStable from 'lodash'; +import { noop } from './utils'; +import setWith from '../src/setWith'; + +describe('setWith', () => { + it('should work with a `customizer` callback', () => { + const actual = setWith({ 0: {} }, '[0][1][2]', 3, (value) => + lodashStable.isObject(value) ? undefined : {}, + ); + + expect(actual).toEqual({ 0: { 1: { 2: 3 } } }); + }); + + it('should work with a `customizer` that returns `undefined`', () => { + const actual = setWith({}, 'a[0].b.c', 4, noop); + expect(actual).toEqual({ a: [{ b: { c: 4 } }] }); + }); +}); diff --git a/test/shuffle.js b/test/shuffle.js deleted file mode 100644 index fb86b89fdd..0000000000 --- a/test/shuffle.js +++ /dev/null @@ -1,29 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import shuffle from '../shuffle.js'; - -describe('shuffle', function() { - var array = [1, 2, 3], - object = { 'a': 1, 'b': 2, 'c': 3 }; - - it('should return a new array', function() { - assert.notStrictEqual(shuffle(array), array); - }); - - it('should contain the same elements after a collection is shuffled', function() { - assert.deepStrictEqual(shuffle(array).sort(), array); - assert.deepStrictEqual(shuffle(object).sort(), array); - }); - - it('should shuffle small collections', function() { - var actual = lodashStable.times(1000, function() { - return shuffle([1, 2]); - }); - - assert.deepStrictEqual(lodashStable.sortBy(lodashStable.uniqBy(actual, String), '0'), [[1, 2], [2, 1]]); - }); - - it('should treat number values for `collection` as empty', function() { - assert.deepStrictEqual(shuffle(1), []); - }); -}); diff --git a/test/shuffle.spec.js b/test/shuffle.spec.js new file mode 100644 index 0000000000..f0d3234632 --- /dev/null +++ b/test/shuffle.spec.js @@ -0,0 +1,29 @@ +import lodashStable from 'lodash'; +import shuffle from '../src/shuffle'; + +describe('shuffle', () => { + const array = [1, 2, 3]; + const object = { a: 1, b: 2, c: 3 }; + + it('should return a new array', () => { + expect(shuffle(array)).not.toBe(array); + }); + + it('should contain the same elements after a collection is shuffled', () => { + expect(shuffle(array).sort()).toEqual(array); + expect(shuffle(object).sort()).toEqual(array); + }); + + it('should shuffle small collections', () => { + const actual = lodashStable.times(1000, () => shuffle([1, 2])); + + expect(lodashStable.sortBy(lodashStable.uniqBy(actual, String), '0')).toEqual([ + [1, 2], + [2, 1], + ]); + }); + + it('should treat number values for `collection` as empty', () => { + expect(shuffle(1)).toEqual([]); + }); +}); diff --git a/test/size.spec.js b/test/size.spec.js new file mode 100644 index 0000000000..079059c7c8 --- /dev/null +++ b/test/size.spec.js @@ -0,0 +1,74 @@ +import lodashStable from 'lodash'; +import { falsey, stubZero, args, push, arrayProto, realm, MAX_SAFE_INTEGER } from './utils'; +import size from '../src/size'; + +describe('size', () => { + const array = [1, 2, 3]; + + it('should return the number of own enumerable string keyed properties of an object', () => { + expect(size({ one: 1, two: 2, three: 3 })).toBe(3); + }); + + it('should return the length of an array', () => { + expect(size(array)).toBe(3); + }); + + it('should accept a falsey `object`', () => { + const expected = lodashStable.map(falsey, stubZero); + + const actual = lodashStable.map(falsey, (object, index) => { + try { + return index ? size(object) : size(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should work with `arguments` objects', () => { + expect(size(args)).toBe(3); + }); + + it('should work with jQuery/MooTools DOM query collections', () => { + function Foo(elements) { + push.apply(this, elements); + } + Foo.prototype = { length: 0, splice: arrayProto.splice }; + + expect(size(new Foo(array))).toBe(3); + }); + + it('should work with maps', () => { + if (Map) { + lodashStable.each([new Map(), realm.map], (map) => { + map.set('a', 1); + map.set('b', 2); + expect(size(map)).toBe(2); + map.clear(); + }); + } + }); + + it('should work with sets', () => { + if (Set) { + lodashStable.each([new Set(), realm.set], (set) => { + set.add(1); + set.add(2); + expect(size(set)).toBe(2); + set.clear(); + }); + } + }); + + it('should not treat objects with negative lengths as array-like', () => { + expect(size({ length: -1 })).toBe(1); + }); + + it('should not treat objects with lengths larger than `MAX_SAFE_INTEGER` as array-like', () => { + expect(size({ length: MAX_SAFE_INTEGER + 1 })).toBe(1); + }); + + it('should not treat objects with non-number lengths as array-like', () => { + expect(size({ length: '0' })).toBe(1); + }); +}); diff --git a/test/size.test.js b/test/size.test.js deleted file mode 100644 index 286173453c..0000000000 --- a/test/size.test.js +++ /dev/null @@ -1,75 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubZero, args, push, arrayProto, realm, MAX_SAFE_INTEGER } from './utils.js'; -import size from '../size.js'; - -describe('size', function() { - var array = [1, 2, 3]; - - it('should return the number of own enumerable string keyed properties of an object', function() { - assert.strictEqual(size({ 'one': 1, 'two': 2, 'three': 3 }), 3); - }); - - it('should return the length of an array', function() { - assert.strictEqual(size(array), 3); - }); - - it('should accept a falsey `object`', function() { - var expected = lodashStable.map(falsey, stubZero); - - var actual = lodashStable.map(falsey, function(object, index) { - try { - return index ? size(object) : size(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `arguments` objects', function() { - assert.strictEqual(size(args), 3); - }); - - it('should work with jQuery/MooTools DOM query collections', function() { - function Foo(elements) { - push.apply(this, elements); - } - Foo.prototype = { 'length': 0, 'splice': arrayProto.splice }; - - assert.strictEqual(size(new Foo(array)), 3); - }); - - it('should work with maps', function() { - if (Map) { - lodashStable.each([new Map, realm.map], function(map) { - map.set('a', 1); - map.set('b', 2); - assert.strictEqual(size(map), 2); - map.clear(); - }); - } - }); - - it('should work with sets', function() { - if (Set) { - lodashStable.each([new Set, realm.set], function(set) { - set.add(1); - set.add(2); - assert.strictEqual(size(set), 2); - set.clear(); - }); - } - }); - - it('should not treat objects with negative lengths as array-like', function() { - assert.strictEqual(size({ 'length': -1 }), 1); - }); - - it('should not treat objects with lengths larger than `MAX_SAFE_INTEGER` as array-like', function() { - assert.strictEqual(size({ 'length': MAX_SAFE_INTEGER + 1 }), 1); - }); - - it('should not treat objects with non-number lengths as array-like', function() { - assert.strictEqual(size({ 'length': '0' }), 1); - }); -}); diff --git a/test/slice-and-toArray.js b/test/slice-and-toArray.js deleted file mode 100644 index 7c8607015d..0000000000 --- a/test/slice-and-toArray.js +++ /dev/null @@ -1,43 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, args, document, body } from './utils.js'; - -describe('slice and toArray', function() { - lodashStable.each(['slice', 'toArray'], function(methodName) { - var array = [1, 2, 3], - func = _[methodName]; - - it('`_.' + methodName + '` should return a dense array', function() { - var sparse = Array(3); - sparse[1] = 2; - - var actual = func(sparse); - - assert.ok('0' in actual); - assert.ok('2' in actual); - assert.deepStrictEqual(actual, sparse); - }); - - it('`_.' + methodName + '` should treat array-like objects like arrays', function() { - var object = { '0': 'a', 'length': 1 }; - assert.deepStrictEqual(func(object), ['a']); - assert.deepStrictEqual(func(args), array); - }); - - it('`_.' + methodName + '` should return a shallow clone of arrays', function() { - var actual = func(array); - assert.deepStrictEqual(actual, array); - assert.notStrictEqual(actual, array); - }); - - it('`_.' + methodName + '` should work with a node list for `collection`', function() { - if (document) { - try { - var actual = func(document.getElementsByTagName('body')); - } catch (e) {} - - assert.deepStrictEqual(actual, [body]); - } - }); - }); -}); diff --git a/test/slice-and-toArray.spec.js b/test/slice-and-toArray.spec.js new file mode 100644 index 0000000000..ff7303c361 --- /dev/null +++ b/test/slice-and-toArray.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import { _, args, document, body } from './utils'; + +describe('slice and toArray', () => { + lodashStable.each(['slice', 'toArray'], (methodName) => { + const array = [1, 2, 3]; + const func = _[methodName]; + + it(`\`_.${methodName}\` should return a dense array`, () => { + const sparse = Array(3); + sparse[1] = 2; + + const actual = func(sparse); + + expect('0' in actual); + expect('2' in actual); + expect(actual).toEqual(sparse); + }); + + it(`\`_.${methodName}\` should treat array-like objects like arrays`, () => { + const object = { 0: 'a', length: 1 }; + expect(func(object)).toEqual(['a']); + expect(func(args)).toEqual(array); + }); + + it(`\`_.${methodName}\` should return a shallow clone of arrays`, () => { + const actual = func(array); + expect(actual).toEqual(array); + expect(actual).not.toBe(array); + }); + + it(`\`_.${methodName}\` should work with a node list for \`collection\``, () => { + if (document) { + try { + var actual = func(document.getElementsByTagName('body')); + } catch (e) {} + + expect(actual).toEqual([body]); + } + }); + }); +}); diff --git a/test/slice.js b/test/slice.js deleted file mode 100644 index 1e47e1283e..0000000000 --- a/test/slice.js +++ /dev/null @@ -1,133 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, LARGE_ARRAY_SIZE } from './utils.js'; -import slice from '../slice.js'; - -describe('slice', function() { - var array = [1, 2, 3]; - - it('should use a default `start` of `0` and a default `end` of `length`', function() { - var actual = slice(array); - assert.deepStrictEqual(actual, array); - assert.notStrictEqual(actual, array); - }); - - it('should work with a positive `start`', function() { - assert.deepStrictEqual(slice(array, 1), [2, 3]); - assert.deepStrictEqual(slice(array, 1, 3), [2, 3]); - }); - - it('should work with a `start` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(start) { - assert.deepStrictEqual(slice(array, start), []); - }); - }); - - it('should treat falsey `start` values as `0`', function() { - var expected = lodashStable.map(falsey, lodashStable.constant(array)); - - var actual = lodashStable.map(falsey, function(start) { - return slice(array, start); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with a negative `start`', function() { - assert.deepStrictEqual(slice(array, -1), [3]); - }); - - it('should work with a negative `start` <= negative `length`', function() { - lodashStable.each([-3, -4, -Infinity], function(start) { - assert.deepStrictEqual(slice(array, start), array); - }); - }); - - it('should work with `start` >= `end`', function() { - lodashStable.each([2, 3], function(start) { - assert.deepStrictEqual(slice(array, start, 2), []); - }); - }); - - it('should work with a positive `end`', function() { - assert.deepStrictEqual(slice(array, 0, 1), [1]); - }); - - it('should work with a `end` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(end) { - assert.deepStrictEqual(slice(array, 0, end), array); - }); - }); - - it('should treat falsey `end` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? array : []; - }); - - var actual = lodashStable.map(falsey, function(end, index) { - return index ? slice(array, 0, end) : slice(array, 0); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with a negative `end`', function() { - assert.deepStrictEqual(slice(array, 0, -1), [1, 2]); - }); - - it('should work with a negative `end` <= negative `length`', function() { - lodashStable.each([-3, -4, -Infinity], function(end) { - assert.deepStrictEqual(slice(array, 0, end), []); - }); - }); - - it('should coerce `start` and `end` to integers', function() { - var positions = [[0.1, 1.6], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; - - var actual = lodashStable.map(positions, function(pos) { - return slice.apply(_, [array].concat(pos)); - }); - - assert.deepStrictEqual(actual, [[1], [1], [1], [2, 3], [1], []]); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1], [2, 3]], - actual = lodashStable.map(array, slice); - - assert.deepStrictEqual(actual, array); - assert.notStrictEqual(actual, array); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1), - length = array.length, - wrapped = _(array); - - lodashStable.each(['map', 'filter'], function(methodName) { - assert.deepEqual(wrapped[methodName]().slice(0, -1).value(), array.slice(0, -1)); - assert.deepEqual(wrapped[methodName]().slice(1).value(), array.slice(1)); - assert.deepEqual(wrapped[methodName]().slice(1, 3).value(), array.slice(1, 3)); - assert.deepEqual(wrapped[methodName]().slice(-1).value(), array.slice(-1)); - - assert.deepEqual(wrapped[methodName]().slice(length).value(), array.slice(length)); - assert.deepEqual(wrapped[methodName]().slice(3, 2).value(), array.slice(3, 2)); - assert.deepEqual(wrapped[methodName]().slice(0, -length).value(), array.slice(0, -length)); - assert.deepEqual(wrapped[methodName]().slice(0, null).value(), array.slice(0, null)); - - assert.deepEqual(wrapped[methodName]().slice(0, length).value(), array.slice(0, length)); - assert.deepEqual(wrapped[methodName]().slice(-length).value(), array.slice(-length)); - assert.deepEqual(wrapped[methodName]().slice(null).value(), array.slice(null)); - - assert.deepEqual(wrapped[methodName]().slice(0, 1).value(), array.slice(0, 1)); - assert.deepEqual(wrapped[methodName]().slice(NaN, '1').value(), array.slice(NaN, '1')); - - assert.deepEqual(wrapped[methodName]().slice(0.1, 1.1).value(), array.slice(0.1, 1.1)); - assert.deepEqual(wrapped[methodName]().slice('0', 1).value(), array.slice('0', 1)); - assert.deepEqual(wrapped[methodName]().slice(0, '1').value(), array.slice(0, '1')); - assert.deepEqual(wrapped[methodName]().slice('1').value(), array.slice('1')); - assert.deepEqual(wrapped[methodName]().slice(NaN, 1).value(), array.slice(NaN, 1)); - assert.deepEqual(wrapped[methodName]().slice(1, NaN).value(), array.slice(1, NaN)); - }); - }); -}); diff --git a/test/slice.spec.js b/test/slice.spec.js new file mode 100644 index 0000000000..de94995786 --- /dev/null +++ b/test/slice.spec.js @@ -0,0 +1,94 @@ +import lodashStable from 'lodash'; +import { falsey } from './utils'; +import slice from '../src/slice'; + +describe('slice', () => { + const array = [1, 2, 3]; + + it('should use a default `start` of `0` and a default `end` of `length`', () => { + const actual = slice(array); + expect(actual).toEqual(array); + expect(actual).not.toBe(array); + }); + + it('should work with a positive `start`', () => { + expect(slice(array, 1)).toEqual( [2, 3]); + expect(slice(array, 1, 3)).toEqual([2, 3]); + }); + + it('should work with a `start` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (start) => { + expect(slice(array, start)).toEqual([]); + }); + }); + + it('should treat falsey `start` values as `0`', () => { + const expected = lodashStable.map(falsey, lodashStable.constant(array)); + + const actual = lodashStable.map(falsey, (start) => slice(array, start)); + + expect(actual).toEqual(expected); + }); + + it('should work with a negative `start`', () => { + expect(slice(array, -1)).toEqual([3]); + }); + + it('should work with a negative `start` <= negative `length`', () => { + lodashStable.each([-3, -4, -Infinity], (start) => { + expect(slice(array, start)).toEqual(array); + }); + }); + + it('should work with `start` >= `end`', () => { + lodashStable.each([2, 3], (start) => { + expect(slice(array, start, 2)).toEqual([]); + }); + }); + + it('should work with a positive `end`', () => { + expect(slice(array, 0, 1)).toEqual([1]); + }); + + it('should work with a `end` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (end) => { + expect(slice(array, 0, end)).toEqual(array); + }); + }); + + it('should treat falsey `end` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => (value === undefined ? array : [])); + + const actual = lodashStable.map(falsey, (end, index) => + index ? slice(array, 0, end) : slice(array, 0), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with a negative `end`', () => { + expect(slice(array, 0, -1)).toEqual([1, 2]); + }); + + it('should work with a negative `end` <= negative `length`', () => { + lodashStable.each([-3, -4, -Infinity], (end) => { + expect(slice(array, 0, end)).toEqual([]); + }); + }); + + it('should coerce `start` and `end` to integers', () => { + const positions = [[0.1, 1.6], ['0', 1], [0, '1'], ['1'], [NaN, 1], [1, NaN]]; + + const actual = lodashStable.map(positions, (pos) => slice.apply(_, [array].concat(pos))); + + expect(actual).toEqual([[1], [1], [1], [2, 3], [1], []]); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [[1], [2, 3]]; + const actual = lodashStable.map(array, slice); + + expect(actual).toEqual(array); + expect(actual).not.toBe(array); + }); +}); diff --git a/test/some.js b/test/some.js deleted file mode 100644 index 5f93f41457..0000000000 --- a/test/some.js +++ /dev/null @@ -1,76 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, empties, stubFalse, stubTrue } from './utils.js'; -import some from '../some.js'; - -describe('some', function() { - it('should return `true` if `predicate` returns truthy for any element', function() { - assert.strictEqual(some([false, 1, ''], identity), true); - assert.strictEqual(some([null, 'a', 0], identity), true); - }); - - it('should return `false` for empty collections', function() { - var expected = lodashStable.map(empties, stubFalse); - - var actual = lodashStable.map(empties, function(value) { - try { - return some(value, identity); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return `true` as soon as `predicate` returns truthy', function() { - var count = 0; - - assert.strictEqual(some([null, true, null], function(value) { - count++; - return value; - }), true); - - assert.strictEqual(count, 2); - }); - - it('should return `false` if `predicate` returns falsey for all elements', function() { - assert.strictEqual(some([false, false, false], identity), false); - assert.strictEqual(some([null, 0, ''], identity), false); - }); - - it('should use `_.identity` when `predicate` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value, index) { - var array = [0, 0]; - return index ? some(array, value) : some(array); - }); - - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(values, stubTrue); - actual = lodashStable.map(values, function(value, index) { - var array = [0, 1]; - return index ? some(array, value) : some(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var objects = [{ 'a': 0, 'b': 0 }, { 'a': 0, 'b': 1 }]; - assert.strictEqual(some(objects, 'a'), false); - assert.strictEqual(some(objects, 'b'), true); - }); - - it('should work with `_.matches` shorthands', function() { - var objects = [{ 'a': 0, 'b': 0 }, { 'a': 1, 'b': 1}]; - assert.strictEqual(some(objects, { 'a': 0 }), true); - assert.strictEqual(some(objects, { 'b': 2 }), false); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var actual = lodashStable.map([[1]], some); - assert.deepStrictEqual(actual, [true]); - }); -}); diff --git a/test/some.spec.js b/test/some.spec.js new file mode 100644 index 0000000000..f714bdc500 --- /dev/null +++ b/test/some.spec.js @@ -0,0 +1,83 @@ +import lodashStable from 'lodash'; +import { identity, empties, stubFalse, stubTrue } from './utils'; +import some from '../src/some'; + +describe('some', () => { + it('should return `true` if `predicate` returns truthy for any element', () => { + expect(some([false, 1, ''], identity)).toBe(true); + expect(some([null, 'a', 0], identity)).toBe(true); + }); + + it('should return `false` for empty collections', () => { + const expected = lodashStable.map(empties, stubFalse); + + const actual = lodashStable.map(empties, (value) => { + try { + return some(value, identity); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should return `true` as soon as `predicate` returns truthy', () => { + let count = 0; + + expect( + some([null, true, null], (value) => { + count++; + return value; + }) + ); + + expect(count).toBe(2); + }); + + it('should return `false` if `predicate` returns falsey for all elements', () => { + expect(some([false, false, false], identity)).toBe(false); + expect(some([null, 0, ''], identity)).toBe(false); + }); + + it('should use `_.identity` when `predicate` is nullish', () => { + const values = [, null, undefined]; + let expected = lodashStable.map(values, stubFalse); + + let actual = lodashStable.map(values, (value, index) => { + const array = [0, 0]; + return index ? some(array, value) : some(array); + }); + + expect(actual).toEqual(expected); + + expected = lodashStable.map(values, stubTrue); + actual = lodashStable.map(values, (value, index) => { + const array = [0, 1]; + return index ? some(array, value) : some(array); + }); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const objects = [ + { a: 0, b: 0 }, + { a: 0, b: 1 }, + ]; + expect(some(objects, 'a')).toBe(false); + expect(some(objects, 'b')).toBe(true); + }); + + it('should work with `_.matches` shorthands', () => { + const objects = [ + { a: 0, b: 0 }, + { a: 1, b: 1 }, + ]; + expect(some(objects, { a: 0 })).toBe(true); + expect(some(objects, { b: 2 })).toBe(false); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const actual = lodashStable.map([[1]], some); + expect(actual).toEqual([true]); + }); +}); diff --git a/test/sortBy-methods.js b/test/sortBy-methods.js deleted file mode 100644 index fb4748d0ff..0000000000 --- a/test/sortBy-methods.js +++ /dev/null @@ -1,87 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('sortBy methods', function() { - lodashStable.each(['orderBy', 'sortBy'], function(methodName) { - var func = _[methodName]; - - function Pair(a, b, c) { - this.a = a; - this.b = b; - this.c = c; - } - - var objects = [ - { 'a': 'x', 'b': 3 }, - { 'a': 'y', 'b': 4 }, - { 'a': 'x', 'b': 1 }, - { 'a': 'y', 'b': 2 } - ]; - - var stableArray = [ - new Pair(1, 1, 1), new Pair(1, 2, 1), - new Pair(1, 1, 1), new Pair(1, 2, 1), - new Pair(1, 3, 1), new Pair(1, 4, 1), - new Pair(1, 5, 1), new Pair(1, 6, 1), - new Pair(2, 1, 2), new Pair(2, 2, 2), - new Pair(2, 3, 2), new Pair(2, 4, 2), - new Pair(2, 5, 2), new Pair(2, 6, 2), - new Pair(undefined, 1, 1), new Pair(undefined, 2, 1), - new Pair(undefined, 3, 1), new Pair(undefined, 4, 1), - new Pair(undefined, 5, 1), new Pair(undefined, 6, 1) - ]; - - var stableObject = lodashStable.zipObject('abcdefghijklmnopqrst'.split(''), stableArray); - - it('`_.' + methodName + '` should sort multiple properties in ascending order', function() { - var actual = func(objects, ['a', 'b']); - assert.deepStrictEqual(actual, [objects[2], objects[0], objects[3], objects[1]]); - }); - - it('`_.' + methodName + '` should support iteratees', function() { - var actual = func(objects, ['a', function(object) { return object.b; }]); - assert.deepStrictEqual(actual, [objects[2], objects[0], objects[3], objects[1]]); - }); - - it('`_.' + methodName + '` should perform a stable sort (test in IE > 8 and V8)', function() { - lodashStable.each([stableArray, stableObject], function(value, index) { - var actual = func(value, ['a', 'c']); - assert.deepStrictEqual(actual, stableArray, index ? 'object' : 'array'); - }); - }); - - it('`_.' + methodName + '` should not error on nullish elements', function() { - try { - var actual = func(objects.concat(null, undefined), ['a', 'b']); - } catch (e) {} - - assert.deepStrictEqual(actual, [objects[2], objects[0], objects[3], objects[1], null, undefined]); - }); - - it('`_.' + methodName + '` should work as an iteratee for methods like `_.reduce`', function() { - var objects = [ - { 'a': 'x', '0': 3 }, - { 'a': 'y', '0': 4 }, - { 'a': 'x', '0': 1 }, - { 'a': 'y', '0': 2 } - ]; - - var funcs = [func, lodashStable.partialRight(func, 'bogus')]; - - lodashStable.each(['a', 0, [0]], function(props, index) { - var expected = lodashStable.map(funcs, lodashStable.constant( - index - ? [objects[2], objects[3], objects[0], objects[1]] - : [objects[0], objects[2], objects[1], objects[3]] - )); - - var actual = lodashStable.map(funcs, function(func) { - return lodashStable.reduce([props], func, objects); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - }); -}); diff --git a/test/sortBy-methods.spec.js b/test/sortBy-methods.spec.js new file mode 100644 index 0000000000..85369b2566 --- /dev/null +++ b/test/sortBy-methods.spec.js @@ -0,0 +1,111 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('sortBy methods', () => { + lodashStable.each(['orderBy', 'sortBy'], (methodName) => { + const func = _[methodName]; + + function Pair(a, b, c) { + this.a = a; + this.b = b; + this.c = c; + } + + const objects = [ + { a: 'x', b: 3 }, + { a: 'y', b: 4 }, + { a: 'x', b: 1 }, + { a: 'y', b: 2 }, + ]; + + const stableArray = [ + new Pair(1, 1, 1), + new Pair(1, 2, 1), + new Pair(1, 1, 1), + new Pair(1, 2, 1), + new Pair(1, 3, 1), + new Pair(1, 4, 1), + new Pair(1, 5, 1), + new Pair(1, 6, 1), + new Pair(2, 1, 2), + new Pair(2, 2, 2), + new Pair(2, 3, 2), + new Pair(2, 4, 2), + new Pair(2, 5, 2), + new Pair(2, 6, 2), + new Pair(undefined, 1, 1), + new Pair(undefined, 2, 1), + new Pair(undefined, 3, 1), + new Pair(undefined, 4, 1), + new Pair(undefined, 5, 1), + new Pair(undefined, 6, 1), + ]; + + const stableObject = lodashStable.zipObject('abcdefghijklmnopqrst'.split(''), stableArray); + + it(`\`_.${methodName}\` should sort multiple properties in ascending order`, () => { + const actual = func(objects, ['a', 'b']); + expect(actual).toEqual([objects[2], objects[0], objects[3], objects[1]]); + }); + + it(`\`_.${methodName}\` should support iteratees`, () => { + const actual = func(objects, [ + 'a', + function (object) { + return object.b; + }, + ]); + expect(actual).toEqual([objects[2], objects[0], objects[3], objects[1]]); + }); + + it(`\`_.${methodName}\` should perform a stable sort (test in IE > 8 and V8)`, () => { + lodashStable.each([stableArray, stableObject], (value, index) => { + const actual = func(value, ['a', 'c']); + expect(actual, stableArray).toEqual(index ? 'object' : 'array'); + }); + }); + + it(`\`_.${methodName}\` should not error on nullish elements`, () => { + try { + var actual = func(objects.concat(null, undefined), ['a', 'b']); + } catch (e) {} + + expect(actual).toEqual([ + objects[2], + objects[0], + objects[3], + objects[1], + null, + undefined, + ]); + }); + + it(`\`_.${methodName}\` should work as an iteratee for methods like \`_.reduce\``, () => { + const objects = [ + { a: 'x', 0: 3 }, + { a: 'y', 0: 4 }, + { a: 'x', 0: 1 }, + { a: 'y', 0: 2 }, + ]; + + const funcs = [func, lodashStable.partialRight(func, 'bogus')]; + + lodashStable.each(['a', 0, [0]], (props, index) => { + const expected = lodashStable.map( + funcs, + lodashStable.constant( + index + ? [objects[2], objects[3], objects[0], objects[1]] + : [objects[0], objects[2], objects[1], objects[3]], + ), + ); + + const actual = lodashStable.map(funcs, (func) => + lodashStable.reduce([props], func, objects), + ); + + expect(actual).toEqual(expected); + }); + }); + }); +}); diff --git a/test/sortBy.js b/test/sortBy.js deleted file mode 100644 index 6cf7acc4ff..0000000000 --- a/test/sortBy.js +++ /dev/null @@ -1,75 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import sortBy from '../sortBy.js'; - -describe('sortBy', function() { - var objects = [ - { 'a': 'x', 'b': 3 }, - { 'a': 'y', 'b': 4 }, - { 'a': 'x', 'b': 1 }, - { 'a': 'y', 'b': 2 } - ]; - - it('should sort in ascending order by `iteratee`', function() { - var actual = lodashStable.map(sortBy(objects, function(object) { - return object.b; - }), 'b'); - - assert.deepStrictEqual(actual, [1, 2, 3, 4]); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var array = [3, 2, 1], - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([1, 2, 3])); - - var actual = lodashStable.map(values, function(value, index) { - return index ? sortBy(array, value) : sortBy(array); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `_.property` shorthands', function() { - var actual = lodashStable.map(sortBy(objects.concat(undefined), 'b'), 'b'); - assert.deepStrictEqual(actual, [1, 2, 3, 4, undefined]); - }); - - it('should work with an object for `collection`', function() { - var actual = sortBy({ 'a': 1, 'b': 2, 'c': 3 }, Math.sin); - assert.deepStrictEqual(actual, [3, 1, 2]); - }); - - it('should move `NaN`, nullish, and symbol values to the end', function() { - var symbol1 = Symbol ? Symbol('a') : null, - symbol2 = Symbol ? Symbol('b') : null, - array = [NaN, undefined, null, 4, symbol1, null, 1, symbol2, undefined, 3, NaN, 2], - expected = [1, 2, 3, 4, symbol1, symbol2, null, null, undefined, undefined, NaN, NaN]; - - assert.deepStrictEqual(sortBy(array), expected); - - array = [NaN, undefined, symbol1, null, 'd', null, 'a', symbol2, undefined, 'c', NaN, 'b']; - expected = ['a', 'b', 'c', 'd', symbol1, symbol2, null, null, undefined, undefined, NaN, NaN]; - - assert.deepStrictEqual(sortBy(array), expected); - }); - - it('should treat number values for `collection` as empty', function() { - assert.deepStrictEqual(sortBy(1), []); - }); - - it('should coerce arrays returned from `iteratee`', function() { - var actual = sortBy(objects, function(object) { - var result = [object.a, object.b]; - result.toString = function() { return String(this[0]); }; - return result; - }); - - assert.deepStrictEqual(actual, [objects[0], objects[2], objects[1], objects[3]]); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var actual = lodashStable.map([[2, 1, 3], [3, 2, 1]], sortBy); - assert.deepStrictEqual(actual, [[1, 2, 3], [1, 2, 3]]); - }); -}); diff --git a/test/sortBy.spec.js b/test/sortBy.spec.js new file mode 100644 index 0000000000..808f9f7aaa --- /dev/null +++ b/test/sortBy.spec.js @@ -0,0 +1,99 @@ +import lodashStable from 'lodash'; +import sortBy from '../src/sortBy'; + +describe('sortBy', () => { + const objects = [ + { a: 'x', b: 3 }, + { a: 'y', b: 4 }, + { a: 'x', b: 1 }, + { a: 'y', b: 2 }, + ]; + + it('should sort in ascending order by `iteratee`', () => { + const actual = lodashStable.map( + sortBy(objects, (object) => object.b), + 'b', + ); + + expect(actual).toEqual([1, 2, 3, 4]); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const array = [3, 2, 1]; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([1, 2, 3])); + + const actual = lodashStable.map(values, (value, index) => + index ? sortBy(array, value) : sortBy(array), + ); + + expect(actual).toEqual(expected); + }); + + it('should work with `_.property` shorthands', () => { + const actual = lodashStable.map(sortBy(objects.concat(undefined), 'b'), 'b'); + expect(actual).toEqual([1, 2, 3, 4, undefined]); + }); + + it('should work with an object for `collection`', () => { + const actual = sortBy({ a: 1, b: 2, c: 3 }, Math.sin); + expect(actual).toEqual([3, 1, 2]); + }); + + it('should move `NaN`, nullish, and symbol values to the end', () => { + const symbol1 = Symbol ? Symbol('a') : null; + const symbol2 = Symbol ? Symbol('b') : null; + let array = [NaN, undefined, null, 4, symbol1, null, 1, symbol2, undefined, 3, NaN, 2]; + let expected = [1, 2, 3, 4, symbol1, symbol2, null, null, undefined, undefined, NaN, NaN]; + + expect(sortBy(array)).toEqual(expected); + + array = [NaN, undefined, symbol1, null, 'd', null, 'a', symbol2, undefined, 'c', NaN, 'b']; + expected = [ + 'a', + 'b', + 'c', + 'd', + symbol1, + symbol2, + null, + null, + undefined, + undefined, + NaN, + NaN, + ]; + + expect(sortBy(array)).toEqual(expected); + }); + + it('should treat number values for `collection` as empty', () => { + expect(sortBy(1)).toEqual([]); + }); + + it('should coerce arrays returned from `iteratee`', () => { + const actual = sortBy(objects, (object) => { + const result = [object.a, object.b]; + result.toString = function () { + return String(this[0]); + }; + return result; + }); + + expect(actual).toEqual([objects[0], objects[2], objects[1], objects[3]]); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const actual = lodashStable.map( + [ + [2, 1, 3], + [3, 2, 1], + ], + sortBy, + ); + expect(actual).toEqual([ + [1, 2, 3], + [1, 2, 3], + ]); + }); +}); diff --git a/test/sortedIndex-methods.js b/test/sortedIndex-methods.js deleted file mode 100644 index 056918bc79..0000000000 --- a/test/sortedIndex-methods.js +++ /dev/null @@ -1,84 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; -import sortBy from '../sortBy.js'; - -describe('sortedIndex methods', function() { - lodashStable.each(['sortedIndex', 'sortedLastIndex'], function(methodName) { - var func = _[methodName], - isSortedIndex = methodName == 'sortedIndex'; - - it('`_.' + methodName + '` should return the insert index', function() { - var array = [30, 50], - values = [30, 40, 50], - expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2]; - - var actual = lodashStable.map(values, function(value) { - return func(array, value); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with an array of strings', function() { - var array = ['a', 'c'], - values = ['a', 'b', 'c'], - expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2]; - - var actual = lodashStable.map(values, function(value) { - return func(array, value); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should accept a nullish `array` and a `value`', function() { - var values = [null, undefined], - expected = lodashStable.map(values, lodashStable.constant([0, 0, 0])); - - var actual = lodashStable.map(values, function(array) { - return [func(array, 1), func(array, undefined), func(array, NaN)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should align with `_.sortBy`', function() { - var symbol1 = Symbol ? Symbol('a') : null, - symbol2 = Symbol ? Symbol('b') : null, - symbol3 = Symbol ? Symbol('c') : null, - expected = [1, '2', {}, symbol1, symbol2, null, undefined, NaN, NaN]; - - lodashStable.each([ - [NaN, symbol1, null, 1, '2', {}, symbol2, NaN, undefined], - ['2', null, 1, symbol1, NaN, {}, NaN, symbol2, undefined] - ], function(array) { - assert.deepStrictEqual(sortBy(array), expected); - assert.strictEqual(func(expected, 3), 2); - assert.strictEqual(func(expected, symbol3), isSortedIndex ? 3 : (Symbol ? 5 : 6)); - assert.strictEqual(func(expected, null), isSortedIndex ? (Symbol ? 5 : 3) : 6); - assert.strictEqual(func(expected, undefined), isSortedIndex ? 6 : 7); - assert.strictEqual(func(expected, NaN), isSortedIndex ? 7 : 9); - }); - }); - - it('`_.' + methodName + '` should align with `_.sortBy` for nulls', function() { - var array = [null, null]; - - assert.strictEqual(func(array, null), isSortedIndex ? 0 : 2); - assert.strictEqual(func(array, 1), 0); - assert.strictEqual(func(array, 'a'), 0); - }); - - it('`_.' + methodName + '` should align with `_.sortBy` for symbols', function() { - var symbol1 = Symbol ? Symbol('a') : null, - symbol2 = Symbol ? Symbol('b') : null, - symbol3 = Symbol ? Symbol('c') : null, - array = [symbol1, symbol2]; - - assert.strictEqual(func(array, symbol3), isSortedIndex ? 0 : 2); - assert.strictEqual(func(array, 1), 0); - assert.strictEqual(func(array, 'a'), 0); - }); - }); -}); diff --git a/test/sortedIndex-methods.spec.js b/test/sortedIndex-methods.spec.js new file mode 100644 index 0000000000..2e39418296 --- /dev/null +++ b/test/sortedIndex-methods.spec.js @@ -0,0 +1,84 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; +import sortBy from '../src/sortBy'; + +describe('sortedIndex methods', () => { + lodashStable.each(['sortedIndex', 'sortedLastIndex'], (methodName) => { + const func = _[methodName]; + const isSortedIndex = methodName === 'sortedIndex'; + + it(`\`_.${methodName}\` should return the insert index`, () => { + const array = [30, 50]; + const values = [30, 40, 50]; + const expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2]; + + const actual = lodashStable.map(values, (value) => func(array, value)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with an array of strings`, () => { + const array = ['a', 'c']; + const values = ['a', 'b', 'c']; + const expected = isSortedIndex ? [0, 1, 1] : [1, 1, 2]; + + const actual = lodashStable.map(values, (value) => func(array, value)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should accept a nullish \`array\` and a \`value\``, () => { + const values = [null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([0, 0, 0])); + + const actual = lodashStable.map(values, (array) => [ + func(array, 1), + func(array, undefined), + func(array, NaN), + ]); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should align with \`_.sortBy\``, () => { + const symbol1 = Symbol ? Symbol('a') : null; + const symbol2 = Symbol ? Symbol('b') : null; + const symbol3 = Symbol ? Symbol('c') : null; + const expected = [1, '2', {}, symbol1, symbol2, null, undefined, NaN, NaN]; + + lodashStable.each( + [ + [NaN, symbol1, null, 1, '2', {}, symbol2, NaN, undefined], + ['2', null, 1, symbol1, NaN, {}, NaN, symbol2, undefined], + ], + (array) => { + expect(sortBy(array)).toEqual(expected); + expect(func(expected, 3)).toBe(2); + expect(func(expected, symbol3)).toBe(isSortedIndex ? 3 : Symbol ? 5 : 6); + expect(func(expected, null)).toBe(isSortedIndex ? (Symbol ? 5 : 3) : 6); + expect(func(expected, undefined)).toBe(isSortedIndex ? 6 : 7); + expect(func(expected, NaN)).toBe(isSortedIndex ? 7 : 9); + }, + ); + }); + + it(`\`_.${methodName}\` should align with \`_.sortBy\` for nulls`, () => { + const array = [null, null]; + + expect(func(array, null)).toBe(isSortedIndex ? 0 : 2); + expect(func(array, 1)).toBe(0); + expect(func(array, 'a')).toBe(0); + }); + + it(`\`_.${methodName}\` should align with \`_.sortBy\` for symbols`, () => { + const symbol1 = Symbol ? Symbol('a') : null; + const symbol2 = Symbol ? Symbol('b') : null; + const symbol3 = Symbol ? Symbol('c') : null; + const array = [symbol1, symbol2]; + + expect(func(array, symbol3)).toBe(isSortedIndex ? 0 : 2); + expect(func(array, 1)).toBe(0); + expect(func(array, 'a')).toBe(0); + }); + }); +}); diff --git a/test/sortedIndexBy-methods.js b/test/sortedIndexBy-methods.js deleted file mode 100644 index ae3b5feaa7..0000000000 --- a/test/sortedIndexBy-methods.js +++ /dev/null @@ -1,59 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, slice, MAX_ARRAY_LENGTH, MAX_ARRAY_INDEX } from './utils.js'; - -describe('sortedIndexBy methods', function() { - lodashStable.each(['sortedIndexBy', 'sortedLastIndexBy'], function(methodName) { - var func = _[methodName], - isSortedIndexBy = methodName == 'sortedIndexBy'; - - it('`_.' + methodName + '` should provide correct `iteratee` arguments', function() { - var args; - - func([30, 50], 40, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [40]); - }); - - it('`_.' + methodName + '` should work with `_.property` shorthands', function() { - var objects = [{ 'x': 30 }, { 'x': 50 }], - actual = func(objects, { 'x': 40 }, 'x'); - - assert.strictEqual(actual, 1); - }); - - it('`_.' + methodName + '` should avoid calling iteratee when length is 0', function() { - var objects = [], - actual = func(objects, { 'x': 50 }, assert.fail); - - assert.strictEqual(actual, 0); - }); - - it('`_.' + methodName + '` should support arrays larger than `MAX_ARRAY_LENGTH / 2`', function() { - lodashStable.each([Math.ceil(MAX_ARRAY_LENGTH / 2), MAX_ARRAY_LENGTH], function(length) { - var array = [], - values = [MAX_ARRAY_LENGTH, NaN, undefined]; - - array.length = length; - - lodashStable.each(values, function(value) { - var steps = 0; - - var actual = func(array, value, function(value) { - steps++; - return value; - }); - - var expected = (isSortedIndexBy ? !lodashStable.isNaN(value) : lodashStable.isFinite(value)) - ? 0 - : Math.min(length, MAX_ARRAY_INDEX); - - assert.ok(steps == 32 || steps == 33); - assert.strictEqual(actual, expected); - }); - }); - }); - }); -}); diff --git a/test/sortedIndexBy-methods.spec.js b/test/sortedIndexBy-methods.spec.js new file mode 100644 index 0000000000..ae5ecdb077 --- /dev/null +++ b/test/sortedIndexBy-methods.spec.js @@ -0,0 +1,60 @@ +import lodashStable from 'lodash'; +import { _, slice, MAX_ARRAY_LENGTH, MAX_ARRAY_INDEX } from './utils'; + +describe('sortedIndexBy methods', () => { + lodashStable.each(['sortedIndexBy', 'sortedLastIndexBy'], (methodName) => { + const func = _[methodName]; + const isSortedIndexBy = methodName === 'sortedIndexBy'; + + it(`\`_.${methodName}\` should provide correct \`iteratee\` arguments`, () => { + let args; + + func([30, 50], 40, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([40]); + }); + + it(`\`_.${methodName}\` should work with \`_.property\` shorthands`, () => { + const objects = [{ x: 30 }, { x: 50 }]; + const actual = func(objects, { x: 40 }, 'x'); + + expect(actual).toBe(1); + }); + + it(`\`_.${methodName}\` should avoid calling iteratee when length is 0`, () => { + const objects = []; + const actual = func(objects, { x: 50 }, assert.fail); + + expect(actual).toBe(0); + }); + + it(`\`_.${methodName}\` should support arrays larger than \`MAX_ARRAY_LENGTH / 2\``, () => { + lodashStable.each([Math.ceil(MAX_ARRAY_LENGTH / 2), MAX_ARRAY_LENGTH], (length) => { + const array = []; + const values = [MAX_ARRAY_LENGTH, NaN, undefined]; + + array.length = length; + + lodashStable.each(values, (value) => { + let steps = 0; + + const actual = func(array, value, (value) => { + steps++; + return value; + }); + + const expected = ( + isSortedIndexBy ? !lodashStable.isNaN(value) : lodashStable.isFinite(value) + ) + ? 0 + : Math.min(length, MAX_ARRAY_INDEX); + + expect(steps === 32 || steps === 33); + expect(actual).toBe(expected); + }); + }); + }); + }); +}); diff --git a/test/sortedIndexOf-methods.js b/test/sortedIndexOf-methods.js deleted file mode 100644 index 14550c564a..0000000000 --- a/test/sortedIndexOf-methods.js +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('sortedIndexOf methods', function() { - lodashStable.each(['sortedIndexOf', 'sortedLastIndexOf'], function(methodName) { - var func = _[methodName], - isSortedIndexOf = methodName == 'sortedIndexOf'; - - it('`_.' + methodName + '` should perform a binary search', function() { - var sorted = [4, 4, 5, 5, 6, 6]; - assert.deepStrictEqual(func(sorted, 5), isSortedIndexOf ? 2 : 3); - }); - }); -}); diff --git a/test/sortedIndexOf-methods.spec.js b/test/sortedIndexOf-methods.spec.js new file mode 100644 index 0000000000..26e867b5cc --- /dev/null +++ b/test/sortedIndexOf-methods.spec.js @@ -0,0 +1,14 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('sortedIndexOf methods', () => { + lodashStable.each(['sortedIndexOf', 'sortedLastIndexOf'], (methodName) => { + const func = _[methodName]; + const isSortedIndexOf = methodName === 'sortedIndexOf'; + + it(`\`_.${methodName}\` should perform a binary search`, () => { + const sorted = [4, 4, 5, 5, 6, 6]; + expect(func(sorted, 5)).toEqual(isSortedIndexOf ? 2 : 3); + }); + }); +}); diff --git a/test/sortedUniq.spec.js b/test/sortedUniq.spec.js new file mode 100644 index 0000000000..f044972d07 --- /dev/null +++ b/test/sortedUniq.spec.js @@ -0,0 +1,19 @@ +import lodashStable from 'lodash'; +import sortedUniq from '../src/sortedUniq'; + +describe('sortedUniq', () => { + it('should return unique values of a sorted array', () => { + const expected = [1, 2, 3]; + + lodashStable.each( + [ + [1, 2, 3], + [1, 1, 2, 2, 3], + [1, 2, 3, 3, 3, 3, 3], + ], + (array) => { + expect(sortedUniq(array)).toEqual(expected); + }, + ); + }); +}); diff --git a/test/sortedUniq.test.js b/test/sortedUniq.test.js deleted file mode 100644 index fec499a66b..0000000000 --- a/test/sortedUniq.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import sortedUniq from '../sortedUniq.js'; - -describe('sortedUniq', function() { - it('should return unique values of a sorted array', function() { - var expected = [1, 2, 3]; - - lodashStable.each([[1, 2, 3], [1, 1, 2, 2, 3], [1, 2, 3, 3, 3, 3, 3]], function(array) { - assert.deepStrictEqual(sortedUniq(array), expected); - }); - }); -}); diff --git a/test/split.js b/test/split.js deleted file mode 100644 index cc8e05b7f1..0000000000 --- a/test/split.js +++ /dev/null @@ -1,35 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import split from '../split.js'; - -describe('split', function() { - it('should split a string by `separator`', function() { - var string = 'abcde'; - assert.deepStrictEqual(split(string, 'c'), ['ab', 'de']); - assert.deepStrictEqual(split(string, /[bd]/), ['a', 'c', 'e']); - assert.deepStrictEqual(split(string, '', 2), ['a', 'b']); - }); - - it('should return an array containing an empty string for empty values', function() { - var values = [, null, undefined, ''], - expected = lodashStable.map(values, lodashStable.constant([''])); - - var actual = lodashStable.map(values, function(value, index) { - return index ? split(value) : split(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var strings = ['abc', 'def', 'ghi'], - actual = lodashStable.map(strings, split); - - assert.deepStrictEqual(actual, [['abc'], ['def'], ['ghi']]); - }); - - it('should allow mixed string and array prototype methods', function() { - var wrapped = _('abc'); - assert.strictEqual(wrapped.split('b').join(','), 'a,c'); - }); -}); diff --git a/test/split.spec.js b/test/split.spec.js new file mode 100644 index 0000000000..4d30374cfd --- /dev/null +++ b/test/split.spec.js @@ -0,0 +1,32 @@ +import lodashStable from 'lodash'; +import split from '../src/split'; + +describe('split', () => { + it('should split a string by `separator`', () => { + const string = 'abcde'; + expect(split(string, 'c'), ['ab').toEqual('de']); + expect(split(string, /[bd]/), ['a', 'c').toEqual('e']); + expect(split(string, '', 2), ['a').toEqual('b']); + }); + + it('should return an array containing an empty string for empty values', () => { + const values = [, null, undefined, '']; + const expected = lodashStable.map(values, lodashStable.constant([''])); + + const actual = lodashStable.map(values, (value, index) => (index ? split(value) : split())); + + expect(actual).toEqual(expected); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const strings = ['abc', 'def', 'ghi']; + const actual = lodashStable.map(strings, split); + + expect(actual, [['abc'], ['def']).toEqual(['ghi']]); + }); + + it('should allow mixed string and array prototype methods', () => { + const wrapped = _('abc'); + expect(wrapped.split('b').join(','), 'a).toBe(c'); + }); +}); diff --git a/test/spread.js b/test/spread.js deleted file mode 100644 index 862221b933..0000000000 --- a/test/spread.js +++ /dev/null @@ -1,58 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, _, stubTrue, falsey } from './utils.js'; - -describe('spread', function() { - function fn(a, b, c) { - return slice.call(arguments); - } - - it('should spread arguments to `func`', function() { - var spread = _.spread(fn), - expected = [1, 2]; - - assert.deepStrictEqual(spread([1, 2]), expected); - assert.deepStrictEqual(spread([1, 2], 3), expected); - }); - - it('should accept a falsey `array`', function() { - var spread = _.spread(stubTrue), - expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(array, index) { - try { - return index ? spread(array) : spread(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with `start`', function() { - var spread = _.spread(fn, 1), - expected = [1, 2, 3]; - - assert.deepStrictEqual(spread(1, [2, 3]), expected); - assert.deepStrictEqual(spread(1, [2, 3], 4), expected); - }); - - it('should treat `start` as `0` for negative or `NaN` values', function() { - var values = [-1, NaN, 'a'], - expected = lodashStable.map(values, lodashStable.constant([1, 2])); - - var actual = lodashStable.map(values, function(value) { - var spread = _.spread(fn, value); - return spread([1, 2]); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should coerce `start` to an integer', function() { - var spread = _.spread(fn, 1.6), - expected = [1, 2, 3]; - - assert.deepStrictEqual(spread(1, [2, 3]), expected); - assert.deepStrictEqual(spread(1, [2, 3], 4), expected); - }); -}); diff --git a/test/spread.spec.js b/test/spread.spec.js new file mode 100644 index 0000000000..7a18015d7b --- /dev/null +++ b/test/spread.spec.js @@ -0,0 +1,57 @@ +import lodashStable from 'lodash'; +import { slice, _, stubTrue, falsey } from './utils'; + +describe('spread', () => { + function fn(a, b, c) { + return slice.call(arguments); + } + + it('should spread arguments to `func`', () => { + const spread = _.spread(fn); + const expected = [1, 2]; + + expect(spread([1, 2])).toEqual(expected); + expect(spread([1, 2], 3)).toEqual(expected); + }); + + it('should accept a falsey `array`', () => { + const spread = _.spread(stubTrue); + const expected = lodashStable.map(falsey, stubTrue); + + const actual = lodashStable.map(falsey, (array, index) => { + try { + return index ? spread(array) : spread(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should work with `start`', () => { + const spread = _.spread(fn, 1); + const expected = [1, 2, 3]; + + expect(spread(1, [2, 3])).toEqual(expected); + expect(spread(1, [2, 3], 4)).toEqual(expected); + }); + + it('should treat `start` as `0` for negative or `NaN` values', () => { + const values = [-1, NaN, 'a']; + const expected = lodashStable.map(values, lodashStable.constant([1, 2])); + + const actual = lodashStable.map(values, (value) => { + const spread = _.spread(fn, value); + return spread([1, 2]); + }); + + expect(actual).toEqual(expected); + }); + + it('should coerce `start` to an integer', () => { + const spread = _.spread(fn, 1.6); + const expected = [1, 2, 3]; + + expect(spread(1, [2, 3])).toEqual(expected); + expect(spread(1, [2, 3], 4)).toEqual(expected); + }); +}); diff --git a/test/startCase.spec.js b/test/startCase.spec.js new file mode 100644 index 0000000000..f85175c676 --- /dev/null +++ b/test/startCase.spec.js @@ -0,0 +1,9 @@ +import startCase from '../src/startCase'; + +describe('startCase', () => { + it('should uppercase only the first character of each word', () => { + expect(startCase('--foo-bar--')).toBe('Foo Bar'); + expect(startCase('fooBar')).toBe('Foo Bar'); + expect(startCase('__FOO_BAR__')).toBe('FOO BAR'); + }); +}); diff --git a/test/startCase.test.js b/test/startCase.test.js deleted file mode 100644 index f61d329d0f..0000000000 --- a/test/startCase.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import startCase from '../startCase.js'; - -describe('startCase', function() { - it('should uppercase only the first character of each word', function() { - assert.strictEqual(startCase('--foo-bar--'), 'Foo Bar'); - assert.strictEqual(startCase('fooBar'), 'Foo Bar'); - assert.strictEqual(startCase('__FOO_BAR__'), 'FOO BAR'); - }); -}); diff --git a/test/startsWith-and-endsWith.js b/test/startsWith-and-endsWith.js deleted file mode 100644 index 58dabfe94b..0000000000 --- a/test/startsWith-and-endsWith.js +++ /dev/null @@ -1,38 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, MAX_SAFE_INTEGER } from './utils.js'; - -describe('startsWith and endsWith', function() { - lodashStable.each(['startsWith', 'endsWith'], function(methodName) { - var func = _[methodName], - isStartsWith = methodName == 'startsWith'; - - var string = 'abc', - chr = isStartsWith ? 'a' : 'c'; - - it('`_.' + methodName + '` should coerce `string` to a string', function() { - assert.strictEqual(func(Object(string), chr), true); - assert.strictEqual(func({ 'toString': lodashStable.constant(string) }, chr), true); - }); - - it('`_.' + methodName + '` should coerce `target` to a string', function() { - assert.strictEqual(func(string, Object(chr)), true); - assert.strictEqual(func(string, { 'toString': lodashStable.constant(chr) }), true); - }); - - it('`_.' + methodName + '` should coerce `position` to a number', function() { - var position = isStartsWith ? 1 : 2; - - assert.strictEqual(func(string, 'b', Object(position)), true); - assert.strictEqual(func(string, 'b', { 'toString': lodashStable.constant(String(position)) }), true); - }); - - it('should return `true` when `target` is an empty string regardless of `position`', function() { - var positions = [-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, MAX_SAFE_INTEGER, Infinity]; - - assert.ok(lodashStable.every(positions, function(position) { - return func(string, '', position); - })); - }); - }); -}); diff --git a/test/startsWith-and-endsWith.spec.js b/test/startsWith-and-endsWith.spec.js new file mode 100644 index 0000000000..1489a20b61 --- /dev/null +++ b/test/startsWith-and-endsWith.spec.js @@ -0,0 +1,37 @@ +import lodashStable from 'lodash'; +import { _, MAX_SAFE_INTEGER } from './utils'; + +describe('startsWith and endsWith', () => { + lodashStable.each(['startsWith', 'endsWith'], (methodName) => { + const func = _[methodName]; + const isStartsWith = methodName === 'startsWith'; + + const string = 'abc'; + const chr = isStartsWith ? 'a' : 'c'; + + it(`\`_.${methodName}\` should coerce \`string\` to a string`, () => { + expect(func(Object(string), chr)).toBe(true); + expect(func({ toString: lodashStable.constant(string) }, chr)).toBe(true); + }); + + it(`\`_.${methodName}\` should coerce \`target\` to a string`, () => { + expect(func(string, Object(chr))).toBe(true); + expect(func(string, { toString: lodashStable.constant(chr) })).toBe(true); + }); + + it(`\`_.${methodName}\` should coerce \`position\` to a number`, () => { + const position = isStartsWith ? 1 : 2; + + expect(func(string, 'b', Object(position))).toBe(true); + expect( + func(string, 'b', { toString: lodashStable.constant(String(position)) }) + ).toBe(true); + }); + + it('should return `true` when `target` is an empty string regardless of `position`', () => { + const positions = [-Infinity, NaN, -3, -1, 0, 1, 2, 3, 5, MAX_SAFE_INTEGER, Infinity]; + + expect(lodashStable.every(positions, (position) => func(string, '', position))); + }); + }); +}); diff --git a/test/startsWith.js b/test/startsWith.js deleted file mode 100644 index d58905032c..0000000000 --- a/test/startsWith.js +++ /dev/null @@ -1,47 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { MAX_SAFE_INTEGER, falsey, stubTrue } from './utils.js'; -import startsWith from '../startsWith.js'; - -describe('startsWith', function() { - var string = 'abc'; - - it('should return `true` if a string starts with `target`', function() { - assert.strictEqual(startsWith(string, 'a'), true); - }); - - it('should return `false` if a string does not start with `target`', function() { - assert.strictEqual(startsWith(string, 'b'), false); - }); - - it('should work with a `position`', function() { - assert.strictEqual(startsWith(string, 'b', 1), true); - }); - - it('should work with `position` >= `length`', function() { - lodashStable.each([3, 5, MAX_SAFE_INTEGER, Infinity], function(position) { - assert.strictEqual(startsWith(string, 'a', position), false); - }); - }); - - it('should treat falsey `position` values as `0`', function() { - var expected = lodashStable.map(falsey, stubTrue); - - var actual = lodashStable.map(falsey, function(position) { - return startsWith(string, 'a', position); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should treat a negative `position` as `0`', function() { - lodashStable.each([-1, -3, -Infinity], function(position) { - assert.strictEqual(startsWith(string, 'a', position), true); - assert.strictEqual(startsWith(string, 'b', position), false); - }); - }); - - it('should coerce `position` to an integer', function() { - assert.strictEqual(startsWith(string, 'bc', 1.2), true); - }); -}); diff --git a/test/startsWith.spec.js b/test/startsWith.spec.js new file mode 100644 index 0000000000..17b98f19a4 --- /dev/null +++ b/test/startsWith.spec.js @@ -0,0 +1,44 @@ +import lodashStable from 'lodash'; +import { MAX_SAFE_INTEGER, falsey, stubTrue } from './utils'; +import startsWith from '../src/startsWith'; + +describe('startsWith', () => { + const string = 'abc'; + + it('should return `true` if a string starts with `target`', () => { + expect(startsWith(string, 'a')).toBe(true); + }); + + it('should return `false` if a string does not start with `target`', () => { + expect(startsWith(string, 'b')).toBe(false); + }); + + it('should work with a `position`', () => { + expect(startsWith(string, 'b', 1)).toBe(true); + }); + + it('should work with `position` >= `length`', () => { + lodashStable.each([3, 5, MAX_SAFE_INTEGER, Infinity], (position) => { + expect(startsWith(string, 'a', position)).toBe(false); + }); + }); + + it('should treat falsey `position` values as `0`', () => { + const expected = lodashStable.map(falsey, stubTrue); + + const actual = lodashStable.map(falsey, (position) => startsWith(string, 'a', position)); + + expect(actual).toEqual(expected); + }); + + it('should treat a negative `position` as `0`', () => { + lodashStable.each([-1, -3, -Infinity], (position) => { + expect(startsWith(string, 'a', position)).toBe(true); + expect(startsWith(string, 'b', position)).toBe(false); + }); + }); + + it('should coerce `position` to an integer', () => { + expect(startsWith(string, 'bc', 1.2)).toBe(true); + }); +}); diff --git a/test/strict-mode-checks.js b/test/strict-mode-checks.js deleted file mode 100644 index 3423fe1b88..0000000000 --- a/test/strict-mode-checks.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, isStrict, freeze } from './utils.js'; - -describe('strict mode checks', function() { - lodashStable.each(['assign', 'assignIn', 'bindAll', 'defaults', 'defaultsDeep', 'merge'], function(methodName) { - var func = _[methodName], - isBindAll = methodName == 'bindAll'; - - it('`_.' + methodName + '` should ' + (isStrict ? '' : 'not ') + 'throw strict mode errors', function() { - var object = freeze({ 'a': undefined, 'b': function() {} }), - pass = !isStrict; - - try { - func(object, isBindAll ? 'b' : { 'a': 1 }); - } catch (e) { - pass = !pass; - } - assert.ok(pass); - }); - }); -}); diff --git a/test/strict-mode-checks.spec.js b/test/strict-mode-checks.spec.js new file mode 100644 index 0000000000..1eab7bddd9 --- /dev/null +++ b/test/strict-mode-checks.spec.js @@ -0,0 +1,26 @@ +import lodashStable from 'lodash'; +import { _, isStrict, freeze } from './utils'; + +describe('strict mode checks', () => { + lodashStable.each( + ['assign', 'assignIn', 'bindAll', 'defaults', 'defaultsDeep', 'merge'], + (methodName) => { + const func = _[methodName]; + const isBindAll = methodName === 'bindAll'; + + it(`\`_.${methodName}\` should ${ + isStrict ? '' : 'not ' + }throw strict mode errors`, () => { + const object = freeze({ a: undefined, b: function () {} }); + let pass = !isStrict; + + try { + func(object, isBindAll ? 'b' : { a: 1 }); + } catch (e) { + pass = !pass; + } + expect(pass); + }); + }, + ); +}); diff --git a/test/stub-methods.js b/test/stub-methods.js deleted file mode 100644 index 1fb0e61b6d..0000000000 --- a/test/stub-methods.js +++ /dev/null @@ -1,32 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, empties } from './utils.js'; - -describe('stub methods', function() { - lodashStable.each(['noop', 'stubTrue', 'stubFalse', 'stubArray', 'stubObject', 'stubString'], function(methodName) { - var func = _[methodName]; - - var pair = ({ - 'stubArray': [[], 'an empty array'], - 'stubFalse': [false, '`false`'], - 'stubObject': [{}, 'an empty object'], - 'stubString': ['', 'an empty string'], - 'stubTrue': [true, '`true`'], - 'noop': [undefined, '`undefined`'] - })[methodName]; - - var values = Array(2).concat(empties, true, 1, 'a'), - expected = lodashStable.map(values, lodashStable.constant(pair[0])); - - it('`_.' + methodName + '` should return ' + pair[1], function() { - var actual = lodashStable.map(values, function(value, index) { - if (index < 2) { - return index ? func.call({}) : func(); - } - return func(value); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/stub-methods.spec.js b/test/stub-methods.spec.js new file mode 100644 index 0000000000..027b947cc0 --- /dev/null +++ b/test/stub-methods.spec.js @@ -0,0 +1,34 @@ +import lodashStable from 'lodash'; +import { _, empties } from './utils'; + +describe('stub methods', () => { + lodashStable.each( + ['noop', 'stubTrue', 'stubFalse', 'stubArray', 'stubObject', 'stubString'], + (methodName) => { + const func = _[methodName]; + + const pair = { + stubArray: [[], 'an empty array'], + stubFalse: [false, '`false`'], + stubObject: [{}, 'an empty object'], + stubString: ['', 'an empty string'], + stubTrue: [true, '`true`'], + noop: [undefined, '`undefined`'], + }[methodName]; + + const values = Array(2).concat(empties, true, 1, 'a'); + const expected = lodashStable.map(values, lodashStable.constant(pair[0])); + + it(`\`_.${methodName}\` should return ${pair[1]}`, () => { + const actual = lodashStable.map(values, (value, index) => { + if (index < 2) { + return index ? func.call({}) : func(); + } + return func(value); + }); + + expect(actual).toEqual(expected); + }); + }, + ); +}); diff --git a/test/subtract.spec.js b/test/subtract.spec.js new file mode 100644 index 0000000000..fbc764b5c1 --- /dev/null +++ b/test/subtract.spec.js @@ -0,0 +1,14 @@ +import subtract from '../src/subtract'; + +describe('subtract', () => { + it('should subtract two numbers', () => { + expect(subtract(6, 4)).toBe(2); + expect(subtract(-6, 4)).toBe(-10); + expect(subtract(-6, -4)).toBe(-2); + }); + + it('should coerce arguments to numbers', () => { + expect(subtract('6', '4')).toBe(2); + expect(subtract('x', 'y')).toEqual(NaN); + }); +}); diff --git a/test/subtract.test.js b/test/subtract.test.js deleted file mode 100644 index b148411ad3..0000000000 --- a/test/subtract.test.js +++ /dev/null @@ -1,15 +0,0 @@ -import assert from 'assert'; -import subtract from '../subtract.js'; - -describe('subtract', function() { - it('should subtract two numbers', function() { - assert.strictEqual(subtract(6, 4), 2); - assert.strictEqual(subtract(-6, 4), -10); - assert.strictEqual(subtract(-6, -4), -2); - }); - - it('should coerce arguments to numbers', function() { - assert.strictEqual(subtract('6', '4'), 2); - assert.deepStrictEqual(subtract('x', 'y'), NaN); - }); -}); diff --git a/test/sum-methods.js b/test/sum-methods.js deleted file mode 100644 index d536b9c50e..0000000000 --- a/test/sum-methods.js +++ /dev/null @@ -1,36 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, empties, stubZero } from './utils.js'; - -describe('sum methods', function() { - lodashStable.each(['sum', 'sumBy'], function(methodName) { - var array = [6, 4, 2], - func = _[methodName]; - - it('`_.' + methodName + '` should return the sum of an array of numbers', function() { - assert.strictEqual(func(array), 12); - }); - - it('`_.' + methodName + '` should return `0` when passing empty `array` values', function() { - var expected = lodashStable.map(empties, stubZero); - - var actual = lodashStable.map(empties, function(value) { - return func(value); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should skip `undefined` values', function() { - assert.strictEqual(func([1, undefined]), 1); - }); - - it('`_.' + methodName + '` should not skip `NaN` values', function() { - assert.deepStrictEqual(func([1, NaN]), NaN); - }); - - it('`_.' + methodName + '` should not coerce values to numbers', function() { - assert.strictEqual(func(['1', '2']), '12'); - }); - }); -}); diff --git a/test/sum-methods.spec.js b/test/sum-methods.spec.js new file mode 100644 index 0000000000..98250e3682 --- /dev/null +++ b/test/sum-methods.spec.js @@ -0,0 +1,33 @@ +import lodashStable from 'lodash'; +import { _, empties, stubZero } from './utils'; + +describe('sum methods', () => { + lodashStable.each(['sum', 'sumBy'], (methodName) => { + const array = [6, 4, 2]; + const func = _[methodName]; + + it(`\`_.${methodName}\` should return the sum of an array of numbers`, () => { + expect(func(array)).toBe(12); + }); + + it(`\`_.${methodName}\` should return \`0\` when passing empty \`array\` values`, () => { + const expected = lodashStable.map(empties, stubZero); + + const actual = lodashStable.map(empties, (value) => func(value)); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should skip \`undefined\` values`, () => { + expect(func([1, undefined])).toBe(1); + }); + + it(`\`_.${methodName}\` should not skip \`NaN\` values`, () => { + expect(func([1, NaN])).toEqual(NaN); + }); + + it(`\`_.${methodName}\` should not coerce values to numbers`, () => { + expect(func(['1', '2'])).toBe('12'); + }); + }); +}); diff --git a/test/sumBy.js b/test/sumBy.js deleted file mode 100644 index 62c780e58a..0000000000 --- a/test/sumBy.js +++ /dev/null @@ -1,32 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import sumBy from '../sumBy.js'; - -describe('sumBy', function() { - var array = [6, 4, 2], - objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }]; - - it('should work with an `iteratee`', function() { - var actual = sumBy(objects, function(object) { - return object.a; - }); - - assert.deepStrictEqual(actual, 6); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - sumBy(array, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [6]); - }); - - it('should work with `_.property` shorthands', function() { - var arrays = [[2], [3], [1]]; - assert.strictEqual(sumBy(arrays, 0), 6); - assert.strictEqual(sumBy(objects, 'a'), 6); - }); -}); diff --git a/test/sumBy.spec.js b/test/sumBy.spec.js new file mode 100644 index 0000000000..cff374de51 --- /dev/null +++ b/test/sumBy.spec.js @@ -0,0 +1,29 @@ +import { slice } from './utils'; +import sumBy from '../src/sumBy'; + +describe('sumBy', () => { + const array = [6, 4, 2]; + const objects = [{ a: 2 }, { a: 3 }, { a: 1 }]; + + it('should work with an `iteratee`', () => { + const actual = sumBy(objects, (object) => object.a); + + expect(actual).toEqual(6); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + sumBy(array, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([6]); + }); + + it('should work with `_.property` shorthands', () => { + const arrays = [[2], [3], [1]]; + expect(sumBy(arrays, 0)).toBe(6); + expect(sumBy(objects, 'a')).toBe(6); + }); +}); diff --git a/test/tail.js b/test/tail.js deleted file mode 100644 index 6fd325796f..0000000000 --- a/test/tail.js +++ /dev/null @@ -1,77 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, stubArray, LARGE_ARRAY_SIZE } from './utils.js'; -import tail from '../tail.js'; - -describe('tail', function() { - var array = [1, 2, 3]; - - it('should accept a falsey `array`', function() { - var expected = lodashStable.map(falsey, stubArray); - - var actual = lodashStable.map(falsey, function(array, index) { - try { - return index ? tail(array) : tail(); - } catch (e) {} - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should exclude the first element', function() { - assert.deepStrictEqual(tail(array), [2, 3]); - }); - - it('should return an empty when querying empty arrays', function() { - assert.deepStrictEqual(tail([]), []); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - actual = lodashStable.map(array, tail); - - assert.deepStrictEqual(actual, [[2, 3], [5, 6], [8, 9]]); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - values = []; - - var actual = _(array).tail().filter(function(value) { - values.push(value); - return false; - }) - .value(); - - assert.deepEqual(actual, []); - assert.deepEqual(values, array.slice(1)); - - values = []; - - actual = _(array).filter(function(value) { - values.push(value); - return isEven(value); - }) - .tail() - .value(); - - assert.deepEqual(actual, tail(_.filter(array, isEven))); - assert.deepEqual(values, array); - }); - - it('should not execute subsequent iteratees on an empty array in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - iteratee = function() { pass = false; }, - pass = true, - actual = _(array).slice(0, 1).tail().map(iteratee).value(); - - assert.ok(pass); - assert.deepEqual(actual, []); - - pass = true; - actual = _(array).filter().slice(0, 1).tail().map(iteratee).value(); - - assert.ok(pass); - assert.deepEqual(actual, []); - }); -}); diff --git a/test/tail.spec.js b/test/tail.spec.js new file mode 100644 index 0000000000..b6250949ab --- /dev/null +++ b/test/tail.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import { falsey, stubArray } from './utils'; +import tail from '../src/tail'; + +describe('tail', () => { + const array = [1, 2, 3]; + + it('should accept a falsey `array`', () => { + const expected = lodashStable.map(falsey, stubArray); + + const actual = lodashStable.map(falsey, (array, index) => { + try { + return index ? tail(array) : tail(); + } catch (e) {} + }); + + expect(actual).toEqual(expected); + }); + + it('should exclude the first element', () => { + expect(tail(array)).toEqual([2, 3]); + }); + + it('should return an empty when querying empty arrays', () => { + expect(tail([])).toEqual([]); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]; + const actual = lodashStable.map(array, tail); + + expect(actual).toEqual([ + [2, 3], + [5, 6], + [8, 9], + ]); + }); +}); diff --git a/test/take.js b/test/take.js deleted file mode 100644 index 76690a58b6..0000000000 --- a/test/take.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import take from '../take.js'; - -describe('take', function() { - var array = [1, 2, 3]; - - it('should take the first two elements', function() { - assert.deepStrictEqual(take(array, 2), [1, 2]); - }); - - it('should treat falsey `n` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? [1] : []; - }); - - var actual = lodashStable.map(falsey, function(n) { - return take(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an empty array when `n` < `1`', function() { - lodashStable.each([0, -1, -Infinity], function(n) { - assert.deepStrictEqual(take(array, n), []); - }); - }); - - it('should return all elements when `n` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(n) { - assert.deepStrictEqual(take(array, n), array); - }); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - actual = lodashStable.map(array, take); - - assert.deepStrictEqual(actual, [[1], [4], [7]]); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(1, LARGE_ARRAY_SIZE + 1), - predicate = function(value) { values.push(value); return isEven(value); }, - values = [], - actual = _(array).take(2).take().value(); - - assert.deepEqual(actual, take(take(array, 2))); - - actual = _(array).filter(predicate).take(2).take().value(); - assert.deepEqual(values, [1, 2]); - assert.deepEqual(actual, take(take(_.filter(array, predicate), 2))); - - actual = _(array).take(6).takeRight(4).take(2).takeRight().value(); - assert.deepEqual(actual, _.takeRight(take(_.takeRight(take(array, 6), 4), 2))); - - values = []; - - actual = _(array).take(array.length - 1).filter(predicate).take(6).takeRight(4).take(2).takeRight().value(); - assert.deepEqual(values, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]); - assert.deepEqual(actual, _.takeRight(take(_.takeRight(take(_.filter(take(array, array.length - 1), predicate), 6), 4), 2))); - }); -}); diff --git a/test/take.spec.js b/test/take.spec.js new file mode 100644 index 0000000000..0b1fc6bf14 --- /dev/null +++ b/test/take.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import { _, falsey, LARGE_ARRAY_SIZE, isEven } from './utils'; +import take from '../src/take'; + +describe('take', () => { + const array = [1, 2, 3]; + + it('should take the first two elements', () => { + expect(take(array, 2)).toEqual([1, 2]); + }); + + it('should treat falsey `n` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => (value === undefined ? [1] : [])); + + const actual = lodashStable.map(falsey, (n) => take(array, n)); + + expect(actual).toEqual(expected); + }); + + it('should return an empty array when `n` < `1`', () => { + lodashStable.each([0, -1, -Infinity], (n) => { + expect(take(array, n)).toEqual([]); + }); + }); + + it('should return all elements when `n` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (n) => { + expect(take(array, n)).toEqual(array); + }); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]; + const actual = lodashStable.map(array, take); + + expect(actual).toEqual([[1], [4], [7]]); + }); +}); diff --git a/test/takeRight.js b/test/takeRight.js deleted file mode 100644 index ee48abc804..0000000000 --- a/test/takeRight.js +++ /dev/null @@ -1,65 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { falsey, LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import takeRight from '../takeRight.js'; - -describe('takeRight', function() { - var array = [1, 2, 3]; - - it('should take the last two elements', function() { - assert.deepStrictEqual(takeRight(array, 2), [2, 3]); - }); - - it('should treat falsey `n` values, except `undefined`, as `0`', function() { - var expected = lodashStable.map(falsey, function(value) { - return value === undefined ? [3] : []; - }); - - var actual = lodashStable.map(falsey, function(n) { - return takeRight(array, n); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an empty array when `n` < `1`', function() { - lodashStable.each([0, -1, -Infinity], function(n) { - assert.deepStrictEqual(takeRight(array, n), []); - }); - }); - - it('should return all elements when `n` >= `length`', function() { - lodashStable.each([3, 4, Math.pow(2, 32), Infinity], function(n) { - assert.deepStrictEqual(takeRight(array, n), array); - }); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = [[1, 2, 3], [4, 5, 6], [7, 8, 9]], - actual = lodashStable.map(array, takeRight); - - assert.deepStrictEqual(actual, [[3], [6], [9]]); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - predicate = function(value) { values.push(value); return isEven(value); }, - values = [], - actual = _(array).takeRight(2).takeRight().value(); - - assert.deepEqual(actual, takeRight(takeRight(array))); - - actual = _(array).filter(predicate).takeRight(2).takeRight().value(); - assert.deepEqual(values, array); - assert.deepEqual(actual, takeRight(takeRight(_.filter(array, predicate), 2))); - - actual = _(array).takeRight(6).take(4).takeRight(2).take().value(); - assert.deepEqual(actual, _.take(takeRight(_.take(takeRight(array, 6), 4), 2))); - - values = []; - - actual = _(array).filter(predicate).takeRight(6).take(4).takeRight(2).take().value(); - assert.deepEqual(values, array); - assert.deepEqual(actual, _.take(takeRight(_.take(takeRight(_.filter(array, predicate), 6), 4), 2))); - }); -}); diff --git a/test/takeRight.spec.js b/test/takeRight.spec.js new file mode 100644 index 0000000000..d8de33844d --- /dev/null +++ b/test/takeRight.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import { falsey, LARGE_ARRAY_SIZE, isEven } from './utils'; +import takeRight from '../src/takeRight'; + +describe('takeRight', () => { + const array = [1, 2, 3]; + + it('should take the last two elements', () => { + expect(takeRight(array, 2)).toEqual([2, 3]); + }); + + it('should treat falsey `n` values, except `undefined`, as `0`', () => { + const expected = lodashStable.map(falsey, (value) => (value === undefined ? [3] : [])); + + const actual = lodashStable.map(falsey, (n) => takeRight(array, n)); + + expect(actual).toEqual(expected); + }); + + it('should return an empty array when `n` < `1`', () => { + lodashStable.each([0, -1, -Infinity], (n) => { + expect(takeRight(array, n)).toEqual([]); + }); + }); + + it('should return all elements when `n` >= `length`', () => { + lodashStable.each([3, 4, 2 ** 32, Infinity], (n) => { + expect(takeRight(array, n)).toEqual(array); + }); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = [ + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + ]; + const actual = lodashStable.map(array, takeRight); + + expect(actual).toEqual([[3], [6], [9]]); + }); +}); diff --git a/test/takeRightWhile.js b/test/takeRightWhile.js deleted file mode 100644 index d080e0c605..0000000000 --- a/test/takeRightWhile.js +++ /dev/null @@ -1,96 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, LARGE_ARRAY_SIZE } from './utils.js'; -import takeRightWhile from '../takeRightWhile.js'; - -describe('takeRightWhile', function() { - var array = [1, 2, 3, 4]; - - var objects = [ - { 'a': 0, 'b': 0 }, - { 'a': 1, 'b': 1 }, - { 'a': 2, 'b': 2 } - ]; - - it('should take elements while `predicate` returns truthy', function() { - var actual = takeRightWhile(array, function(n) { - return n > 2; - }); - - assert.deepStrictEqual(actual, [3, 4]); - }); - - it('should provide correct `predicate` arguments', function() { - var args; - - takeRightWhile(array, function() { - args = slice.call(arguments); - }); - - assert.deepStrictEqual(args, [4, 3, array]); - }); - - it('should work with `_.matches` shorthands', function() { - assert.deepStrictEqual(takeRightWhile(objects, { 'b': 2 }), objects.slice(2)); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - assert.deepStrictEqual(takeRightWhile(objects, ['b', 2]), objects.slice(2)); - }); - - it('should work with `_.property` shorthands', function() { - assert.deepStrictEqual(takeRightWhile(objects, 'b'), objects.slice(1)); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - predicate = function(n) { return n > 2; }, - expected = takeRightWhile(array, predicate), - wrapped = _(array).takeRightWhile(predicate); - - assert.deepEqual(wrapped.value(), expected); - assert.deepEqual(wrapped.reverse().value(), expected.slice().reverse()); - assert.strictEqual(wrapped.last(), _.last(expected)); - }); - - it('should provide correct `predicate` arguments in a lazy sequence', function() { - var args, - array = lodashStable.range(LARGE_ARRAY_SIZE + 1); - - var expected = [ - square(LARGE_ARRAY_SIZE), - LARGE_ARRAY_SIZE - 1, - lodashStable.map(array.slice(1), square) - ]; - - _(array).slice(1).takeRightWhile(function(value, index, array) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, [LARGE_ARRAY_SIZE, LARGE_ARRAY_SIZE - 1, array.slice(1)]); - - _(array).slice(1).map(square).takeRightWhile(function(value, index, array) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, expected); - - _(array).slice(1).map(square).takeRightWhile(function(value, index) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, expected); - - _(array).slice(1).map(square).takeRightWhile(function(index) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, [square(LARGE_ARRAY_SIZE)]); - - _(array).slice(1).map(square).takeRightWhile(function() { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, expected); - }); -}); diff --git a/test/takeRightWhile.spec.js b/test/takeRightWhile.spec.js new file mode 100644 index 0000000000..7d44be5352 --- /dev/null +++ b/test/takeRightWhile.spec.js @@ -0,0 +1,42 @@ +import { slice } from './utils'; +import takeRightWhile from '../src/takeRightWhile'; + +describe('takeRightWhile', () => { + const array = [1, 2, 3, 4]; + + // const objects = [ + // { a: 0, b: 0 }, + // { a: 1, b: 1 }, + // { a: 2, b: 2 }, + // ]; + + it('should take elements while `predicate` returns truthy', () => { + const actual = takeRightWhile(array, (n) => n > 2); + + expect(actual).toEqual([3, 4]); + }); + + it('should provide correct `predicate` arguments', () => { + let args; + + takeRightWhile(array, function () { + args = slice.call(arguments); + }); + + expect(args).toEqual([4, 3, array]); + }); + + // FIXME: Perhaps takeRightWhile semantic changes. + // + // it('should work with `_.matches` shorthands', () => { + // expect(takeRightWhile(objects, { b: 2 })).toEqual(objects.slice(2)); + // }); + // + // it('should work with `_.matchesProperty` shorthands', () => { + // expect(takeRightWhile(objects, ['b', 2])).toEqual(objects.slice(2)); + // }); + // + // it('should work with `_.property` shorthands', () => { + // expect(takeRightWhile(objects, 'b')).toEqual(objects.slice(1)); + // }); +}); diff --git a/test/takeWhile.js b/test/takeWhile.js deleted file mode 100644 index 43d1b10d9c..0000000000 --- a/test/takeWhile.js +++ /dev/null @@ -1,102 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, LARGE_ARRAY_SIZE, square } from './utils.js'; -import takeWhile from '../takeWhile.js'; - -describe('takeWhile', function() { - var array = [1, 2, 3, 4]; - - var objects = [ - { 'a': 2, 'b': 2 }, - { 'a': 1, 'b': 1 }, - { 'a': 0, 'b': 0 } - ]; - - it('should take elements while `predicate` returns truthy', function() { - var actual = takeWhile(array, function(n) { - return n < 3; - }); - - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('should provide correct `predicate` arguments', function() { - var args; - - takeWhile(array, function() { - args = slice.call(arguments); - }); - - assert.deepStrictEqual(args, [1, 0, array]); - }); - - it('should work with `_.matches` shorthands', function() { - assert.deepStrictEqual(takeWhile(objects, { 'b': 2 }), objects.slice(0, 1)); - }); - - it('should work with `_.matchesProperty` shorthands', function() { - assert.deepStrictEqual(takeWhile(objects, ['b', 2]), objects.slice(0, 1)); - }); - it('should work with `_.property` shorthands', function() { - assert.deepStrictEqual(takeWhile(objects, 'b'), objects.slice(0, 2)); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE), - predicate = function(n) { return n < 3; }, - expected = takeWhile(array, predicate), - wrapped = _(array).takeWhile(predicate); - - assert.deepEqual(wrapped.value(), expected); - assert.deepEqual(wrapped.reverse().value(), expected.slice().reverse()); - assert.strictEqual(wrapped.last(), _.last(expected)); - }); - - it('should work in a lazy sequence with `take`', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE); - - var actual = _(array) - .takeWhile(function(n) { return n < 4; }) - .take(2) - .takeWhile(function(n) { return n == 0; }) - .value(); - - assert.deepEqual(actual, [0]); - }); - - it('should provide correct `predicate` arguments in a lazy sequence', function() { - var args, - array = lodashStable.range(LARGE_ARRAY_SIZE + 1), - expected = [1, 0, lodashStable.map(array.slice(1), square)]; - - _(array).slice(1).takeWhile(function(value, index, array) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, [1, 0, array.slice(1)]); - - _(array).slice(1).map(square).takeWhile(function(value, index, array) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, expected); - - _(array).slice(1).map(square).takeWhile(function(value, index) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, expected); - - _(array).slice(1).map(square).takeWhile(function(value) { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, [1]); - - _(array).slice(1).map(square).takeWhile(function() { - args = slice.call(arguments); - }).value(); - - assert.deepEqual(args, expected); - }); -}); diff --git a/test/takeWhile.spec.js b/test/takeWhile.spec.js new file mode 100644 index 0000000000..91e881c04d --- /dev/null +++ b/test/takeWhile.spec.js @@ -0,0 +1,41 @@ +import { slice } from './utils'; +import takeWhile from '../src/takeWhile'; + +describe('takeWhile', () => { + const array = [1, 2, 3, 4]; + + // const objects = [ + // { a: 2, b: 2 }, + // { a: 1, b: 1 }, + // { a: 0, b: 0 }, + // ]; + + it('should take elements while `predicate` returns truthy', () => { + const actual = takeWhile(array, (n) => n < 3); + + expect(actual).toEqual([1, 2]); + }); + + it('should provide correct `predicate` arguments', () => { + let args; + + takeWhile(array, function () { + args = slice.call(arguments); + }); + + expect(args).toEqual([1, 0, array]); + }); + + // FIXME: Perhaps takeWhile semantic changes. + // + // it('should work with `_.matches` shorthands', () => { + // expect(takeWhile(objects, { b: 2 }), objects.slice(0).toEqual(1)); + // }); + // + // it('should work with `_.matchesProperty` shorthands', () => { + // expect(takeWhile(objects, ['b', 2]), objects.slice(0).toEqual(1)); + // }); + // it('should work with `_.property` shorthands', () => { + // expect(takeWhile(objects, 'b'), objects.slice(0).toEqual(2)); + // }); +}); diff --git a/test/tap.js b/test/tap.js deleted file mode 100644 index d50fa573ca..0000000000 --- a/test/tap.js +++ /dev/null @@ -1,30 +0,0 @@ -import assert from 'assert'; - -describe('tap', function() { - it('should intercept and return the given value', function() { - var intercepted, - array = [1, 2, 3]; - - var actual = _.tap(array, function(value) { - intercepted = value; - }); - - assert.strictEqual(actual, array); - assert.strictEqual(intercepted, array); - }); - - it('should intercept unwrapped values and return wrapped values when chaining', function() { - var intercepted, - array = [1, 2, 3]; - - var wrapped = _(array).tap(function(value) { - intercepted = value; - value.pop(); - }); - - assert.ok(wrapped instanceof _); - - wrapped.value(); - assert.strictEqual(intercepted, array); - }); -}); diff --git a/test/tap.spec.js b/test/tap.spec.js new file mode 100644 index 0000000000..c0d0755b3e --- /dev/null +++ b/test/tap.spec.js @@ -0,0 +1,28 @@ +describe('tap', () => { + it('should intercept and return the given value', () => { + let intercepted; + const array = [1, 2, 3]; + + const actual = _.tap(array, (value) => { + intercepted = value; + }); + + expect(actual).toBe(array); + expect(intercepted).toBe(array); + }); + + it('should intercept unwrapped values and return wrapped values when chaining', () => { + let intercepted; + const array = [1, 2, 3]; + + const wrapped = _(array).tap((value) => { + intercepted = value; + value.pop(); + }); + + expect(wrapped instanceof _); + + wrapped.value(); + expect(intercepted).toBe(array); + }); +}); diff --git a/test/template.js b/test/template.js deleted file mode 100644 index 01308dbce0..0000000000 --- a/test/template.js +++ /dev/null @@ -1,451 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { numberTag, stubString, stubTrue, stubFalse } from './utils.js'; -import template from '../template.js'; -import templateSettings from '../templateSettings.js'; - -describe('template', function() { - it('should escape values in "escape" delimiters', function() { - var strings = ['

<%- value %>

', '

<%-value%>

', '

<%-\nvalue\n%>

'], - expected = lodashStable.map(strings, lodashStable.constant('

&<>"'/

')), - data = { 'value': '&<>"\'/' }; - - var actual = lodashStable.map(strings, function(string) { - return template(string)(data); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should not reference `_.escape` when "escape" delimiters are not used', function() { - var compiled = template('<%= typeof __e %>'); - assert.strictEqual(compiled({}), 'undefined'); - }); - - it('should evaluate JavaScript in "evaluate" delimiters', function() { - var compiled = template( - '' - ); - - var data = { 'collection': { 'a': 'A', 'b': 'B' } }, - actual = compiled(data); - - assert.strictEqual(actual, ''); - }); - - it('should support "evaluate" delimiters with single line comments (test production builds)', function() { - var compiled = template('<% // A code comment. %><% if (value) { %>yap<% } else { %>nope<% } %>'), - data = { 'value': true }; - - assert.strictEqual(compiled(data), 'yap'); - }); - - it('should support referencing variables declared in "evaluate" delimiters from other delimiters', function() { - var compiled = template('<% var b = a; %><%= b.value %>'), - data = { 'a': { 'value': 1 } }; - - assert.strictEqual(compiled(data), '1'); - }); - - it('should interpolate data properties in "interpolate" delimiters', function() { - var strings = ['<%= a %>BC', '<%=a%>BC', '<%=\na\n%>BC'], - expected = lodashStable.map(strings, lodashStable.constant('ABC')), - data = { 'a': 'A' }; - - var actual = lodashStable.map(strings, function(string) { - return template(string)(data); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should support "interpolate" delimiters with escaped values', function() { - var compiled = template('<%= a ? "a=\\"A\\"" : "" %>'), - data = { 'a': true }; - - assert.strictEqual(compiled(data), 'a="A"'); - }); - - it('should support "interpolate" delimiters containing ternary operators', function() { - var compiled = template('<%= value ? value : "b" %>'), - data = { 'value': 'a' }; - - assert.strictEqual(compiled(data), 'a'); - }); - - it('should support "interpolate" delimiters containing global values', function() { - var compiled = template('<%= typeof Math.abs %>'); - - try { - var actual = compiled(); - } catch (e) {} - - assert.strictEqual(actual, 'function'); - }); - - it('should support complex "interpolate" delimiters', function() { - lodashStable.forOwn({ - '<%= a + b %>': '3', - '<%= b - a %>': '1', - '<%= a = b %>': '2', - '<%= !a %>': 'false', - '<%= ~a %>': '-2', - '<%= a * b %>': '2', - '<%= a / b %>': '0.5', - '<%= a % b %>': '1', - '<%= a >> b %>': '0', - '<%= a << b %>': '4', - '<%= a & b %>': '0', - '<%= a ^ b %>': '3', - '<%= a | b %>': '3', - '<%= {}.toString.call(0) %>': numberTag, - '<%= a.toFixed(2) %>': '1.00', - '<%= obj["a"] %>': '1', - '<%= delete a %>': 'true', - '<%= "a" in obj %>': 'true', - '<%= obj instanceof Object %>': 'true', - '<%= new Boolean %>': 'false', - '<%= typeof a %>': 'number', - '<%= void a %>': '' - }, - function(value, key) { - var compiled = template(key), - data = { 'a': 1, 'b': 2 }; - - assert.strictEqual(compiled(data), value, key); - }); - }); - - it('should support ES6 template delimiters', function() { - var data = { 'value': 2 }; - assert.strictEqual(template('1${value}3')(data), '123'); - assert.strictEqual(template('${"{" + value + "\\}"}')(data), '{2}'); - }); - - it('should support the "imports" option', function() { - var compiled = template('<%= a %>', { 'imports': { 'a': 1 } }); - assert.strictEqual(compiled({}), '1'); - }); - - it('should support the "variable" options', function() { - var compiled = template( - '<% _.each( data.a, function( value ) { %>' + - '<%= value.valueOf() %>' + - '<% }) %>', { 'variable': 'data' } - ); - - var data = { 'a': [1, 2, 3] }; - - try { - assert.strictEqual(compiled(data), '123'); - } catch (e) { - assert.ok(false, e.message); - } - }); - - it('should support custom delimiters', function() { - lodashStable.times(2, function(index) { - var settingsClone = lodashStable.clone(templateSettings); - - var settings = lodashStable.assign(index ? templateSettings : {}, { - 'escape': /\{\{-([\s\S]+?)\}\}/g, - 'evaluate': /\{\{([\s\S]+?)\}\}/g, - 'interpolate': /\{\{=([\s\S]+?)\}\}/g - }); - - var expected = '', - compiled = template('', index ? null : settings), - data = { 'collection': ['a & A', 'b & B'] }; - - assert.strictEqual(compiled(data), expected); - lodashStable.assign(templateSettings, settingsClone); - }); - }); - - it('should support custom delimiters containing special characters', function() { - lodashStable.times(2, function(index) { - var settingsClone = lodashStable.clone(templateSettings); - - var settings = lodashStable.assign(index ? templateSettings : {}, { - 'escape': /<\?-([\s\S]+?)\?>/g, - 'evaluate': /<\?([\s\S]+?)\?>/g, - 'interpolate': /<\?=([\s\S]+?)\?>/g - }); - - var expected = '', - compiled = template('', index ? null : settings), - data = { 'collection': ['a & A', 'b & B'] }; - - assert.strictEqual(compiled(data), expected); - lodashStable.assign(templateSettings, settingsClone); - }); - }); - - it('should use a `with` statement by default', function() { - var compiled = template('<%= index %><%= collection[index] %><% _.each(collection, function(value, index) { %><%= index %><% }); %>'), - actual = compiled({ 'index': 1, 'collection': ['a', 'b', 'c'] }); - - assert.strictEqual(actual, '1b012'); - }); - - it('should use `_.templateSettings.imports._.templateSettings`', function() { - var lodash = templateSettings.imports._, - settingsClone = lodashStable.clone(lodash.templateSettings); - - lodash.templateSettings = lodashStable.assign(lodash.templateSettings, { - 'interpolate': /\{\{=([\s\S]+?)\}\}/g - }); - - var compiled = template('{{= a }}'); - assert.strictEqual(compiled({ 'a': 1 }), '1'); - - if (settingsClone) { - lodashStable.assign(lodash.templateSettings, settingsClone); - } else { - delete lodash.templateSettings; - } - }); - - it('should fallback to `_.templateSettings`', function() { - var lodash = templateSettings.imports._, - delimiter = templateSettings.interpolate; - - templateSettings.imports._ = { 'escape': lodashStable.escape }; - templateSettings.interpolate = /\{\{=([\s\S]+?)\}\}/g; - - var compiled = template('{{= a }}'); - assert.strictEqual(compiled({ 'a': 1 }), '1'); - - templateSettings.imports._ = lodash; - templateSettings.interpolate = delimiter; - }); - - it('should ignore `null` delimiters', function() { - var delimiter = { - 'escape': /\{\{-([\s\S]+?)\}\}/g, - 'evaluate': /\{\{([\s\S]+?)\}\}/g, - 'interpolate': /\{\{=([\s\S]+?)\}\}/g - }; - - lodashStable.forOwn({ - 'escape': '{{- a }}', - 'evaluate': '{{ print(a) }}', - 'interpolate': '{{= a }}' - }, - function(value, key) { - var settings = { 'escape': null, 'evaluate': null, 'interpolate': null }; - settings[key] = delimiter[key]; - - var expected = '1 <%- a %> <% print(a) %> <%= a %>', - compiled = template(value + ' <%- a %> <% print(a) %> <%= a %>', settings), - data = { 'a': 1 }; - - assert.strictEqual(compiled(data), expected); - }); - }); - - it('should work without delimiters', function() { - var expected = 'abc'; - assert.strictEqual(template(expected)({}), expected); - }); - - it('should work with `this` references', function() { - var compiled = template('a<%= this.String("b") %>c'); - assert.strictEqual(compiled(), 'abc'); - - var object = { 'b': 'B' }; - object.compiled = template('A<%= this.b %>C', { 'variable': 'obj' }); - assert.strictEqual(object.compiled(), 'ABC'); - }); - - it('should work with backslashes', function() { - var compiled = template('<%= a %> \\b'), - data = { 'a': 'A' }; - - assert.strictEqual(compiled(data), 'A \\b'); - }); - - it('should work with escaped characters in string literals', function() { - var compiled = template('<% print("\'\\n\\r\\t\\u2028\\u2029\\\\") %>'); - assert.strictEqual(compiled(), "'\n\r\t\u2028\u2029\\"); - - var data = { 'a': 'A' }; - compiled = template('\'\n\r\t<%= a %>\u2028\u2029\\"'); - assert.strictEqual(compiled(data), '\'\n\r\tA\u2028\u2029\\"'); - }); - - it('should handle \\u2028 & \\u2029 characters', function() { - var compiled = template('\u2028<%= "\\u2028\\u2029" %>\u2029'); - assert.strictEqual(compiled(), '\u2028\u2028\u2029\u2029'); - }); - - it('should work with statements containing quotes', function() { - var compiled = template("<%\ - if (a == 'A' || a == \"a\") {\ - %>'a',\"A\"<%\ - } %>" - ); - - var data = { 'a': 'A' }; - assert.strictEqual(compiled(data), "'a',\"A\""); - }); - - it('should work with templates containing newlines and comments', function() { - var compiled = template('<%\n\ - // A code comment.\n\ - if (value) { value += 3; }\n\ - %>

<%= value %>

' - ); - - assert.strictEqual(compiled({ 'value': 3 }), '

6

'); - }); - - it('should tokenize delimiters', function() { - var compiled = template(''), - data = { 'type': 1 }; - - assert.strictEqual(compiled(data), ''); - }); - - it('should evaluate delimiters once', function() { - var actual = [], - compiled = template('<%= func("a") %><%- func("b") %><% func("c") %>'), - data = { 'func': function(value) { actual.push(value); } }; - - compiled(data); - assert.deepStrictEqual(actual, ['a', 'b', 'c']); - }); - - it('should match delimiters before escaping text', function() { - var compiled = template('<<\n a \n>>', { 'evaluate': /<<(.*?)>>/g }); - assert.strictEqual(compiled(), '<<\n a \n>>'); - }); - - it('should resolve nullish values to an empty string', function() { - var compiled = template('<%= a %><%- a %>'), - data = { 'a': null }; - - assert.strictEqual(compiled(data), ''); - - data = { 'a': undefined }; - assert.strictEqual(compiled(data), ''); - - data = { 'a': {} }; - compiled = template('<%= a.b %><%- a.b %>'); - assert.strictEqual(compiled(data), ''); - }); - - it('should return an empty string for empty values', function() { - var values = [, null, undefined, ''], - expected = lodashStable.map(values, stubString), - data = { 'a': 1 }; - - var actual = lodashStable.map(values, function(value, index) { - var compiled = index ? template(value) : template(); - return compiled(data); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should parse delimiters without newlines', function() { - var expected = '<<\nprint("

" + (value ? "yes" : "no") + "

")\n>>', - compiled = template(expected, { 'evaluate': /<<(.+?)>>/g }), - data = { 'value': true }; - - assert.strictEqual(compiled(data), expected); - }); - - it('should support recursive calls', function() { - var compiled = template('<%= a %><% a = _.template(c)(obj) %><%= a %>'), - data = { 'a': 'A', 'b': 'B', 'c': '<%= b %>' }; - - assert.strictEqual(compiled(data), 'AB'); - }); - - it('should coerce `text` to a string', function() { - var object = { 'toString': lodashStable.constant('<%= a %>') }, - data = { 'a': 1 }; - - assert.strictEqual(template(object)(data), '1'); - }); - - it('should not modify the `options` object', function() { - var options = {}; - template('', options); - assert.deepStrictEqual(options, {}); - }); - - it('should not modify `_.templateSettings` when `options` are given', function() { - var data = { 'a': 1 }; - - assert.ok(!('a' in templateSettings)); - template('', {}, data); - assert.ok(!('a' in templateSettings)); - - delete templateSettings.a; - }); - - it('should not error for non-object `data` and `options` values', function() { - template('')(1); - assert.ok(true, '`data` value'); - - template('', 1)(1); - assert.ok(true, '`options` value'); - }); - - it('should expose the source on compiled templates', function() { - var compiled = template('x'), - values = [String(compiled), compiled.source], - expected = lodashStable.map(values, stubTrue); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.includes(value, '__p'); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should expose the source on SyntaxErrors', function() { - try { - template('<% if x %>'); - } catch (e) { - var source = e.source; - } - assert.ok(lodashStable.includes(source, '__p')); - }); - - it('should not include sourceURLs in the source', function() { - var options = { 'sourceURL': '/a/b/c' }, - compiled = template('x', options), - values = [compiled.source, undefined]; - - try { - template('<% if x %>', options); - } catch (e) { - values[1] = e.source; - } - var expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(values, function(value) { - return lodashStable.includes(value, 'sourceURL'); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var array = ['<%= a %>', '<%- b %>', '<% print(c) %>'], - compiles = lodashStable.map(array, template), - data = { 'a': 'one', 'b': '"two"', 'c': 'three' }; - - var actual = lodashStable.map(compiles, function(compiled) { - return compiled(data); - }); - - assert.deepStrictEqual(actual, ['one', '"two"', 'three']); - }); -}); diff --git a/test/template.spec.js b/test/template.spec.js new file mode 100644 index 0000000000..bf1c22ee89 --- /dev/null +++ b/test/template.spec.js @@ -0,0 +1,464 @@ +import lodashStable from 'lodash'; +import { numberTag, stubString, stubTrue, stubFalse } from './utils'; +import template from '../src/template'; +import templateSettings from '../src/templateSettings'; + +describe('template', () => { + it('should escape values in "escape" delimiters', () => { + const strings = ['

<%- value %>

', '

<%-value%>

', '

<%-\nvalue\n%>

']; + const expected = lodashStable.map( + strings, + lodashStable.constant('

&<>"'/

'), + ); + const data = { value: '&<>"\'/' }; + + const actual = lodashStable.map(strings, (string) => template(string)(data)); + + expect(actual).toEqual(expected); + }); + + it('should not reference `_.escape` when "escape" delimiters are not used', () => { + const compiled = template('<%= typeof __e %>'); + expect(compiled({})).toBe('undefined'); + }); + + it('should evaluate JavaScript in "evaluate" delimiters', () => { + const compiled = template( + '', + ); + + const data = { collection: { a: 'A', b: 'B' } }; + const actual = compiled(data); + + expect(actual).toBe(''); + }); + + it('should support "evaluate" delimiters with single line comments (test production builds)', () => { + const compiled = template( + '<% // A code comment. %><% if (value) { %>yap<% } else { %>nope<% } %>', + ); + const data = { value: true }; + + expect(compiled(data)).toBe('yap'); + }); + + it('should support referencing variables declared in "evaluate" delimiters from other delimiters', () => { + const compiled = template('<% var b = a; %><%= b.value %>'); + const data = { a: { value: 1 } }; + + expect(compiled(data)).toBe('1'); + }); + + it('should interpolate data properties in "interpolate" delimiters', () => { + const strings = ['<%= a %>BC', '<%=a%>BC', '<%=\na\n%>BC']; + const expected = lodashStable.map(strings, lodashStable.constant('ABC')); + const data = { a: 'A' }; + + const actual = lodashStable.map(strings, (string) => template(string)(data)); + + expect(actual).toEqual(expected); + }); + + it('should support "interpolate" delimiters with escaped values', () => { + const compiled = template('<%= a ? "a=\\"A\\"" : "" %>'); + const data = { a: true }; + + expect(compiled(data)).toBe('a="A"'); + }); + + it('should support "interpolate" delimiters containing ternary operators', () => { + const compiled = template('<%= value ? value : "b" %>'); + const data = { value: 'a' }; + + expect(compiled(data)).toBe('a'); + }); + + it('should support "interpolate" delimiters containing global values', () => { + const compiled = template('<%= typeof Math.abs %>'); + + try { + var actual = compiled(); + } catch (e) {} + + expect(actual).toBe('function'); + }); + + it('should support complex "interpolate" delimiters', () => { + lodashStable.forOwn( + { + '<%= a + b %>': '3', + '<%= b - a %>': '1', + '<%= a = b %>': '2', + '<%= !a %>': 'false', + '<%= ~a %>': '-2', + '<%= a * b %>': '2', + '<%= a / b %>': '0.5', + '<%= a % b %>': '1', + '<%= a >> b %>': '0', + '<%= a << b %>': '4', + '<%= a & b %>': '0', + '<%= a ^ b %>': '3', + '<%= a | b %>': '3', + '<%= {}.toString.call(0) %>': numberTag, + '<%= a.toFixed(2) %>': '1.00', + '<%= obj["a"] %>': '1', + '<%= delete a %>': 'true', + '<%= "a" in obj %>': 'true', + '<%= obj instanceof Object %>': 'true', + '<%= new Boolean %>': 'false', + '<%= typeof a %>': 'number', + '<%= void a %>': '', + }, + (value, key) => { + const compiled = template(key); + const data = { a: 1, b: 2 }; + + expect(compiled(data), value).toBe(key); + }, + ); + }); + + it('should support ES6 template delimiters', () => { + const data = { value: 2 }; + expect(template('1${value}3')(data)).toBe('123'); + expect(template('${"{" + value + "\\}"}')(data)).toBe('{2}'); + }); + + it('should support the "imports" option', () => { + const compiled = template('<%= a %>', { imports: { a: 1 } }); + expect(compiled({})).toBe('1'); + }); + + it('should support the "variable" options', () => { + const compiled = template( + '<% _.each( data.a, function( value ) { %>' + '<%= value.valueOf() %>' + '<% }) %>', + { variable: 'data' }, + ); + + const data = { a: [1, 2, 3] }; + + try { + expect(compiled(data)).toBe('123'); + } catch (e) { + expect(false, e.message) + } + }); + + it('should support custom delimiters', () => { + lodashStable.times(2, (index) => { + const settingsClone = lodashStable.clone(templateSettings); + + const settings = lodashStable.assign(index ? templateSettings : {}, { + escape: /\{\{-([\s\S]+?)\}\}/g, + evaluate: /\{\{([\s\S]+?)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g, + }); + + const expected = ''; + const compiled = template( + '', + index ? null : settings, + ); + const data = { collection: ['a & A', 'b & B'] }; + + expect(compiled(data)).toBe(expected); + lodashStable.assign(templateSettings, settingsClone); + }); + }); + + it('should support custom delimiters containing special characters', () => { + lodashStable.times(2, (index) => { + const settingsClone = lodashStable.clone(templateSettings); + + const settings = lodashStable.assign(index ? templateSettings : {}, { + escape: /<\?-([\s\S]+?)\?>/g, + evaluate: /<\?([\s\S]+?)\?>/g, + interpolate: /<\?=([\s\S]+?)\?>/g, + }); + + const expected = ''; + const compiled = template( + '', + index ? null : settings, + ); + const data = { collection: ['a & A', 'b & B'] }; + + expect(compiled(data)).toBe(expected); + lodashStable.assign(templateSettings, settingsClone); + }); + }); + + it('should use a `with` statement by default', () => { + const compiled = template( + '<%= index %><%= collection[index] %><% _.each(collection, function(value, index) { %><%= index %><% }); %>', + ); + const actual = compiled({ index: 1, collection: ['a', 'b', 'c'] }); + + expect(actual).toBe('1b012'); + }); + + it('should use `_.templateSettings.imports._.templateSettings`', () => { + const lodash = templateSettings.imports._; + const settingsClone = lodashStable.clone(lodash.templateSettings); + + lodash.templateSettings = lodashStable.assign(lodash.templateSettings, { + interpolate: /\{\{=([\s\S]+?)\}\}/g, + }); + + const compiled = template('{{= a }}'); + expect(compiled({ a: 1 })).toBe('1'); + + if (settingsClone) { + lodashStable.assign(lodash.templateSettings, settingsClone); + } else { + delete lodash.templateSettings; + } + }); + + it('should fallback to `_.templateSettings`', () => { + const lodash = templateSettings.imports._; + const delimiter = templateSettings.interpolate; + + templateSettings.imports._ = { escape: lodashStable.escape }; + templateSettings.interpolate = /\{\{=([\s\S]+?)\}\}/g; + + const compiled = template('{{= a }}'); + expect(compiled({ a: 1 })).toBe('1'); + + templateSettings.imports._ = lodash; + templateSettings.interpolate = delimiter; + }); + + it('should ignore `null` delimiters', () => { + const delimiter = { + escape: /\{\{-([\s\S]+?)\}\}/g, + evaluate: /\{\{([\s\S]+?)\}\}/g, + interpolate: /\{\{=([\s\S]+?)\}\}/g, + }; + + lodashStable.forOwn( + { + escape: '{{- a }}', + evaluate: '{{ print(a) }}', + interpolate: '{{= a }}', + }, + (value, key) => { + const settings = { escape: null, evaluate: null, interpolate: null }; + settings[key] = delimiter[key]; + + const expected = '1 <%- a %> <% print(a) %> <%= a %>'; + const compiled = template(`${value} <%- a %> <% print(a) %> <%= a %>`, settings); + const data = { a: 1 }; + + expect(compiled(data)).toBe(expected); + }, + ); + }); + + it('should work without delimiters', () => { + const expected = 'abc'; + expect(template(expected)({})).toBe(expected); + }); + + it('should work with `this` references', () => { + const compiled = template('a<%= this.String("b") %>c'); + expect(compiled()).toBe('abc'); + + const object = { b: 'B' }; + object.compiled = template('A<%= this.b %>C', { variable: 'obj' }); + expect(object.compiled()).toBe('ABC'); + }); + + it('should work with backslashes', () => { + const compiled = template('<%= a %> \\b'); + const data = { a: 'A' }; + + expect(compiled(data)).toBe('A \\b'); + }); + + it('should work with escaped characters in string literals', () => { + let compiled = template('<% print("\'\\n\\r\\t\\u2028\\u2029\\\\") %>'); + expect(compiled()).toBe("'\n\r\t\u2028\u2029\\"); + + const data = { a: 'A' }; + compiled = template('\'\n\r\t<%= a %>\u2028\u2029\\"'); + expect(compiled(data)).toBe('\'\n\r\tA\u2028\u2029\\"'); + }); + + it('should handle \\u2028 & \\u2029 characters', () => { + const compiled = template('\u2028<%= "\\u2028\\u2029" %>\u2029'); + expect(compiled()).toBe('\u2028\u2028\u2029\u2029'); + }); + + it('should work with statements containing quotes', () => { + const compiled = template( + '<%\ + if (a === \'A\' || a === "a") {\ + %>\'a\',"A"<%\ + } %>', + ); + + const data = { a: 'A' }; + expect(compiled(data), '\'a\').toBe("A"'); + }); + + it('should work with templates containing newlines and comments', () => { + const compiled = template( + '<%\n\ + // A code comment.\n\ + if (value) { value += 3; }\n\ + %>

<%= value %>

', + ); + + expect(compiled({ value: 3 })).toBe('

6

'); + }); + + it('should tokenize delimiters', () => { + const compiled = template(''); + const data = { type: 1 }; + + expect(compiled(data)).toBe(''); + }); + + it('should evaluate delimiters once', () => { + const actual = []; + const compiled = template('<%= func("a") %><%- func("b") %><% func("c") %>'); + const data = { + func: function (value) { + actual.push(value); + }, + }; + + compiled(data); + expect(actual, ['a', 'b').toEqual('c']); + }); + + it('should match delimiters before escaping text', () => { + const compiled = template('<<\n a \n>>', { evaluate: /<<(.*?)>>/g }); + expect(compiled()).toBe('<<\n a \n>>'); + }); + + it('should resolve nullish values to an empty string', () => { + let compiled = template('<%= a %><%- a %>'); + let data = { a: null }; + + expect(compiled(data)).toBe(''); + + data = { a: undefined }; + expect(compiled(data)).toBe(''); + + data = { a: {} }; + compiled = template('<%= a.b %><%- a.b %>'); + expect(compiled(data)).toBe(''); + }); + + it('should return an empty string for empty values', () => { + const values = [, null, undefined, '']; + const expected = lodashStable.map(values, stubString); + const data = { a: 1 }; + + const actual = lodashStable.map(values, (value, index) => { + const compiled = index ? template(value) : template(); + return compiled(data); + }); + + expect(actual).toEqual(expected); + }); + + it('should parse delimiters without newlines', () => { + const expected = '<<\nprint("

" + (value ? "yes" : "no") + "

")\n>>'; + const compiled = template(expected, { evaluate: /<<(.+?)>>/g }); + const data = { value: true }; + + expect(compiled(data)).toBe(expected); + }); + + it('should support recursive calls', () => { + const compiled = template('<%= a %><% a = _.template(c)(obj) %><%= a %>'); + const data = { a: 'A', b: 'B', c: '<%= b %>' }; + + expect(compiled(data)).toBe('AB'); + }); + + it('should coerce `text` to a string', () => { + const object = { toString: lodashStable.constant('<%= a %>') }; + const data = { a: 1 }; + + expect(template(object)(data)).toBe('1'); + }); + + it('should not modify the `options` object', () => { + const options = {}; + template('', options); + expect(options).toEqual({}); + }); + + it('should not modify `_.templateSettings` when `options` are given', () => { + const data = { a: 1 }; + + expect(('a' in templateSettings)).toBe(false) + template('', {}, data); + expect(('a' in templateSettings)).toBe(false) + + delete templateSettings.a; + }); + + it('should not error for non-object `data` and `options` values', () => { + template('')(1); + expect(true, '`data` value') + + template('', 1)(1); + expect(true, '`options` value') + }); + + it('should expose the source on compiled templates', () => { + const compiled = template('x'); + const values = [String(compiled), compiled.source]; + const expected = lodashStable.map(values, stubTrue); + + const actual = lodashStable.map(values, (value) => lodashStable.includes(value, '__p')); + + expect(actual).toEqual(expected); + }); + + it('should expose the source on SyntaxErrors', () => { + try { + template('<% if x %>'); + } catch (e) { + var source = e.source; + } + expect(lodashStable.includes(source, '__p')) + }); + + it('should not include sourceURLs in the source', () => { + const options = { sourceURL: '/a/b/c' }; + const compiled = template('x', options); + const values = [compiled.source, undefined]; + + try { + template('<% if x %>', options); + } catch (e) { + values[1] = e.source; + } + const expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(values, (value) => + lodashStable.includes(value, 'sourceURL'), + ); + + expect(actual).toEqual(expected); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const array = ['<%= a %>', '<%- b %>', '<% print(c) %>']; + const compiles = lodashStable.map(array, template); + const data = { a: 'one', b: '"two"', c: 'three' }; + + const actual = lodashStable.map(compiles, (compiled) => compiled(data)); + + expect(actual, ['one', '"two"').toEqual('three']); + }); +}); diff --git a/test/throttle.js b/test/throttle.js deleted file mode 100644 index c495b00eb5..0000000000 --- a/test/throttle.js +++ /dev/null @@ -1,227 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { identity, isModularize, argv, isPhantom } from './utils.js'; -import throttle from '../throttle.js'; -import runInContext from '../runInContext.js'; - -describe('throttle', function() { - it('should throttle a function', function(done) { - var callCount = 0, - throttled = throttle(function() { callCount++; }, 32); - - throttled(); - throttled(); - throttled(); - - var lastCount = callCount; - assert.ok(callCount); - - setTimeout(function() { - assert.ok(callCount > lastCount); - done(); - }, 64); - }); - - it('subsequent calls should return the result of the first call', function(done) { - var throttled = throttle(identity, 32), - results = [throttled('a'), throttled('b')]; - - assert.deepStrictEqual(results, ['a', 'a']); - - setTimeout(function() { - var results = [throttled('c'), throttled('d')]; - assert.notStrictEqual(results[0], 'a'); - assert.notStrictEqual(results[0], undefined); - - assert.notStrictEqual(results[1], 'd'); - assert.notStrictEqual(results[1], undefined); - done(); - }, 64); - }); - - it('should clear timeout when `func` is called', function(done) { - if (!isModularize) { - var callCount = 0, - dateCount = 0; - - var lodash = runInContext({ - 'Date': { - 'now': function() { - return ++dateCount == 5 ? Infinity : +new Date; - } - } - }); - - var throttled = lodash.throttle(function() { callCount++; }, 32); - - throttled(); - throttled(); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 64); - } - else { - done(); - } - }); - - it('should not trigger a trailing call when invoked once', function(done) { - var callCount = 0, - throttled = throttle(function() { callCount++; }, 32); - - throttled(); - assert.strictEqual(callCount, 1); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - done(); - }, 64); - }); - - lodashStable.times(2, function(index) { - it('should trigger a call when invoked repeatedly' + (index ? ' and `leading` is `false`' : ''), function(done) { - var callCount = 0, - limit = (argv || isPhantom) ? 1000 : 320, - options = index ? { 'leading': false } : {}, - throttled = throttle(function() { callCount++; }, 32, options); - - var start = +new Date; - while ((new Date - start) < limit) { - throttled(); - } - var actual = callCount > 1; - setTimeout(function() { - assert.ok(actual); - done(); - }, 1); - }); - }); - - it('should trigger a second throttled call as soon as possible', function(done) { - var callCount = 0; - - var throttled = throttle(function() { - callCount++; - }, 128, { 'leading': false }); - - throttled(); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - throttled(); - }, 192); - - setTimeout(function() { - assert.strictEqual(callCount, 1); - }, 254); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 384); - }); - - it('should apply default options', function(done) { - var callCount = 0, - throttled = throttle(function() { callCount++; }, 32, {}); - - throttled(); - throttled(); - assert.strictEqual(callCount, 1); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 128); - }); - - it('should support a `leading` option', function() { - var withLeading = throttle(identity, 32, { 'leading': true }); - assert.strictEqual(withLeading('a'), 'a'); - - var withoutLeading = throttle(identity, 32, { 'leading': false }); - assert.strictEqual(withoutLeading('a'), undefined); - }); - - it('should support a `trailing` option', function(done) { - var withCount = 0, - withoutCount = 0; - - var withTrailing = throttle(function(value) { - withCount++; - return value; - }, 64, { 'trailing': true }); - - var withoutTrailing = throttle(function(value) { - withoutCount++; - return value; - }, 64, { 'trailing': false }); - - assert.strictEqual(withTrailing('a'), 'a'); - assert.strictEqual(withTrailing('b'), 'a'); - - assert.strictEqual(withoutTrailing('a'), 'a'); - assert.strictEqual(withoutTrailing('b'), 'a'); - - setTimeout(function() { - assert.strictEqual(withCount, 2); - assert.strictEqual(withoutCount, 1); - done(); - }, 256); - }); - - it('should not update `lastCalled`, at the end of the timeout, when `trailing` is `false`', function(done) { - var callCount = 0; - - var throttled = throttle(function() { - callCount++; - }, 64, { 'trailing': false }); - - throttled(); - throttled(); - - setTimeout(function() { - throttled(); - throttled(); - }, 96); - - setTimeout(function() { - assert.ok(callCount > 1); - done(); - }, 192); - }); - - it('should work with a system time of `0`', function(done) { - if (!isModularize) { - var callCount = 0, - dateCount = 0; - - var lodash = runInContext({ - 'Date': { - 'now': function() { - return ++dateCount < 4 ? 0 : +new Date; - } - } - }); - - var throttled = lodash.throttle(function(value) { - callCount++; - return value; - }, 32); - - var results = [throttled('a'), throttled('b'), throttled('c')]; - assert.deepStrictEqual(results, ['a', 'a', 'a']); - assert.strictEqual(callCount, 1); - - setTimeout(function() { - assert.strictEqual(callCount, 2); - done(); - }, 64); - } - else { - done(); - } - }); -}); diff --git a/test/throttle.spec.js b/test/throttle.spec.js new file mode 100644 index 0000000000..4d8c2c5424 --- /dev/null +++ b/test/throttle.spec.js @@ -0,0 +1,260 @@ +import lodashStable, { runInContext } from 'lodash'; +import * as assert from 'assert'; +import { identity, isModularize, argv, isPhantom } from './utils'; +import throttle from '../src/throttle'; + +describe('throttle', () => { + it('should throttle a function', (done) => { + let callCount = 0; + const throttled = throttle(() => { + callCount++; + }, 32); + + throttled(); + throttled(); + throttled(); + + const lastCount = callCount; + expect(callCount); + + setTimeout(() => { + expect(callCount > lastCount); + done(); + }, 64); + }); + + it('subsequent calls should return the result of the first call', (done) => { + const throttled = throttle(identity, 32); + const results = [throttled('a'), throttled('b')]; + + expect(results).toEqual(['a', 'a']); + + setTimeout(() => { + const results = [throttled('c'), throttled('d')]; + assert.notStrictEqual(results[0], 'a'); + assert.notStrictEqual(results[0], undefined); + + assert.notStrictEqual(results[1], 'd'); + assert.notStrictEqual(results[1], undefined); + done(); + }, 64); + }); + + it('should clear timeout when `func` is called', (done) => { + if (!isModularize) { + let callCount = 0; + let dateCount = 0; + + const lodash = runInContext({ + Date: { + now: function () { + return ++dateCount === 5 ? Infinity : +new Date(); + }, + }, + }); + + const throttled = lodash.throttle(() => { + callCount++; + }, 32); + + throttled(); + throttled(); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 64); + } else { + done(); + } + }); + + it('should not trigger a trailing call when invoked once', (done) => { + let callCount = 0; + const throttled = throttle(() => { + callCount++; + }, 32); + + throttled(); + expect(callCount).toBe(1); + + setTimeout(() => { + expect(callCount).toBe(1); + done(); + }, 64); + }); + + lodashStable.times(2, (index) => { + it(`should trigger a call when invoked repeatedly${ + index ? ' and `leading` is `false`' : '' + }`, (done) => { + let callCount = 0; + const limit = argv || isPhantom ? 1000 : 320; + const options = index ? { leading: false } : {}; + const throttled = throttle( + () => { + callCount++; + }, + 32, + options, + ); + + const start = +new Date(); + while (new Date() - start < limit) { + throttled(); + } + const actual = callCount > 1; + setTimeout(() => { + expect(actual); + done(); + }, 1); + }); + }); + + it('should trigger a second throttled call as soon as possible', (done) => { + let callCount = 0; + + const throttled = throttle( + () => { + callCount++; + }, + 128, + { leading: false }, + ); + + throttled(); + + setTimeout(() => { + expect(callCount).toBe(1); + throttled(); + }, 192); + + setTimeout(() => { + expect(callCount).toBe(1); + }, 254); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 384); + }); + + it('should apply default options', (done) => { + let callCount = 0; + const throttled = throttle( + () => { + callCount++; + }, + 32, + {}, + ); + + throttled(); + throttled(); + expect(callCount).toBe(1); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 128); + }); + + it('should support a `leading` option', () => { + const withLeading = throttle(identity, 32, { leading: true }); + expect(withLeading('a')).toBe('a'); + + const withoutLeading = throttle(identity, 32, { leading: false }); + expect(withoutLeading('a')).toBe(undefined); + }); + + it('should support a `trailing` option', (done) => { + let withCount = 0; + let withoutCount = 0; + + const withTrailing = throttle( + (value) => { + withCount++; + return value; + }, + 64, + { trailing: true }, + ); + + const withoutTrailing = throttle( + (value) => { + withoutCount++; + return value; + }, + 64, + { trailing: false }, + ); + + expect(withTrailing('a')).toBe('a'); + expect(withTrailing('b')).toBe('a'); + + expect(withoutTrailing('a')).toBe('a'); + expect(withoutTrailing('b')).toBe('a'); + + setTimeout(() => { + expect(withCount).toBe(2); + expect(withoutCount).toBe(1); + done(); + }, 256); + }); + + it('should not update `lastCalled`, at the end of the timeout, when `trailing` is `false`', (done) => { + let callCount = 0; + + const throttled = throttle( + () => { + callCount++; + }, + 64, + { trailing: false }, + ); + + throttled(); + throttled(); + + setTimeout(() => { + throttled(); + throttled(); + }, 96); + + setTimeout(() => { + expect(callCount > 1); + done(); + }, 192); + }); + + it('should work with a system time of `0`', (done) => { + if (!isModularize) { + let callCount = 0; + let dateCount = 0; + + const lodash = runInContext({ + Date: { + now: function () { + return ++dateCount < 4 ? 0 : +new Date(); + }, + }, + }); + + const throttled = lodash.throttle((value) => { + callCount++; + return value; + }, 32); + + const results = [throttled('a'), throttled('b'), throttled('c')]; + expect(results).toEqual(['a', 'a', 'a']); + expect(callCount).toBe(1); + + setTimeout(() => { + expect(callCount).toBe(2); + done(); + }, 64); + } else { + done(); + } + }); +}); diff --git a/test/times.js b/test/times.js deleted file mode 100644 index b9873f2048..0000000000 --- a/test/times.js +++ /dev/null @@ -1,62 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice, doubled, falsey, stubArray } from './utils.js'; -import times from '../times.js'; -import identity from '../identity.js'; - -describe('times', function() { - it('should coerce non-finite `n` values to `0`', function() { - lodashStable.each([-Infinity, NaN, Infinity], function(n) { - assert.deepStrictEqual(times(n), []); - }); - }); - - it('should coerce `n` to an integer', function() { - var actual = times(2.6, identity); - assert.deepStrictEqual(actual, [0, 1]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - times(1, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [0]); - }); - - it('should use `_.identity` when `iteratee` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant([0, 1, 2])); - - var actual = lodashStable.map(values, function(value, index) { - return index ? times(3, value) : times(3); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an array of the results of each `iteratee` execution', function() { - assert.deepStrictEqual(times(3, doubled), [0, 2, 4]); - }); - - it('should return an empty array for falsey and negative `n` values', function() { - var values = falsey.concat(-1, -Infinity), - expected = lodashStable.map(values, stubArray); - - var actual = lodashStable.map(values, function(value, index) { - return index ? times(value) : times(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should return an unwrapped value when implicitly chaining', function() { - assert.deepStrictEqual(_(3).times(), [0, 1, 2]); - }); - - it('should return a wrapped value when explicitly chaining', function() { - assert.ok(_(3).chain().times() instanceof _); - }); -}); diff --git a/test/times.spec.js b/test/times.spec.js new file mode 100644 index 0000000000..75d781ab71 --- /dev/null +++ b/test/times.spec.js @@ -0,0 +1,50 @@ +import lodashStable from 'lodash'; +import { slice, doubled, falsey, stubArray } from './utils'; +import times from '../src/times'; + +describe('times', () => { + it('should coerce non-finite `n` values to `0`', () => { + lodashStable.each([-Infinity, NaN, Infinity], (n) => { + expect(times(n)).toEqual([]); + }); + }); + + it('should coerce `n` to an integer', () => { + const actual = times(2.6, (n) => n); + expect(actual, [0, 1]).toEqual(1); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + times(1, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([0]); + }); + + it('should use `_.identity` when `iteratee` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant([0, 1, 2])); + + const actual = lodashStable.map(values, (value, index) => + index ? times(3, value) : times(3), + ); + + expect(actual).toEqual(expected); + }); + + it('should return an array of the results of each `iteratee` execution', () => { + expect(times(3, doubled)).toEqual([0, 2, 4]); + }); + + it('should return an empty array for falsey and negative `n` values', () => { + const values = falsey.concat(-1, -Infinity); + const expected = lodashStable.map(values, stubArray); + + const actual = lodashStable.map(values, (value, index) => (index ? times(value) : times())); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/toArray.spec.js b/test/toArray.spec.js new file mode 100644 index 0000000000..4c8c896e90 --- /dev/null +++ b/test/toArray.spec.js @@ -0,0 +1,35 @@ +import lodashStable from 'lodash'; +import { arrayProto, LARGE_ARRAY_SIZE } from './utils'; +import toArray from '../src/toArray'; + +describe('toArray', () => { + it('should convert objects to arrays', () => { + expect(toArray({ a: 1, b: 2 })).toEqual([1, 2]); + }); + + it('should convert iterables to arrays', () => { + if (Symbol && Symbol.iterator) { + const object = { 0: 'a', length: 1 }; + object[Symbol.iterator] = arrayProto[Symbol.iterator]; + expect(toArray(object)).toEqual(['a']); + } + }); + + it('should convert maps to arrays', () => { + if (Map) { + const map = new Map(); + map.set('a', 1); + map.set('b', 2); + expect(toArray(map)).toEqual([ + ['a', 1], + ['b', 2], + ]); + } + }); + + it('should convert strings to arrays', () => { + expect(toArray('')).toEqual([]); + expect(toArray('ab')).toEqual(['a', 'b']); + expect(toArray(Object('ab'))).toEqual(['a', 'b']); + }); +}); diff --git a/test/toArray.test.js b/test/toArray.test.js deleted file mode 100644 index 7baaf3c411..0000000000 --- a/test/toArray.test.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { arrayProto, LARGE_ARRAY_SIZE } from './utils.js'; -import toArray from '../toArray.js'; - -describe('toArray', function() { - it('should convert objects to arrays', function() { - assert.deepStrictEqual(toArray({ 'a': 1, 'b': 2 }), [1, 2]); - }); - - it('should convert iterables to arrays', function() { - if (Symbol && Symbol.iterator) { - var object = { '0': 'a', 'length': 1 }; - object[Symbol.iterator] = arrayProto[Symbol.iterator]; - - assert.deepStrictEqual(toArray(object), ['a']); - } - }); - - it('should convert maps to arrays', function() { - if (Map) { - var map = new Map; - map.set('a', 1); - map.set('b', 2); - assert.deepStrictEqual(toArray(map), [['a', 1], ['b', 2]]); - } - }); - - it('should convert strings to arrays', function() { - assert.deepStrictEqual(toArray(''), []); - assert.deepStrictEqual(toArray('ab'), ['a', 'b']); - assert.deepStrictEqual(toArray(Object('ab')), ['a', 'b']); - }); - - it('should work in a lazy sequence', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE + 1); - - var object = lodashStable.zipObject(lodashStable.times(LARGE_ARRAY_SIZE, function(index) { - return ['key' + index, index]; - })); - - var actual = _(array).slice(1).map(String).toArray().value(); - assert.deepEqual(actual, lodashStable.map(array.slice(1), String)); - - actual = _(object).toArray().slice(1).map(String).value(); - assert.deepEqual(actual, _.map(toArray(object).slice(1), String)); - }); -}); diff --git a/test/toInteger-methods.js b/test/toInteger-methods.js deleted file mode 100644 index c9d0c03498..0000000000 --- a/test/toInteger-methods.js +++ /dev/null @@ -1,25 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, MAX_SAFE_INTEGER, MAX_INTEGER } from './utils.js'; - -describe('toInteger methods', function() { - lodashStable.each(['toInteger', 'toSafeInteger'], function(methodName) { - var func = _[methodName], - isSafe = methodName == 'toSafeInteger'; - - it('`_.' + methodName + '` should convert values to integers', function() { - assert.strictEqual(func(-5.6), -5); - assert.strictEqual(func('5.6'), 5); - assert.strictEqual(func(), 0); - assert.strictEqual(func(NaN), 0); - - var expected = isSafe ? MAX_SAFE_INTEGER : MAX_INTEGER; - assert.strictEqual(func(Infinity), expected); - assert.strictEqual(func(-Infinity), -expected); - }); - - it('`_.' + methodName + '` should support `value` of `-0`', function() { - assert.strictEqual(1 / func(-0), -Infinity); - }); - }); -}); diff --git a/test/toInteger-methods.spec.js b/test/toInteger-methods.spec.js new file mode 100644 index 0000000000..8423327785 --- /dev/null +++ b/test/toInteger-methods.spec.js @@ -0,0 +1,24 @@ +import lodashStable from 'lodash'; +import { _, MAX_SAFE_INTEGER, MAX_INTEGER } from './utils'; + +describe('toInteger methods', () => { + lodashStable.each(['toInteger', 'toSafeInteger'], (methodName) => { + const func = _[methodName]; + const isSafe = methodName === 'toSafeInteger'; + + it(`\`_.${methodName}\` should convert values to integers`, () => { + expect(func(-5.6)).toBe(-5); + expect(func('5.6')).toBe(5); + expect(func()).toBe(0); + expect(func(NaN)).toBe(0); + + const expected = isSafe ? MAX_SAFE_INTEGER : MAX_INTEGER; + expect(func(Infinity)).toBe(expected); + expect(func(-Infinity)).toBe(-expected); + }); + + it(`\`_.${methodName}\` should support \`value\` of \`-0\``, () => { + expect(1 / func(-0)).toBe(-Infinity); + }); + }); +}); diff --git a/test/toLength.spec.js b/test/toLength.spec.js new file mode 100644 index 0000000000..975ccfc748 --- /dev/null +++ b/test/toLength.spec.js @@ -0,0 +1,21 @@ +import { MAX_INTEGER, MAX_ARRAY_LENGTH } from './utils'; +import toLength from '../src/toLength'; + +describe('toLength', () => { + it('should return a valid length', () => { + expect(toLength(-1)).toBe(0); + expect(toLength('1')).toBe(1); + expect(toLength(1.1)).toBe(1); + expect(toLength(MAX_INTEGER)).toBe(MAX_ARRAY_LENGTH); + }); + + it('should return `value` if a valid length', () => { + expect(toLength(0)).toBe(0); + expect(toLength(3)).toBe(3); + expect(toLength(MAX_ARRAY_LENGTH)).toBe(MAX_ARRAY_LENGTH); + }); + + it('should convert `-0` to `0`', () => { + expect(1 / toLength(-0)).toBe(Infinity); + }); +}); diff --git a/test/toLength.test.js b/test/toLength.test.js deleted file mode 100644 index 3abbdd227e..0000000000 --- a/test/toLength.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import { MAX_INTEGER, MAX_ARRAY_LENGTH } from './utils.js'; -import toLength from '../toLength.js'; - -describe('toLength', function() { - it('should return a valid length', function() { - assert.strictEqual(toLength(-1), 0); - assert.strictEqual(toLength('1'), 1); - assert.strictEqual(toLength(1.1), 1); - assert.strictEqual(toLength(MAX_INTEGER), MAX_ARRAY_LENGTH); - }); - - it('should return `value` if a valid length', function() { - assert.strictEqual(toLength(0), 0); - assert.strictEqual(toLength(3), 3); - assert.strictEqual(toLength(MAX_ARRAY_LENGTH), MAX_ARRAY_LENGTH); - }); - - it('should convert `-0` to `0`', function() { - assert.strictEqual(1 / toLength(-0), Infinity); - }); -}); diff --git a/test/toLower.js b/test/toLower.js deleted file mode 100644 index 03e7117d96..0000000000 --- a/test/toLower.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import toLower from '../toLower.js'; - -describe('toLower', function() { - it('should convert whole string to lower case', function() { - assert.deepStrictEqual(toLower('--Foo-Bar--'), '--foo-bar--'); - assert.deepStrictEqual(toLower('fooBar'), 'foobar'); - assert.deepStrictEqual(toLower('__FOO_BAR__'), '__foo_bar__'); - }); -}); diff --git a/test/toLower.spec.js b/test/toLower.spec.js new file mode 100644 index 0000000000..46820fd8f0 --- /dev/null +++ b/test/toLower.spec.js @@ -0,0 +1,9 @@ +import toLower from '../src/toLower'; + +describe('toLower', () => { + it('should convert whole string to lower case', () => { + expect(toLower('--Foo-Bar--')).toEqual('--foo-bar--'); + expect(toLower('fooBar')).toEqual('foobar'); + expect(toLower('__FOO_BAR__')).toEqual('__foo_bar__'); + }); +}); diff --git a/test/toPairs-methods.js b/test/toPairs-methods.js deleted file mode 100644 index 34f0ddba25..0000000000 --- a/test/toPairs-methods.js +++ /dev/null @@ -1,61 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('toPairs methods', function() { - lodashStable.each(['toPairs', 'toPairsIn'], function(methodName) { - var func = _[methodName], - isToPairs = methodName == 'toPairs'; - - it('`_.' + methodName + '` should create an array of string keyed-value pairs', function() { - var object = { 'a': 1, 'b': 2 }, - actual = lodashStable.sortBy(func(object), 0); - - assert.deepStrictEqual(actual, [['a', 1], ['b', 2]]); - }); - - it('`_.' + methodName + '` should ' + (isToPairs ? 'not ' : '') + 'include inherited string keyed property values', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var expected = isToPairs ? [['a', 1]] : [['a', 1], ['b', 2]], - actual = lodashStable.sortBy(func(new Foo), 0); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should convert objects with a `length` property', function() { - var object = { '0': 'a', '1': 'b', 'length': 2 }, - actual = lodashStable.sortBy(func(object), 0); - - assert.deepStrictEqual(actual, [['0', 'a'], ['1', 'b'], ['length', 2]]); - }); - - it('`_.' + methodName + '` should convert maps', function() { - if (Map) { - var map = new Map; - map.set('a', 1); - map.set('b', 2); - assert.deepStrictEqual(func(map), [['a', 1], ['b', 2]]); - } - }); - - it('`_.' + methodName + '` should convert sets', function() { - if (Set) { - var set = new Set; - set.add(1); - set.add(2); - assert.deepStrictEqual(func(set), [[1, 1], [2, 2]]); - } - }); - - it('`_.' + methodName + '` should convert strings', function() { - lodashStable.each(['xo', Object('xo')], function(string) { - var actual = lodashStable.sortBy(func(string), 0); - assert.deepStrictEqual(actual, [['0', 'x'], ['1', 'o']]); - }); - }); - }); -}); diff --git a/test/toPairs-methods.spec.js b/test/toPairs-methods.spec.js new file mode 100644 index 0000000000..819cc10f0e --- /dev/null +++ b/test/toPairs-methods.spec.js @@ -0,0 +1,83 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('toPairs methods', () => { + lodashStable.each(['toPairs', 'toPairsIn'], (methodName) => { + const func = _[methodName]; + const isToPairs = methodName === 'toPairs'; + + it(`\`_.${methodName}\` should create an array of string keyed-value pairs`, () => { + const object = { a: 1, b: 2 }; + const actual = lodashStable.sortBy(func(object), 0); + + expect(actual).toEqual([ + ['a', 1], + ['b', 2], + ]); + }); + + it(`\`_.${methodName}\` should ${ + isToPairs ? 'not ' : '' + }include inherited string keyed property values`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const expected = isToPairs + ? [['a', 1]] + : [ + ['a', 1], + ['b', 2], + ]; + const actual = lodashStable.sortBy(func(new Foo()), 0); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should convert objects with a \`length\` property`, () => { + const object = { 0: 'a', 1: 'b', length: 2 }; + const actual = lodashStable.sortBy(func(object), 0); + + expect(actual).toEqual([ + ['0', 'a'], + ['1', 'b'], + ['length', 2], + ]); + }); + + it(`\`_.${methodName}\` should convert maps`, () => { + if (Map) { + const map = new Map(); + map.set('a', 1); + map.set('b', 2); + expect(func(map)).toEqual([ + ['a', 1], + ['b', 2], + ]); + } + }); + + it(`\`_.${methodName}\` should convert sets`, () => { + if (Set) { + const set = new Set(); + set.add(1); + set.add(2); + expect(func(set)).toEqual([ + [1, 1], + [2, 2], + ]); + } + }); + + it(`\`_.${methodName}\` should convert strings`, () => { + lodashStable.each(['xo', Object('xo')], (string) => { + const actual = lodashStable.sortBy(func(string), 0); + expect(actual).toEqual([ + ['0', 'x'], + ['1', 'o'], + ]); + }); + }); + }); +}); diff --git a/test/toPairs.js b/test/toPairs.js deleted file mode 100644 index 8605aa7bba..0000000000 --- a/test/toPairs.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import entries from '../entries.js'; -import toPairs from '../toPairs.js'; - -describe('toPairs', function() { - it('should be aliased', function() { - assert.strictEqual(entries, toPairs); - }); -}); diff --git a/test/toPairs.spec.js b/test/toPairs.spec.js new file mode 100644 index 0000000000..6192cba8fd --- /dev/null +++ b/test/toPairs.spec.js @@ -0,0 +1,8 @@ +import entries from '../src/entries'; +import toPairs from '../src/toPairs'; + +describe('toPairs', () => { + it('should be aliased', () => { + expect(entries).toBe(toPairs); + }); +}); diff --git a/test/toPairsIn.js b/test/toPairsIn.js deleted file mode 100644 index f35de06d9d..0000000000 --- a/test/toPairsIn.js +++ /dev/null @@ -1,9 +0,0 @@ -import assert from 'assert'; -import entriesIn from '../entriesIn.js'; -import toPairsIn from '../toPairsIn.js'; - -describe('toPairsIn', function() { - it('should be aliased', function() { - assert.strictEqual(entriesIn, toPairsIn); - }); -}); diff --git a/test/toPairsIn.spec.js b/test/toPairsIn.spec.js new file mode 100644 index 0000000000..8ef7bbc122 --- /dev/null +++ b/test/toPairsIn.spec.js @@ -0,0 +1,8 @@ +import entriesIn from '../src/entriesIn'; +import toPairsIn from '../src/toPairsIn'; + +describe('toPairsIn', () => { + it('should be aliased', () => { + expect(entriesIn).toBe(toPairsIn); + }); +}); diff --git a/test/toPath.js b/test/toPath.js deleted file mode 100644 index 361de46570..0000000000 --- a/test/toPath.js +++ /dev/null @@ -1,66 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { symbol } from './utils.js'; -import toPath from '../toPath.js'; - -describe('toPath', function() { - it('should convert a string to a path', function() { - assert.deepStrictEqual(toPath('a.b.c'), ['a', 'b', 'c']); - assert.deepStrictEqual(toPath('a[0].b.c'), ['a', '0', 'b', 'c']); - }); - - it('should coerce array elements to strings', function() { - var array = ['a', 'b', 'c']; - - lodashStable.each([array, lodashStable.map(array, Object)], function(value) { - var actual = toPath(value); - assert.deepStrictEqual(actual, array); - assert.notStrictEqual(actual, array); - }); - }); - - it('should return new path array', function() { - assert.notStrictEqual(toPath('a.b.c'), toPath('a.b.c')); - }); - - it('should not coerce symbols to strings', function() { - if (Symbol) { - var object = Object(symbol); - lodashStable.each([symbol, object, [symbol], [object]], function(value) { - var actual = toPath(value); - assert.ok(lodashStable.isSymbol(actual[0])); - }); - } - }); - - it('should handle complex paths', function() { - var actual = toPath('a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g'); - assert.deepStrictEqual(actual, ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g']); - }); - - it('should handle consecutive empty brackets and dots', function() { - var expected = ['', 'a']; - assert.deepStrictEqual(toPath('.a'), expected); - assert.deepStrictEqual(toPath('[].a'), expected); - - expected = ['', '', 'a']; - assert.deepStrictEqual(toPath('..a'), expected); - assert.deepStrictEqual(toPath('[][].a'), expected); - - expected = ['a', '', 'b']; - assert.deepStrictEqual(toPath('a..b'), expected); - assert.deepStrictEqual(toPath('a[].b'), expected); - - expected = ['a', '', '', 'b']; - assert.deepStrictEqual(toPath('a...b'), expected); - assert.deepStrictEqual(toPath('a[][].b'), expected); - - expected = ['a', '']; - assert.deepStrictEqual(toPath('a.'), expected); - assert.deepStrictEqual(toPath('a[]'), expected); - - expected = ['a', '', '']; - assert.deepStrictEqual(toPath('a..'), expected); - assert.deepStrictEqual(toPath('a[][]'), expected); - }); -}); diff --git a/test/toPath.spec.js b/test/toPath.spec.js new file mode 100644 index 0000000000..f5e80a3583 --- /dev/null +++ b/test/toPath.spec.js @@ -0,0 +1,65 @@ +import lodashStable from 'lodash'; +import { symbol } from './utils'; +import toPath from '../src/toPath'; + +describe('toPath', () => { + it('should convert a string to a path', () => { + expect(toPath('a.b.c'), ['a', 'b').toEqual('c']); + expect(toPath('a[0].b.c'), ['a', '0', 'b').toEqual('c']); + }); + + it('should coerce array elements to strings', () => { + const array = ['a', 'b', 'c']; + + lodashStable.each([array, lodashStable.map(array, Object)], (value) => { + const actual = toPath(value); + expect(actual).toEqual(array); + expect(actual).not.toBe(array); + }); + }); + + it('should return new path array', () => { + expect(toPath('a.b.c')).toBe(toPath('a.b.c')); + }); + + it('should not coerce symbols to strings', () => { + if (Symbol) { + const object = Object(symbol); + lodashStable.each([symbol, object, [symbol], [object]], (value) => { + const actual = toPath(value); + expect(lodashStable.isSymbol(actual[0])) + }); + } + }); + + it('should handle complex paths', () => { + const actual = toPath('a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g'); + expect(actual, ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f').toEqual('g']); + }); + + it('should handle consecutive empty brackets and dots', () => { + let expected = ['', 'a']; + expect(toPath('.a')).toEqual(expected); + expect(toPath('[].a')).toEqual(expected); + + expected = ['', '', 'a']; + expect(toPath('..a')).toEqual(expected); + expect(toPath('[][].a')).toEqual(expected); + + expected = ['a', '', 'b']; + expect(toPath('a..b')).toEqual(expected); + expect(toPath('a[].b')).toEqual(expected); + + expected = ['a', '', '', 'b']; + expect(toPath('a...b')).toEqual(expected); + expect(toPath('a[][].b')).toEqual(expected); + + expected = ['a', '']; + expect(toPath('a.')).toEqual(expected); + expect(toPath('a[]')).toEqual(expected); + + expected = ['a', '', '']; + expect(toPath('a..')).toEqual(expected); + expect(toPath('a[][]')).toEqual(expected); + }); +}); diff --git a/test/toPlainObject.js b/test/toPlainObject.js deleted file mode 100644 index 78046d28f5..0000000000 --- a/test/toPlainObject.js +++ /dev/null @@ -1,30 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { args } from './utils.js'; -import toPlainObject from '../toPlainObject.js'; - -describe('toPlainObject', function() { - it('should flatten inherited string keyed properties', function() { - function Foo() { - this.b = 2; - } - Foo.prototype.c = 3; - - var actual = lodashStable.assign({ 'a': 1 }, toPlainObject(new Foo)); - assert.deepStrictEqual(actual, { 'a': 1, 'b': 2, 'c': 3 }); - }); - - it('should convert `arguments` objects to plain objects', function() { - var actual = toPlainObject(args), - expected = { '0': 1, '1': 2, '2': 3 }; - - assert.deepStrictEqual(actual, expected); - }); - - it('should convert arrays to plain objects', function() { - var actual = toPlainObject(['a', 'b', 'c']), - expected = { '0': 'a', '1': 'b', '2': 'c' }; - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/toPlainObject.spec.js b/test/toPlainObject.spec.js new file mode 100644 index 0000000000..0b5fd2a58a --- /dev/null +++ b/test/toPlainObject.spec.js @@ -0,0 +1,29 @@ +import lodashStable from 'lodash'; +import { args } from './utils'; +import toPlainObject from '../src/toPlainObject'; + +describe('toPlainObject', () => { + it('should flatten inherited string keyed properties', () => { + function Foo() { + this.b = 2; + } + Foo.prototype.c = 3; + + const actual = lodashStable.assign({ a: 1 }, toPlainObject(new Foo())); + expect(actual, { a: 1, b: 2).toEqual(c: 3 }); + }); + + it('should convert `arguments` objects to plain objects', () => { + const actual = toPlainObject(args); + const expected = { 0: 1, 1: 2, 2: 3 }; + + expect(actual).toEqual(expected); + }); + + it('should convert arrays to plain objects', () => { + const actual = toPlainObject(['a', 'b', 'c']); + const expected = { 0: 'a', 1: 'b', 2: 'c' }; + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/toString.spec.js b/test/toString.spec.js new file mode 100644 index 0000000000..147374cc8d --- /dev/null +++ b/test/toString.spec.js @@ -0,0 +1,42 @@ +import lodashStable from 'lodash'; +import { stubString, symbol } from './utils'; +import toString from '../src/toString'; + +describe('toString', () => { + it('should treat nullish values as empty strings', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubString); + + const actual = lodashStable.map(values, (value, index) => + index ? toString(value) : toString(), + ); + + expect(actual).toEqual(expected); + }); + + it('should preserve the sign of `0`', () => { + const values = [-0, Object(-0), 0, Object(0)]; + const expected = ['-0', '-0', '0', '0']; + const actual = lodashStable.map(values, toString); + + expect(actual).toEqual(expected); + }); + + it('should preserve the sign of `0` in an array', () => { + const values = [-0, Object(-0), 0, Object(0)]; + expect(toString(values), '-0,-0,0).toEqual(0'); + }); + + it('should handle symbols', () => { + expect(toString(symbol)).toBe('Symbol(a)'); + }); + + it('should handle an array of symbols', () => { + expect(toString([symbol])).toBe('Symbol(a)'); + }); + + it('should return the `toString` result of the wrapped value', () => { + const wrapped = _([1, 2, 3]); + expect(wrapped.toString(), '1,2).toBe(3'); + }); +}); diff --git a/test/toString.test.js b/test/toString.test.js deleted file mode 100644 index 16e08ab574..0000000000 --- a/test/toString.test.js +++ /dev/null @@ -1,43 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubString, symbol } from './utils.js'; -import toString from '../toString.js'; - -describe('toString', function() { - it('should treat nullish values as empty strings', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubString); - - var actual = lodashStable.map(values, function(value, index) { - return index ? toString(value) : toString(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should preserve the sign of `0`', function() { - var values = [-0, Object(-0), 0, Object(0)], - expected = ['-0', '-0', '0', '0'], - actual = lodashStable.map(values, toString); - - assert.deepStrictEqual(actual, expected); - }); - - it('should preserve the sign of `0` in an array', function() { - var values = [-0, Object(-0), 0, Object(0)]; - assert.deepStrictEqual(toString(values), '-0,-0,0,0'); - }); - - it('should handle symbols', function() { - assert.strictEqual(toString(symbol), 'Symbol(a)'); - }); - - it('should handle an array of symbols', function() { - assert.strictEqual(toString([symbol]), 'Symbol(a)'); - }); - - it('should return the `toString` result of the wrapped value', function() { - var wrapped = _([1, 2, 3]); - assert.strictEqual(wrapped.toString(), '1,2,3'); - }); -}); diff --git a/test/toUpper.js b/test/toUpper.js deleted file mode 100644 index bb6b83729f..0000000000 --- a/test/toUpper.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import toUpper from '../toUpper.js'; - -describe('toUpper', function() { - it('should convert whole string to upper case', function() { - assert.deepStrictEqual(toUpper('--Foo-Bar'), '--FOO-BAR'); - assert.deepStrictEqual(toUpper('fooBar'), 'FOOBAR'); - assert.deepStrictEqual(toUpper('__FOO_BAR__'), '__FOO_BAR__'); - }); -}); diff --git a/test/toUpper.spec.js b/test/toUpper.spec.js new file mode 100644 index 0000000000..fb5fbdc7e3 --- /dev/null +++ b/test/toUpper.spec.js @@ -0,0 +1,9 @@ +import toUpper from '../src/toUpper'; + +describe('toUpper', () => { + it('should convert whole string to upper case', () => { + expect(toUpper('--Foo-Bar')).toEqual('--FOO-BAR'); + expect(toUpper('fooBar')).toEqual('FOOBAR'); + expect(toUpper('__FOO_BAR__')).toEqual('__FOO_BAR__'); + }); +}); diff --git a/test/transform.js b/test/transform.js deleted file mode 100644 index c8121183ab..0000000000 --- a/test/transform.js +++ /dev/null @@ -1,205 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -import { - stubTrue, - square, - typedArrays, - noop, - stubObject, - stubFalse, - falsey, - slice, - realm, -} from './utils.js'; - -import transform from '../transform.js'; - -describe('transform', function() { - function Foo() { - this.a = 1; - this.b = 2; - this.c = 3; - } - - it('should create an object with the same `[[Prototype]]` as `object` when `accumulator` is nullish', function() { - var accumulators = [, null, undefined], - object = new Foo, - expected = lodashStable.map(accumulators, stubTrue); - - var iteratee = function(result, value, key) { - result[key] = square(value); - }; - - var mapper = function(accumulator, index) { - return index ? transform(object, iteratee, accumulator) : transform(object, iteratee); - }; - - var results = lodashStable.map(accumulators, mapper); - - var actual = lodashStable.map(results, function(result) { - return result instanceof Foo; - }); - - assert.deepStrictEqual(actual, expected); - - expected = lodashStable.map(accumulators, lodashStable.constant({ 'a': 1, 'b': 4, 'c': 9 })); - actual = lodashStable.map(results, lodashStable.toPlainObject); - - assert.deepStrictEqual(actual, expected); - - object = { 'a': 1, 'b': 2, 'c': 3 }; - actual = lodashStable.map(accumulators, mapper); - - assert.deepStrictEqual(actual, expected); - - object = [1, 2, 3]; - expected = lodashStable.map(accumulators, lodashStable.constant([1, 4, 9])); - actual = lodashStable.map(accumulators, mapper); - - assert.deepStrictEqual(actual, expected); - }); - - it('should create regular arrays from typed arrays', function() { - var expected = lodashStable.map(typedArrays, stubTrue); - - var actual = lodashStable.map(typedArrays, function(type) { - var Ctor = root[type], - array = Ctor ? new Ctor(new ArrayBuffer(24)) : []; - - return lodashStable.isArray(transform(array, noop)); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should support an `accumulator` value', function() { - var values = [new Foo, [1, 2, 3], { 'a': 1, 'b': 2, 'c': 3 }], - expected = lodashStable.map(values, lodashStable.constant([1, 4, 9])); - - var actual = lodashStable.map(values, function(value) { - return transform(value, function(result, value) { - result.push(square(value)); - }, []); - }); - - assert.deepStrictEqual(actual, expected); - - var object = { 'a': 1, 'b': 4, 'c': 9 }, - expected = [object, { '0': 1, '1': 4, '2': 9 }, object]; - - actual = lodashStable.map(values, function(value) { - return transform(value, function(result, value, key) { - result[key] = square(value); - }, {}); - }); - - assert.deepStrictEqual(actual, expected); - - lodashStable.each([[], {}], function(accumulator) { - var actual = lodashStable.map(values, function(value) { - return transform(value, noop, accumulator); - }); - - assert.ok(lodashStable.every(actual, function(result) { - return result === accumulator; - })); - - assert.strictEqual(transform(null, null, accumulator), accumulator); - }); - }); - - it('should treat sparse arrays as dense', function() { - var actual = transform(Array(1), function(result, value, index) { - result[index] = String(value); - }); - - assert.deepStrictEqual(actual, ['undefined']); - }); - - it('should work without an `iteratee`', function() { - assert.ok(transform(new Foo) instanceof Foo); - }); - - it('should ensure `object` is an object before using its `[[Prototype]]`', function() { - var Ctors = [Boolean, Boolean, Number, Number, Number, String, String], - values = [false, true, 0, 1, NaN, '', 'a'], - expected = lodashStable.map(values, stubObject); - - var results = lodashStable.map(values, function(value) { - return transform(value); - }); - - assert.deepStrictEqual(results, expected); - - expected = lodashStable.map(values, stubFalse); - - var actual = lodashStable.map(results, function(value, index) { - return value instanceof Ctors[index]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should ensure `object` constructor is a function before using its `[[Prototype]]`', function() { - Foo.prototype.constructor = null; - assert.ok(!(transform(new Foo) instanceof Foo)); - Foo.prototype.constructor = Foo; - }); - - it('should create an empty object when given a falsey `object`', function() { - var expected = lodashStable.map(falsey, stubObject); - - var actual = lodashStable.map(falsey, function(object, index) { - return index ? transform(object) : transform(); - }); - - assert.deepStrictEqual(actual, expected); - }); - - lodashStable.each({ - 'array': [1, 2, 3], - 'object': { 'a': 1, 'b': 2, 'c': 3 } - }, - function(object, key) { - it('should provide correct `iteratee` arguments when transforming an ' + key, function() { - var args; - - transform(object, function() { - args || (args = slice.call(arguments)); - }); - - var first = args[0]; - if (key == 'array') { - assert.ok(first !== object && lodashStable.isArray(first)); - assert.deepStrictEqual(args, [first, 1, 0, object]); - } else { - assert.ok(first !== object && lodashStable.isPlainObject(first)); - assert.deepStrictEqual(args, [first, 1, 'a', object]); - } - }); - }); - - it('should create an object from the same realm as `object`', function() { - var objects = lodashStable.filter(realm, function(value) { - return lodashStable.isObject(value) && !lodashStable.isElement(value); - }); - - var expected = lodashStable.map(objects, stubTrue); - - var actual = lodashStable.map(objects, function(object) { - var Ctor = object.constructor, - result = transform(object); - - if (result === object) { - return false; - } - if (lodashStable.isTypedArray(object)) { - return result instanceof Array; - } - return result instanceof Ctor || !(new Ctor instanceof Ctor); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/transform.spec.js b/test/transform.spec.js new file mode 100644 index 0000000000..1920499283 --- /dev/null +++ b/test/transform.spec.js @@ -0,0 +1,205 @@ +import lodashStable from 'lodash'; + +import { + stubTrue, + square, + typedArrays, + noop, + stubObject, + stubFalse, + falsey, + slice, + realm, +} from './utils'; + +import transform from '../src/transform'; + +describe('transform', () => { + function Foo() { + this.a = 1; + this.b = 2; + this.c = 3; + } + + it('should create an object with the same `[[Prototype]]` as `object` when `accumulator` is nullish', () => { + const accumulators = [, null, undefined]; + let object = new Foo(); + let expected = lodashStable.map(accumulators, stubTrue); + + const iteratee = function (result, value, key) { + result[key] = square(value); + }; + + const mapper = function (accumulator, index) { + return index ? transform(object, iteratee, accumulator) : transform(object, iteratee); + }; + + const results = lodashStable.map(accumulators, mapper); + + let actual = lodashStable.map(results, (result) => result instanceof Foo); + + expect(actual).toEqual(expected); + + expected = lodashStable.map(accumulators, lodashStable.constant({ a: 1, b: 4, c: 9 })); + actual = lodashStable.map(results, lodashStable.toPlainObject); + + expect(actual).toEqual(expected); + + object = { a: 1, b: 2, c: 3 }; + actual = lodashStable.map(accumulators, mapper); + + expect(actual).toEqual(expected); + + object = [1, 2, 3]; + expected = lodashStable.map(accumulators, lodashStable.constant([1, 4, 9])); + actual = lodashStable.map(accumulators, mapper); + + expect(actual).toEqual(expected); + }); + + it('should create regular arrays from typed arrays', () => { + const expected = lodashStable.map(typedArrays, stubTrue); + + const actual = lodashStable.map(typedArrays, (type) => { + const Ctor = root[type]; + const array = Ctor ? new Ctor(new ArrayBuffer(24)) : []; + + return lodashStable.isArray(transform(array, noop)); + }); + + expect(actual).toEqual(expected); + }); + + it('should support an `accumulator` value', () => { + const values = [new Foo(), [1, 2, 3], { a: 1, b: 2, c: 3 }]; + var expected = lodashStable.map(values, lodashStable.constant([1, 4, 9])); + + let actual = lodashStable.map(values, (value) => + transform( + value, + (result, value) => { + result.push(square(value)); + }, + [], + ), + ); + + expect(actual).toEqual(expected); + + const object = { a: 1, b: 4, c: 9 }; + var expected = [object, { 0: 1, 1: 4, 2: 9 }, object]; + + actual = lodashStable.map(values, (value) => + transform( + value, + (result, value, key) => { + result[key] = square(value); + }, + {}, + ), + ); + + expect(actual).toEqual(expected); + + lodashStable.each([[], {}], (accumulator) => { + const actual = lodashStable.map(values, (value) => transform(value, noop, accumulator)); + + expect(lodashStable.every(actual, (result) => result === accumulator)) + + expect(transform(null, null, accumulator)).toBe(accumulator); + }); + }); + + it('should treat sparse arrays as dense', () => { + const actual = transform(Array(1), (result, value, index) => { + result[index] = String(value); + }); + + expect(actual).toEqual(['undefined']); + }); + + it('should work without an `iteratee`', () => { + expect(transform(new Foo()) instanceof Foo) + }); + + it('should ensure `object` is an object before using its `[[Prototype]]`', () => { + const Ctors = [Boolean, Boolean, Number, Number, Number, String, String]; + const values = [false, true, 0, 1, NaN, '', 'a']; + let expected = lodashStable.map(values, stubObject); + + const results = lodashStable.map(values, (value) => transform(value)); + + expect(results).toEqual(expected); + + expected = lodashStable.map(values, stubFalse); + + const actual = lodashStable.map(results, (value, index) => value instanceof Ctors[index]); + + expect(actual).toEqual(expected); + }); + + it('should ensure `object` constructor is a function before using its `[[Prototype]]`', () => { + Foo.prototype.constructor = null; + expect((transform(new Foo()) instanceof Foo)).toBe(false) + Foo.prototype.constructor = Foo; + }); + + it('should create an empty object when given a falsey `object`', () => { + const expected = lodashStable.map(falsey, stubObject); + + const actual = lodashStable.map(falsey, (object, index) => + index ? transform(object) : transform(), + ); + + expect(actual).toEqual(expected); + }); + + lodashStable.each( + { + array: [1, 2, 3], + object: { a: 1, b: 2, c: 3 }, + }, + (object, key) => { + it(`should provide correct \`iteratee\` arguments when transforming an ${key}`, () => { + let args; + + transform(object, function () { + args || (args = slice.call(arguments)); + }); + + const first = args[0]; + if (key === 'array') { + expect(first !== object && lodashStable.isArray(first)) + expect(args, [first, 1, 0).toEqual(object]); + } else { + expect(first !== object && lodashStable.isPlainObject(first)) + expect(args, [first, 1, 'a').toEqual(object]); + } + }); + }, + ); + + it('should create an object from the same realm as `object`', () => { + const objects = lodashStable.filter( + realm, + (value) => lodashStable.isObject(value) && !lodashStable.isElement(value), + ); + + const expected = lodashStable.map(objects, stubTrue); + + const actual = lodashStable.map(objects, (object) => { + const Ctor = object.constructor; + const result = transform(object); + + if (result === object) { + return false; + } + if (lodashStable.isTypedArray(object)) { + return result instanceof Array; + } + return result instanceof Ctor || !(new Ctor() instanceof Ctor); + }); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/trim-methods.js b/test/trim-methods.js deleted file mode 100644 index c3f8a845ce..0000000000 --- a/test/trim-methods.js +++ /dev/null @@ -1,83 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, whitespace } from './utils.js'; - -describe('trim methods', function() { - lodashStable.each(['trim', 'trimStart', 'trimEnd'], function(methodName, index) { - var func = _[methodName], - parts = []; - - if (index != 2) { - parts.push('leading'); - } - if (index != 1) { - parts.push('trailing'); - } - parts = parts.join(' and '); - - it('`_.' + methodName + '` should remove ' + parts + ' whitespace', function() { - var string = whitespace + 'a b c' + whitespace, - expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''); - - assert.strictEqual(func(string), expected); - }); - - it('`_.' + methodName + '` should coerce `string` to a string', function() { - var object = { 'toString': lodashStable.constant(whitespace + 'a b c' + whitespace) }, - expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''); - - assert.strictEqual(func(object), expected); - }); - - it('`_.' + methodName + '` should remove ' + parts + ' `chars`', function() { - var string = '-_-a-b-c-_-', - expected = (index == 2 ? '-_-' : '') + 'a-b-c' + (index == 1 ? '-_-' : ''); - - assert.strictEqual(func(string, '_-'), expected); - }); - - it('`_.' + methodName + '` should coerce `chars` to a string', function() { - var object = { 'toString': lodashStable.constant('_-') }, - string = '-_-a-b-c-_-', - expected = (index == 2 ? '-_-' : '') + 'a-b-c' + (index == 1 ? '-_-' : ''); - - assert.strictEqual(func(string, object), expected); - }); - - it('`_.' + methodName + '` should return an empty string for empty values and `chars`', function() { - lodashStable.each([null, '_-'], function(chars) { - assert.strictEqual(func(null, chars), ''); - assert.strictEqual(func(undefined, chars), ''); - assert.strictEqual(func('', chars), ''); - }); - }); - - it('`_.' + methodName + '` should work with `undefined` or empty string values for `chars`', function() { - var string = whitespace + 'a b c' + whitespace, - expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''); - - assert.strictEqual(func(string, undefined), expected); - assert.strictEqual(func(string, ''), string); - }); - - it('`_.' + methodName + '` should work as an iteratee for methods like `_.map`', function() { - var string = Object(whitespace + 'a b c' + whitespace), - trimmed = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''), - actual = lodashStable.map([string, string, string], func); - - assert.deepStrictEqual(actual, [trimmed, trimmed, trimmed]); - }); - - it('`_.' + methodName + '` should return an unwrapped value when implicitly chaining', function() { - var string = whitespace + 'a b c' + whitespace, - expected = (index == 2 ? whitespace : '') + 'a b c' + (index == 1 ? whitespace : ''); - - assert.strictEqual(_(string)[methodName](), expected); - }); - - it('`_.' + methodName + '` should return a wrapped value when explicitly chaining', function() { - var string = whitespace + 'a b c' + whitespace; - assert.ok(_(string).chain()[methodName]() instanceof _); - }); - }); -}); diff --git a/test/trim-methods.spec.js b/test/trim-methods.spec.js new file mode 100644 index 0000000000..133af83670 --- /dev/null +++ b/test/trim-methods.spec.js @@ -0,0 +1,90 @@ +import lodashStable from 'lodash'; +import { _, whitespace } from './utils'; + +describe('trim methods', () => { + lodashStable.each(['trim', 'trimStart', 'trimEnd'], (methodName, index) => { + const func = _[methodName]; + let parts = []; + + if (index !== 2) { + parts.push('leading'); + } + if (index !== 1) { + parts.push('trailing'); + } + parts = parts.join(' and '); + + it(`\`_.${methodName}\` should remove ${parts} whitespace`, () => { + const string = `${whitespace}a b c${whitespace}`; + const expected = `${index === 2 ? whitespace : ''}a b c${ + index === 1 ? whitespace : '' + }`; + + expect(func(string)).toBe(expected); + }); + + it(`\`_.${methodName}\` should coerce \`string\` to a string`, () => { + const object = { toString: lodashStable.constant(`${whitespace}a b c${whitespace}`) }; + const expected = `${index === 2 ? whitespace : ''}a b c${ + index === 1 ? whitespace : '' + }`; + + expect(func(object)).toBe(expected); + }); + + it(`\`_.${methodName}\` should remove ${parts} \`chars\``, () => { + const string = '-_-a-b-c-_-'; + const expected = `${index === 2 ? '-_-' : ''}a-b-c${index === 1 ? '-_-' : ''}`; + + expect(func(string, '_-')).toBe(expected); + }); + + it(`\`_.${methodName}\` should coerce \`chars\` to a string`, () => { + const object = { toString: lodashStable.constant('_-') }; + const string = '-_-a-b-c-_-'; + const expected = `${index === 2 ? '-_-' : ''}a-b-c${index === 1 ? '-_-' : ''}`; + + expect(func(string, object)).toBe(expected); + }); + + it(`\`_.${methodName}\` should return an empty string for empty values and \`chars\``, () => { + lodashStable.each([null, '_-'], (chars) => { + expect(func(null, chars)).toBe(''); + expect(func(undefined, chars)).toBe(''); + expect(func('', chars)).toBe(''); + }); + }); + + it(`\`_.${methodName}\` should work with \`undefined\` or empty string values for \`chars\``, () => { + const string = `${whitespace}a b c${whitespace}`; + const expected = `${index === 2 ? whitespace : ''}a b c${ + index === 1 ? whitespace : '' + }`; + + expect(func(string, undefined)).toBe(expected); + expect(func(string, '')).toBe(string); + }); + + it(`\`_.${methodName}\` should work as an iteratee for methods like \`_.map\``, () => { + const string = Object(`${whitespace}a b c${whitespace}`); + const trimmed = `${index === 2 ? whitespace : ''}a b c${index === 1 ? whitespace : ''}`; + const actual = lodashStable.map([string, string, string], func); + + expect(actual).toEqual([trimmed, trimmed, trimmed]); + }); + + it(`\`_.${methodName}\` should return an unwrapped value when implicitly chaining`, () => { + const string = `${whitespace}a b c${whitespace}`; + const expected = `${index === 2 ? whitespace : ''}a b c${ + index === 1 ? whitespace : '' + }`; + + expect(_(string)[methodName]()).toBe(expected); + }); + + it(`\`_.${methodName}\` should return a wrapped value when explicitly chaining`, () => { + const string = `${whitespace}a b c${whitespace}`; + expect(_(string).chain()[methodName]() instanceof _).toBeTruthy(); + }); + }); +}); diff --git a/test/truncate.js b/test/truncate.js deleted file mode 100644 index 240eae0b96..0000000000 --- a/test/truncate.js +++ /dev/null @@ -1,64 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import truncate from '../truncate.js'; - -describe('truncate', function() { - var string = 'hi-diddly-ho there, neighborino'; - - it('should use a default `length` of `30`', function() { - assert.strictEqual(truncate(string), 'hi-diddly-ho there, neighbo...'); - }); - - it('should not truncate if `string` is <= `length`', function() { - assert.strictEqual(truncate(string, { 'length': string.length }), string); - assert.strictEqual(truncate(string, { 'length': string.length + 2 }), string); - }); - - it('should truncate string the given length', function() { - assert.strictEqual(truncate(string, { 'length': 24 }), 'hi-diddly-ho there, n...'); - }); - - it('should support a `omission` option', function() { - assert.strictEqual(truncate(string, { 'omission': ' [...]' }), 'hi-diddly-ho there, neig [...]'); - }); - - it('should coerce nullish `omission` values to strings', function() { - assert.strictEqual(truncate(string, { 'omission': null }), 'hi-diddly-ho there, neighbnull'); - assert.strictEqual(truncate(string, { 'omission': undefined }), 'hi-diddly-ho there, nundefined'); - }); - - it('should support a `length` option', function() { - assert.strictEqual(truncate(string, { 'length': 4 }), 'h...'); - }); - - it('should support a `separator` option', function() { - assert.strictEqual(truncate(string, { 'length': 24, 'separator': ' ' }), 'hi-diddly-ho there,...'); - assert.strictEqual(truncate(string, { 'length': 24, 'separator': /,? +/ }), 'hi-diddly-ho there...'); - assert.strictEqual(truncate(string, { 'length': 24, 'separator': /,? +/g }), 'hi-diddly-ho there...'); - }); - - it('should treat negative `length` as `0`', function() { - lodashStable.each([0, -2], function(length) { - assert.strictEqual(truncate(string, { 'length': length }), '...'); - }); - }); - - it('should coerce `length` to an integer', function() { - lodashStable.each(['', NaN, 4.6, '4'], function(length, index) { - var actual = index > 1 ? 'h...' : '...'; - assert.strictEqual(truncate(string, { 'length': { 'valueOf': lodashStable.constant(length) } }), actual); - }); - }); - - it('should coerce `string` to a string', function() { - assert.strictEqual(truncate(Object(string), { 'length': 4 }), 'h...'); - assert.strictEqual(truncate({ 'toString': lodashStable.constant(string) }, { 'length': 5 }), 'hi...'); - }); - - it('should work as an iteratee for methods like `_.map`', function() { - var actual = lodashStable.map([string, string, string], truncate), - truncated = 'hi-diddly-ho there, neighbo...'; - - assert.deepStrictEqual(actual, [truncated, truncated, truncated]); - }); -}); diff --git a/test/truncate.spec.js b/test/truncate.spec.js new file mode 100644 index 0000000000..0df0c6a155 --- /dev/null +++ b/test/truncate.spec.js @@ -0,0 +1,65 @@ +import lodashStable from 'lodash'; +import truncate from '../src/truncate'; + +describe('truncate', () => { + const string = 'hi-diddly-ho there, neighborino'; + + it('should use a default `length` of `30`', () => { + expect(truncate(string), 'hi-diddly-ho there).toBe(neighbo...'); + }); + + it('should not truncate if `string` is <= `length`', () => { + expect(truncate(string, { length: string.length })).toBe(string); + expect(truncate(string, { length: string.length + 2 })).toBe(string); + }); + + it('should truncate string the given length', () => { + expect(truncate(string, { length: 24 }), 'hi-diddly-ho there).toBe(n...'); + }); + + it('should support a `omission` option', () => { + expect(truncate(string, { omission: ' [...]' })).toBe('hi-diddly-ho there, neig [...]'); + }); + + it('should coerce nullish `omission` values to strings', () => { + expect(truncate(string, { omission: null }), 'hi-diddly-ho there).toBe(neighbnull'); + expect(truncate(string, { omission: undefined })).toBe('hi-diddly-ho there, nundefined'); + }); + + it('should support a `length` option', () => { + expect(truncate(string, { length: 4 })).toBe('h...'); + }); + + it('should support a `separator` option', () => { + expect(truncate(string, { length: 24, separator: ' ' })).toBe('hi-diddly-ho there,...'); + expect(truncate(string, { length: 24, separator: /,? +/ })).toBe('hi-diddly-ho there...'); + expect(truncate(string, { length: 24, separator: /,? +/g })).toBe('hi-diddly-ho there...'); + }); + + it('should treat negative `length` as `0`', () => { + lodashStable.each([0, -2], (length) => { + expect(truncate(string, { length: length })).toBe('...'); + }); + }); + + it('should coerce `length` to an integer', () => { + lodashStable.each(['', NaN, 4.6, '4'], (length, index) => { + const actual = index > 1 ? 'h...' : '...'; + expect(truncate(string, { length: { valueOf: lodashStable.constant(length) } })).toBe( + actual, + ); + }); + }); + + it('should coerce `string` to a string', () => { + expect(truncate(Object(string), { length: 4 })).toBe('h...'); + expect(truncate({ toString: lodashStable.constant(string) }, { length: 5 })).toBe('hi...'); + }); + + it('should work as an iteratee for methods like `_.map`', () => { + const actual = lodashStable.map([string, string, string], truncate); + const truncated = 'hi-diddly-ho there, neighbo...'; + + expect(actual).toEqual([truncated, truncated, truncated]); + }); +}); diff --git a/test/unary.js b/test/unary.js deleted file mode 100644 index 333c89091b..0000000000 --- a/test/unary.js +++ /dev/null @@ -1,27 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice } from './utils.js'; -import unary from '../unary.js'; - -describe('unary', function() { - function fn() { - return slice.call(arguments); - } - - it('should cap the number of arguments provided to `func`', function() { - var actual = lodashStable.map(['6', '8', '10'], unary(parseInt)); - assert.deepStrictEqual(actual, [6, 8, 10]); - }); - - it('should not force a minimum argument count', function() { - var capped = unary(fn); - assert.deepStrictEqual(capped(), []); - }); - - it('should use `this` binding of function', function() { - var capped = unary(function(a, b) { return this; }), - object = { 'capped': capped }; - - assert.strictEqual(object.capped(), object); - }); -}); diff --git a/test/unary.spec.js b/test/unary.spec.js new file mode 100644 index 0000000000..2a90c9c5e9 --- /dev/null +++ b/test/unary.spec.js @@ -0,0 +1,28 @@ +import lodashStable from 'lodash'; +import { slice } from './utils'; +import unary from '../src/unary'; + +describe('unary', () => { + function fn() { + return slice.call(arguments); + } + + it('should cap the number of arguments provided to `func`', () => { + const actual = lodashStable.map(['6', '8', '10'], unary(parseInt)); + expect(actual).toEqual([6, 8, 10]); + }); + + it('should not force a minimum argument count', () => { + const capped = unary(fn); + expect(capped()).toEqual([]); + }); + + it('should use `this` binding of function', () => { + const capped = unary(function (a, b) { + return this; + }); + const object = { capped: capped }; + + expect(object.capped()).toBe(object); + }); +}); diff --git a/test/uncommon-symbols.js b/test/uncommon-symbols.js deleted file mode 100644 index f108be7931..0000000000 --- a/test/uncommon-symbols.js +++ /dev/null @@ -1,170 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { emojiVar, comboMarks, fitzModifiers } from './utils.js'; -import repeat from '../repeat.js'; -import camelCase from '../camelCase.js'; -import capitalize from '../capitalize.js'; -import pad from '../pad.js'; -import padStart from '../padStart.js'; -import padEnd from '../padEnd.js'; -import size from '../size.js'; -import split from '../split.js'; -import toArray from '../toArray.js'; -import trim from '../trim.js'; -import trimStart from '../trimStart.js'; -import trimEnd from '../trimEnd.js'; -import truncate from '../truncate.js'; -import words from '../words.js'; - -describe('uncommon symbols', function() { - var flag = '\ud83c\uddfa\ud83c\uddf8', - heart = '\u2764' + emojiVar, - hearts = '\ud83d\udc95', - comboGlyph = '\ud83d\udc68\u200d' + heart + '\u200d\ud83d\udc8B\u200d\ud83d\udc68', - hashKeycap = '#' + emojiVar + '\u20e3', - leafs = '\ud83c\udf42', - mic = '\ud83c\udf99', - noMic = mic + '\u20e0', - raisedHand = '\u270B' + emojiVar, - rocket = '\ud83d\ude80', - thumbsUp = '\ud83d\udc4d'; - - it('should account for astral symbols', function() { - var allHearts = repeat(hearts, 10), - chars = hearts + comboGlyph, - string = 'A ' + leafs + ', ' + comboGlyph + ', and ' + rocket, - trimChars = comboGlyph + hearts, - trimString = trimChars + string + trimChars; - - assert.strictEqual(camelCase(hearts + ' the ' + leafs), hearts + 'The' + leafs); - assert.strictEqual(camelCase(string), 'a' + leafs + comboGlyph + 'And' + rocket); - assert.strictEqual(capitalize(rocket), rocket); - - assert.strictEqual(pad(string, 16), ' ' + string + ' '); - assert.strictEqual(padStart(string, 16), ' ' + string); - assert.strictEqual(padEnd(string, 16), string + ' '); - - assert.strictEqual(pad(string, 16, chars), hearts + string + chars); - assert.strictEqual(padStart(string, 16, chars), chars + hearts + string); - assert.strictEqual(padEnd(string, 16, chars), string + chars + hearts); - - assert.strictEqual(size(string), 13); - assert.deepStrictEqual(split(string, ' '), ['A', leafs + ',', comboGlyph + ',', 'and', rocket]); - assert.deepStrictEqual(split(string, ' ', 3), ['A', leafs + ',', comboGlyph + ',']); - assert.deepStrictEqual(split(string, undefined), [string]); - assert.deepStrictEqual(split(string, undefined, -1), [string]); - assert.deepStrictEqual(split(string, undefined, 0), []); - - var expected = ['A', ' ', leafs, ',', ' ', comboGlyph, ',', ' ', 'a', 'n', 'd', ' ', rocket]; - - assert.deepStrictEqual(split(string, ''), expected); - assert.deepStrictEqual(split(string, '', 6), expected.slice(0, 6)); - assert.deepStrictEqual(toArray(string), expected); - - assert.strictEqual(trim(trimString, chars), string); - assert.strictEqual(trimStart(trimString, chars), string + trimChars); - assert.strictEqual(trimEnd(trimString, chars), trimChars + string); - - assert.strictEqual(truncate(string, { 'length': 13 }), string); - assert.strictEqual(truncate(string, { 'length': 6 }), 'A ' + leafs + '...'); - - assert.deepStrictEqual(words(string), ['A', leafs, comboGlyph, 'and', rocket]); - assert.deepStrictEqual(toArray(hashKeycap), [hashKeycap]); - assert.deepStrictEqual(toArray(noMic), [noMic]); - - lodashStable.times(2, function(index) { - var separator = index ? RegExp(hearts) : hearts, - options = { 'length': 4, 'separator': separator }, - actual = truncate(string, options); - - assert.strictEqual(actual, 'A...'); - assert.strictEqual(actual.length, 4); - - actual = truncate(allHearts, options); - assert.strictEqual(actual, hearts + '...'); - assert.strictEqual(actual.length, 5); - }); - }); - - it('should account for combining diacritical marks', function() { - var values = lodashStable.map(comboMarks, function(mark) { - return 'o' + mark; - }); - - var expected = lodashStable.map(values, function(value) { - return [1, [value], [value]]; - }); - - var actual = lodashStable.map(values, function(value) { - return [size(value), toArray(value), words(value)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should account for fitzpatrick modifiers', function() { - var values = lodashStable.map(fitzModifiers, function(modifier) { - return thumbsUp + modifier; - }); - - var expected = lodashStable.map(values, function(value) { - return [1, [value], [value]]; - }); - - var actual = lodashStable.map(values, function(value) { - return [size(value), toArray(value), words(value)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should account for regional symbols', function() { - var pair = flag.match(/\ud83c[\udde6-\uddff]/g), - regionals = pair.join(' '); - - assert.strictEqual(size(flag), 1); - assert.strictEqual(size(regionals), 3); - - assert.deepStrictEqual(toArray(flag), [flag]); - assert.deepStrictEqual(toArray(regionals), [pair[0], ' ', pair[1]]); - - assert.deepStrictEqual(words(flag), [flag]); - assert.deepStrictEqual(words(regionals), [pair[0], pair[1]]); - }); - - it('should account for variation selectors', function() { - assert.strictEqual(size(heart), 1); - assert.deepStrictEqual(toArray(heart), [heart]); - assert.deepStrictEqual(words(heart), [heart]); - }); - - it('should account for variation selectors with fitzpatrick modifiers', function() { - var values = lodashStable.map(fitzModifiers, function(modifier) { - return raisedHand + modifier; - }); - - var expected = lodashStable.map(values, function(value) { - return [1, [value], [value]]; - }); - - var actual = lodashStable.map(values, function(value) { - return [size(value), toArray(value), words(value)]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should match lone surrogates', function() { - var pair = hearts.split(''), - surrogates = pair[0] + ' ' + pair[1]; - - assert.strictEqual(size(surrogates), 3); - assert.deepStrictEqual(toArray(surrogates), [pair[0], ' ', pair[1]]); - assert.deepStrictEqual(words(surrogates), []); - }); - - it('should match side by side fitzpatrick modifiers separately ', function() { - var string = fitzModifiers[0] + fitzModifiers[0]; - assert.deepStrictEqual(toArray(string), [fitzModifiers[0], fitzModifiers[0]]); - }); -}); diff --git a/test/uncommon-symbols.spec.js b/test/uncommon-symbols.spec.js new file mode 100644 index 0000000000..259379c64b --- /dev/null +++ b/test/uncommon-symbols.spec.js @@ -0,0 +1,177 @@ +import lodashStable from 'lodash'; +import { emojiVar, comboMarks, fitzModifiers } from './utils'; +import repeat from '../src/repeat'; +import camelCase from '../src/camelCase'; +import capitalize from '../src/capitalize'; +import pad from '../src/pad'; +import padStart from '../src/padStart'; +import padEnd from '../src/padEnd'; +import size from '../src/size'; +import split from '../src/split'; +import toArray from '../src/toArray'; +import trim from '../src/trim'; +import trimStart from '../src/trimStart'; +import trimEnd from '../src/trimEnd'; +import truncate from '../src/truncate'; +import words from '../src/words'; + +describe('uncommon symbols', () => { + const flag = '\ud83c\uddfa\ud83c\uddf8'; + const heart = `\u2764${emojiVar}`; + const hearts = '\ud83d\udc95'; + const comboGlyph = `\ud83d\udc68\u200d${heart}\u200d\ud83d\udc8B\u200d\ud83d\udc68`; + const hashKeycap = `#${emojiVar}\u20e3`; + const leafs = '\ud83c\udf42'; + const mic = '\ud83c\udf99'; + const noMic = `${mic}\u20e0`; + const raisedHand = `\u270B${emojiVar}`; + const rocket = '\ud83d\ude80'; + const thumbsUp = '\ud83d\udc4d'; + + it('should account for astral symbols', () => { + const allHearts = repeat(hearts, 10); + const chars = hearts + comboGlyph; + const string = `A ${leafs}, ${comboGlyph}, and ${rocket}`; + const trimChars = comboGlyph + hearts; + const trimString = trimChars + string + trimChars; + + expect(camelCase(`${hearts} the ${leafs}`)).toBe(`${hearts}The${leafs}`); + expect(camelCase(string)).toBe(`a${leafs}${comboGlyph}And${rocket}`); + expect(capitalize(rocket)).toBe(rocket); + + expect(pad(string, 16)).toBe(` ${string} `); + expect(padStart(string, 16)).toBe(` ${string}`); + expect(padEnd(string, 16)).toBe(`${string} `); + + expect(pad(string, 16, chars)).toBe(hearts + string + chars); + expect(padStart(string, 16, chars)).toBe(chars + hearts + string); + expect(padEnd(string, 16, chars)).toBe(string + chars + hearts); + + expect(size(string)).toBe(13); + expect(split(string, ' ')).toEqual(['A', `${leafs},`, `${comboGlyph},`, 'and', rocket]); + expect(split(string, ' ', 3), ['A', `${leafs},`, `${comboGlyph}).toEqual(`]); + expect(split(string, undefined)).toEqual([string]); + expect(split(string, undefined, -1)).toEqual([string]); + expect(split(string, undefined, 0)).toEqual([]); + + const expected = [ + 'A', + ' ', + leafs, + ',', + ' ', + comboGlyph, + ',', + ' ', + 'a', + 'n', + 'd', + ' ', + rocket, + ]; + + expect(split(string, '')).toEqual(expected); + expect(split(string, '', 6), expected.slice(0).toEqual(6)); + expect(toArray(string)).toEqual(expected); + + expect(trim(trimString, chars)).toBe(string); + expect(trimStart(trimString, chars)).toBe(string + trimChars); + expect(trimEnd(trimString, chars)).toBe(trimChars + string); + + expect(truncate(string, { length: 13 })).toBe(string); + expect(truncate(string, { length: 6 })).toBe(`A ${leafs}...`); + + expect(words(string)).toEqual(['A', leafs, comboGlyph, 'and', rocket]); + expect(toArray(hashKeycap)).toEqual([hashKeycap]); + expect(toArray(noMic)).toEqual([noMic]); + + lodashStable.times(2, (index) => { + const separator = index ? RegExp(hearts) : hearts; + const options = { length: 4, separator: separator }; + let actual = truncate(string, options); + + expect(actual).toBe('A...'); + expect(actual.length).toBe(4); + + actual = truncate(allHearts, options); + expect(actual).toBe(`${hearts}...`); + expect(actual.length).toBe(5); + }); + }); + + it('should account for combining diacritical marks', () => { + const values = lodashStable.map(comboMarks, (mark) => `o${mark}`); + + const expected = lodashStable.map(values, (value) => [1, [value], [value]]); + + const actual = lodashStable.map(values, (value) => [ + size(value), + toArray(value), + words(value), + ]); + + expect(actual).toEqual(expected); + }); + + it('should account for fitzpatrick modifiers', () => { + const values = lodashStable.map(fitzModifiers, (modifier) => thumbsUp + modifier); + + const expected = lodashStable.map(values, (value) => [1, [value], [value]]); + + const actual = lodashStable.map(values, (value) => [ + size(value), + toArray(value), + words(value), + ]); + + expect(actual).toEqual(expected); + }); + + it('should account for regional symbols', () => { + const pair = flag.match(/\ud83c[\udde6-\uddff]/g); + const regionals = pair.join(' '); + + expect(size(flag)).toBe(1); + expect(size(regionals)).toBe(3); + + expect(toArray(flag)).toEqual([flag]); + expect(toArray(regionals)).toEqual([pair[0], ' ', pair[1]]); + + expect(words(flag)).toEqual([flag]); + expect(words(regionals)).toEqual([pair[0], pair[1]]); + }); + + it('should account for variation selectors', () => { + expect(size(heart)).toBe(1); + expect(toArray(heart)).toEqual([heart]); + expect(words(heart)).toEqual([heart]); + }); + + it('should account for variation selectors with fitzpatrick modifiers', () => { + const values = lodashStable.map(fitzModifiers, (modifier) => raisedHand + modifier); + + const expected = lodashStable.map(values, (value) => [1, [value], [value]]); + + const actual = lodashStable.map(values, (value) => [ + size(value), + toArray(value), + words(value), + ]); + + expect(actual).toEqual(expected); + }); + + it('should match lone surrogates', () => { + const pair = hearts.split(''); + const surrogates = `${pair[0]} ${pair[1]}`; + + expect(size(surrogates)).toBe(3); + expect(toArray(surrogates)).toEqual([pair[0], ' ', pair[1]]); + expect(words(surrogates)).toEqual([]); + }); + + it('should match side by side fitzpatrick modifiers separately ', () => { + const string = fitzModifiers[0] + fitzModifiers[0]; + expect(toArray(string)).toEqual([fitzModifiers[0], fitzModifiers[0]]); + }); +}); diff --git a/test/unescape.js b/test/unescape.js deleted file mode 100644 index 7bd56137f7..0000000000 --- a/test/unescape.js +++ /dev/null @@ -1,40 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import unescape from '../unescape.js'; -import escape from '../escape.js'; - -describe('unescape', function() { - var escaped = '&<>"'/', - unescaped = '&<>"\'/'; - - escaped += escaped; - unescaped += unescaped; - - it('should unescape entities in order', function() { - assert.strictEqual(unescape('&lt;'), '<'); - }); - - it('should unescape the proper entities', function() { - assert.strictEqual(unescape(escaped), unescaped); - }); - - it('should handle strings with nothing to unescape', function() { - assert.strictEqual(unescape('abc'), 'abc'); - }); - - it('should unescape the same characters escaped by `_.escape`', function() { - assert.strictEqual(unescape(escape(unescaped)), unescaped); - }); - - it('should handle leading zeros in html entities', function() { - assert.strictEqual(unescape('''), "'"); - assert.strictEqual(unescape('''), "'"); - assert.strictEqual(unescape('''), "'"); - }); - - lodashStable.each(['`', '/'], function(entity) { - it('should not unescape the "' + entity + '" entity', function() { - assert.strictEqual(unescape(entity), entity); - }); - }); -}); diff --git a/test/unescape.spec.js b/test/unescape.spec.js new file mode 100644 index 0000000000..b2116ae67d --- /dev/null +++ b/test/unescape.spec.js @@ -0,0 +1,39 @@ +import lodashStable from 'lodash'; +import unescape from '../src/unescape'; +import escape from '../src/escape'; + +describe('unescape', () => { + let escaped = '&<>"'/'; + let unescaped = '&<>"\'/'; + + escaped += escaped; + unescaped += unescaped; + + it('should unescape entities in order', () => { + expect(unescape('&lt;')).toBe('<'); + }); + + it('should unescape the proper entities', () => { + expect(unescape(escaped)).toBe(unescaped); + }); + + it('should handle strings with nothing to unescape', () => { + expect(unescape('abc')).toBe('abc'); + }); + + it('should unescape the same characters escaped by `_.escape`', () => { + expect(unescape(escape(unescaped))).toBe(unescaped); + }); + + it('should handle leading zeros in html entities', () => { + expect(unescape(''')).toBe("'"); + expect(unescape(''')).toBe("'"); + expect(unescape(''')).toBe("'"); + }); + + lodashStable.each(['`', '/'], (entity) => { + it(`should not unescape the "${entity}" entity`, () => { + expect(unescape(entity)).toBe(entity); + }); + }); +}); diff --git a/test/union-methods.js b/test/union-methods.js deleted file mode 100644 index fc872e278e..0000000000 --- a/test/union-methods.js +++ /dev/null @@ -1,31 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, args } from './utils.js'; - -describe('union methods', function() { - lodashStable.each(['union', 'unionBy', 'unionWith'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should return the union of two arrays', function() { - var actual = func([2], [1, 2]); - assert.deepStrictEqual(actual, [2, 1]); - }); - - it('`_.' + methodName + '` should return the union of multiple arrays', function() { - var actual = func([2], [1, 2], [2, 3]); - assert.deepStrictEqual(actual, [2, 1, 3]); - }); - - it('`_.' + methodName + '` should not flatten nested arrays', function() { - var actual = func([1, 3, 2], [1, [5]], [2, [4]]); - assert.deepStrictEqual(actual, [1, 3, 2, [5], [4]]); - }); - - it('`_.' + methodName + '` should ignore values that are not arrays or `arguments` objects', function() { - var array = [0]; - assert.deepStrictEqual(func(array, 3, { '0': 1 }, null), array); - assert.deepStrictEqual(func(null, array, null, [2, 1]), [0, 2, 1]); - assert.deepStrictEqual(func(array, null, args, null), [0, 1, 2, 3]); - }); - }); -}); diff --git a/test/union-methods.spec.js b/test/union-methods.spec.js new file mode 100644 index 0000000000..da40683dc7 --- /dev/null +++ b/test/union-methods.spec.js @@ -0,0 +1,30 @@ +import lodashStable from 'lodash'; +import { _, args } from './utils'; + +describe('union methods', () => { + lodashStable.each(['union', 'unionBy', 'unionWith'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should return the union of two arrays`, () => { + const actual = func([2], [1, 2]); + expect(actual, [2).toEqual(1]); + }); + + it(`\`_.${methodName}\` should return the union of multiple arrays`, () => { + const actual = func([2], [1, 2], [2, 3]); + expect(actual, [2, 1).toEqual(3]); + }); + + it(`\`_.${methodName}\` should not flatten nested arrays`, () => { + const actual = func([1, 3, 2], [1, [5]], [2, [4]]); + expect(actual, [1, 3, 2, [5]).toEqual([4]]); + }); + + it(`\`_.${methodName}\` should ignore values that are not arrays or \`arguments\` objects`, () => { + const array = [0]; + expect(func(array, 3, { 0: 1 }, null)).toEqual(array); + expect(func(null, array, null, [2, 1]), [0, 2).toEqual(1]); + expect(func(array, null, args, null), [0, 1, 2).toEqual(3]); + }); + }); +}); diff --git a/test/unionBy.js b/test/unionBy.js deleted file mode 100644 index 9dd5f8e55c..0000000000 --- a/test/unionBy.js +++ /dev/null @@ -1,28 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import unionBy from '../unionBy.js'; - -describe('unionBy', function() { - it('should accept an `iteratee`', function() { - var actual = unionBy([2.1], [1.2, 2.3], Math.floor); - assert.deepStrictEqual(actual, [2.1, 1.2]); - - actual = unionBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - assert.deepStrictEqual(actual, [{ 'x': 1 }, { 'x': 2 }]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - unionBy([2.1], [1.2, 2.3], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [2.1]); - }); - - it('should output values from the first possible array', function() { - var actual = unionBy([{ 'x': 1, 'y': 1 }], [{ 'x': 1, 'y': 2 }], 'x'); - assert.deepStrictEqual(actual, [{ 'x': 1, 'y': 1 }]); - }); -}); diff --git a/test/unionBy.spec.js b/test/unionBy.spec.js new file mode 100644 index 0000000000..2c8294b31c --- /dev/null +++ b/test/unionBy.spec.js @@ -0,0 +1,31 @@ +import { slice } from './utils'; +import unionBy from '../src/unionBy'; + +describe('unionBy', () => { + // FIXME: Work out path as function. + // + // it('should accept an `iteratee`', () => { + // let actual = unionBy([2.1], [1.2, 2.3], Math.floor); + // expect(actual).toEqual([2.1, 1.2]); + // + // actual = unionBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x'); + // expect(actual).toEqual([{ x: 1 }, { x: 2 }]); + // }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + unionBy([2.1], [1.2, 2.3], function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([2.1]); + }); + + // FIXME: Work out path as function. + // + // it('should output values from the first possible array', () => { + // const actual = unionBy([{ x: 1, y: 1 }], [{ x: 1, y: 2 }], 'x'); + // expect(actual).toEqual([{ x: 1, y: 1 }]); + // }); +}); diff --git a/test/unionWith.spec.js b/test/unionWith.spec.js new file mode 100644 index 0000000000..f7e39f9179 --- /dev/null +++ b/test/unionWith.spec.js @@ -0,0 +1,27 @@ +import lodashStable from 'lodash'; +import unionWith from '../src/unionWith'; + +describe('unionWith', () => { + it('should work with a `comparator`', () => { + const objects = [ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + ]; + const others = [ + { x: 1, y: 1 }, + { x: 1, y: 2 }, + ]; + const actual = unionWith(objects, others, lodashStable.isEqual); + + expect(actual).toEqual([objects[0], objects[1], others[0]]); + }); + + it('should output values from the first possible array', () => { + const objects = [{ x: 1, y: 1 }]; + const others = [{ x: 1, y: 2 }]; + + const actual = unionWith(objects, others, (a, b) => a.x === b.x); + + expect(actual).toEqual([{ x: 1, y: 1 }]); + }); +}); diff --git a/test/unionWith.test.js b/test/unionWith.test.js deleted file mode 100644 index e5f4d9664b..0000000000 --- a/test/unionWith.test.js +++ /dev/null @@ -1,24 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import unionWith from '../unionWith.js'; - -describe('unionWith', function() { - it('should work with a `comparator`', function() { - var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }], - others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }], - actual = unionWith(objects, others, lodashStable.isEqual); - - assert.deepStrictEqual(actual, [objects[0], objects[1], others[0]]); - }); - - it('should output values from the first possible array', function() { - var objects = [{ 'x': 1, 'y': 1 }], - others = [{ 'x': 1, 'y': 2 }]; - - var actual = unionWith(objects, others, function(a, b) { - return a.x == b.x; - }); - - assert.deepStrictEqual(actual, [{ 'x': 1, 'y': 1 }]); - }); -}); diff --git a/test/uniq-methods.js b/test/uniq-methods.js deleted file mode 100644 index 6949a311fb..0000000000 --- a/test/uniq-methods.js +++ /dev/null @@ -1,123 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import sortBy from '../sortBy.js'; - -describe('uniq methods', function() { - lodashStable.each(['uniq', 'uniqBy', 'uniqWith', 'sortedUniq', 'sortedUniqBy'], function(methodName) { - var func = _[methodName], - isSorted = /^sorted/.test(methodName), - objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }, { 'a': 2 }, { 'a': 3 }, { 'a': 1 }]; - - if (isSorted) { - objects = sortBy(objects, 'a'); - } - else { - it('`_.' + methodName + '` should return unique values of an unsorted array', function() { - var array = [2, 1, 2]; - assert.deepStrictEqual(func(array), [2, 1]); - }); - } - it('`_.' + methodName + '` should return unique values of a sorted array', function() { - var array = [1, 2, 2]; - assert.deepStrictEqual(func(array), [1, 2]); - }); - - it('`_.' + methodName + '` should treat object instances as unique', function() { - assert.deepStrictEqual(func(objects), objects); - }); - - it('`_.' + methodName + '` should treat `-0` as `0`', function() { - var actual = lodashStable.map(func([-0, 0]), lodashStable.toString); - assert.deepStrictEqual(actual, ['0']); - }); - - it('`_.' + methodName + '` should match `NaN`', function() { - assert.deepStrictEqual(func([NaN, NaN]), [NaN]); - }); - - it('`_.' + methodName + '` should work with large arrays', function() { - var largeArray = [], - expected = [0, {}, 'a'], - count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); - - lodashStable.each(expected, function(value) { - lodashStable.times(count, function() { - largeArray.push(value); - }); - }); - - assert.deepStrictEqual(func(largeArray), expected); - }); - - it('`_.' + methodName + '` should work with large arrays of `-0` as `0`', function() { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, function(index) { - return isEven(index) ? -0 : 0; - }); - - var actual = lodashStable.map(func(largeArray), lodashStable.toString); - assert.deepStrictEqual(actual, ['0']); - }); - - it('`_.' + methodName + '` should work with large arrays of boolean, `NaN`, and nullish values', function() { - var largeArray = [], - expected = [null, undefined, false, true, NaN], - count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); - - lodashStable.each(expected, function(value) { - lodashStable.times(count, function() { - largeArray.push(value); - }); - }); - - assert.deepStrictEqual(func(largeArray), expected); - }); - - it('`_.' + methodName + '` should work with large arrays of symbols', function() { - if (Symbol) { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, Symbol); - assert.deepStrictEqual(func(largeArray), largeArray); - } - }); - - it('`_.' + methodName + '` should work with large arrays of well-known symbols', function() { - // See http://www.ecma-international.org/ecma-262/6.0/#sec-well-known-symbols. - if (Symbol) { - var expected = [ - Symbol.hasInstance, Symbol.isConcatSpreadable, Symbol.iterator, - Symbol.match, Symbol.replace, Symbol.search, Symbol.species, - Symbol.split, Symbol.toPrimitive, Symbol.toStringTag, Symbol.unscopables - ]; - - var largeArray = [], - count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); - - expected = lodashStable.map(expected, function(symbol) { - return symbol || {}; - }); - - lodashStable.each(expected, function(value) { - lodashStable.times(count, function() { - largeArray.push(value); - }); - }); - - assert.deepStrictEqual(func(largeArray), expected); - } - }); - - it('`_.' + methodName + '` should distinguish between numbers and numeric strings', function() { - var largeArray = [], - expected = ['2', 2, Object('2'), Object(2)], - count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); - - lodashStable.each(expected, function(value) { - lodashStable.times(count, function() { - largeArray.push(value); - }); - }); - - assert.deepStrictEqual(func(largeArray), expected); - }); - }); -}); diff --git a/test/uniq-methods.spec.js b/test/uniq-methods.spec.js new file mode 100644 index 0000000000..5dd8485466 --- /dev/null +++ b/test/uniq-methods.spec.js @@ -0,0 +1,130 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, isEven } from './utils'; +import sortBy from '../src/sortBy'; + +describe('uniq methods', () => { + lodashStable.each( + ['uniq', 'uniqBy', 'uniqWith', 'sortedUniq', 'sortedUniqBy'], + (methodName) => { + const func = _[methodName]; + const isSorted = /^sorted/.test(methodName); + let objects = [{ a: 2 }, { a: 3 }, { a: 1 }, { a: 2 }, { a: 3 }, { a: 1 }]; + + if (isSorted) { + objects = sortBy(objects, 'a'); + } else { + it(`\`_.${methodName}\` should return unique values of an unsorted array`, () => { + const array = [2, 1, 2]; + expect(func(array), [2).toEqual(1]); + }); + } + it(`\`_.${methodName}\` should return unique values of a sorted array`, () => { + const array = [1, 2, 2]; + expect(func(array), [1).toEqual(2]); + }); + + it(`\`_.${methodName}\` should treat object instances as unique`, () => { + expect(func(objects)).toEqual(objects); + }); + + it(`\`_.${methodName}\` should treat \`-0\` as \`0\``, () => { + const actual = lodashStable.map(func([-0, 0]), lodashStable.toString); + expect(actual).toEqual(['0']); + }); + + it(`\`_.${methodName}\` should match \`NaN\``, () => { + expect(func([NaN, NaN])).toEqual([NaN]); + }); + + it(`\`_.${methodName}\` should work with large arrays`, () => { + const largeArray = []; + const expected = [0, {}, 'a']; + const count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); + + lodashStable.each(expected, (value) => { + lodashStable.times(count, () => { + largeArray.push(value); + }); + }); + + expect(func(largeArray)).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with large arrays of \`-0\` as \`0\``, () => { + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, (index) => + isEven(index) ? -0 : 0, + ); + + const actual = lodashStable.map(func(largeArray), lodashStable.toString); + expect(actual).toEqual(['0']); + }); + + it(`\`_.${methodName}\` should work with large arrays of boolean, \`NaN\`, and nullish values`, () => { + const largeArray = []; + const expected = [null, undefined, false, true, NaN]; + const count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); + + lodashStable.each(expected, (value) => { + lodashStable.times(count, () => { + largeArray.push(value); + }); + }); + + expect(func(largeArray)).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with large arrays of symbols`, () => { + if (Symbol) { + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, Symbol); + expect(func(largeArray)).toEqual(largeArray); + } + }); + + it(`\`_.${methodName}\` should work with large arrays of well-known symbols`, () => { + // See http://www.ecma-international.org/ecma-262/6.0/#sec-well-known-symbols. + if (Symbol) { + let expected = [ + Symbol.hasInstance, + Symbol.isConcatSpreadable, + Symbol.iterator, + Symbol.match, + Symbol.replace, + Symbol.search, + Symbol.species, + Symbol.split, + Symbol.toPrimitive, + Symbol.toStringTag, + Symbol.unscopables, + ]; + + const largeArray = []; + const count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); + + expected = lodashStable.map(expected, (symbol) => symbol || {}); + + lodashStable.each(expected, (value) => { + lodashStable.times(count, () => { + largeArray.push(value); + }); + }); + + expect(func(largeArray)).toEqual(expected); + } + }); + + it(`\`_.${methodName}\` should distinguish between numbers and numeric strings`, () => { + const largeArray = []; + const expected = ['2', 2, Object('2'), Object(2)]; + const count = Math.ceil(LARGE_ARRAY_SIZE / expected.length); + + lodashStable.each(expected, (value) => { + lodashStable.times(count, () => { + largeArray.push(value); + }); + }); + + expect(func(largeArray)).toEqual(expected); + }); + }, + ); +}); diff --git a/test/uniq.js b/test/uniq.js deleted file mode 100644 index 5eaf74c418..0000000000 --- a/test/uniq.js +++ /dev/null @@ -1,11 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; - -describe('uniq', function() { - it('should perform an unsorted uniq when used as an iteratee for methods like `_.map`', function() { - var array = [[2, 1, 2], [1, 2, 1]], - actual = lodashStable.map(array, lodashStable.uniq); - - assert.deepStrictEqual(actual, [[2, 1], [1, 2]]); - }); -}); diff --git a/test/uniq.spec.js b/test/uniq.spec.js new file mode 100644 index 0000000000..2b59b1910e --- /dev/null +++ b/test/uniq.spec.js @@ -0,0 +1,16 @@ +import lodashStable from 'lodash'; + +describe('uniq', () => { + it('should perform an unsorted uniq when used as an iteratee for methods like `_.map`', () => { + const array = [ + [2, 1, 2], + [1, 2, 1], + ]; + const actual = lodashStable.map(array, lodashStable.uniq); + + expect(actual).toEqual([ + [2, 1], + [1, 2], + ]); + }); +}); diff --git a/test/uniqBy-methods.js b/test/uniqBy-methods.js deleted file mode 100644 index 046ae644d2..0000000000 --- a/test/uniqBy-methods.js +++ /dev/null @@ -1,74 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, LARGE_ARRAY_SIZE, slice } from './utils.js'; -import sortBy from '../sortBy.js'; - -describe('uniqBy methods', function() { - lodashStable.each(['uniqBy', 'sortedUniqBy'], function(methodName) { - var func = _[methodName], - isSorted = methodName == 'sortedUniqBy', - objects = [{ 'a': 2 }, { 'a': 3 }, { 'a': 1 }, { 'a': 2 }, { 'a': 3 }, { 'a': 1 }]; - - if (isSorted) { - objects = sortBy(objects, 'a'); - } - it('`_.' + methodName + '` should work with an `iteratee`', function() { - var expected = isSorted ? [{ 'a': 1 }, { 'a': 2 }, { 'a': 3 }] : objects.slice(0, 3); - - var actual = func(objects, function(object) { - return object.a; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should work with large arrays', function() { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, function() { - return [1, 2]; - }); - - var actual = func(largeArray, String); - assert.strictEqual(actual[0], largeArray[0]); - assert.deepStrictEqual(actual, [[1, 2]]); - }); - - it('`_.' + methodName + '` should provide correct `iteratee` arguments', function() { - var args; - - func(objects, function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [objects[0]]); - }); - - it('`_.' + methodName + '` should work with `_.property` shorthands', function() { - var expected = isSorted ? [{ 'a': 1 }, { 'a': 2 }, { 'a': 3 }] : objects.slice(0, 3), - actual = func(objects, 'a'); - - assert.deepStrictEqual(actual, expected); - - var arrays = [[2], [3], [1], [2], [3], [1]]; - if (isSorted) { - arrays = lodashStable.sortBy(arrays, 0); - } - expected = isSorted ? [[1], [2], [3]] : arrays.slice(0, 3); - actual = func(arrays, 0); - - assert.deepStrictEqual(actual, expected); - }); - - lodashStable.each({ - 'an array': [0, 'a'], - 'an object': { '0': 'a' }, - 'a number': 0, - 'a string': '0' - }, - function(iteratee, key) { - it('`_.' + methodName + '` should work with ' + key + ' for `iteratee`', function() { - var actual = func([['a'], ['a'], ['b']], iteratee); - assert.deepStrictEqual(actual, [['a'], ['b']]); - }); - }); - }); -}); diff --git a/test/uniqBy-methods.spec.js b/test/uniqBy-methods.spec.js new file mode 100644 index 0000000000..54170e5594 --- /dev/null +++ b/test/uniqBy-methods.spec.js @@ -0,0 +1,71 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, slice } from './utils'; +import sortBy from '../src/sortBy'; + +describe('uniqBy methods', () => { + lodashStable.each(['uniqBy', 'sortedUniqBy'], (methodName) => { + const func = _[methodName]; + const isSorted = methodName === 'sortedUniqBy'; + let objects = [{ a: 2 }, { a: 3 }, { a: 1 }, { a: 2 }, { a: 3 }, { a: 1 }]; + + if (isSorted) { + objects = sortBy(objects, 'a'); + } + it(`\`_.${methodName}\` should work with an \`iteratee\``, () => { + const expected = isSorted ? [{ a: 1 }, { a: 2 }, { a: 3 }] : objects.slice(0, 3); + + const actual = func(objects, (object) => object.a); + + expect(actual).toEqual(expected); + }); + + it('should work with large arrays', () => { + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, () => [1, 2]); + + const actual = func(largeArray, String); + expect(actual[0]).toBe(largeArray[0]); + expect(actual, [[1).toEqual(2]]); + }); + + it(`\`_.${methodName}\` should provide correct \`iteratee\` arguments`, () => { + let args; + + func(objects, function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([objects[0]]); + }); + + it(`\`_.${methodName}\` should work with \`_.property\` shorthands`, () => { + let expected = isSorted ? [{ a: 1 }, { a: 2 }, { a: 3 }] : objects.slice(0, 3); + let actual = func(objects, 'a'); + + expect(actual).toEqual(expected); + + let arrays = [[2], [3], [1], [2], [3], [1]]; + if (isSorted) { + arrays = lodashStable.sortBy(arrays, 0); + } + expected = isSorted ? [[1], [2], [3]] : arrays.slice(0, 3); + actual = func(arrays, 0); + + expect(actual).toEqual(expected); + }); + + lodashStable.each( + { + 'an array': [0, 'a'], + 'an object': { 0: 'a' }, + 'a number': 0, + 'a string': '0', + }, + (iteratee, key) => { + it(`\`_.${methodName}\` should work with ${key} for \`iteratee\``, () => { + const actual = func([['a'], ['a'], ['b']], iteratee); + expect(actual, [['a']).toEqual(['b']]); + }); + }, + ); + }); +}); diff --git a/test/uniqWith.spec.js b/test/uniqWith.spec.js new file mode 100644 index 0000000000..05d76be240 --- /dev/null +++ b/test/uniqWith.spec.js @@ -0,0 +1,31 @@ +import lodashStable from 'lodash'; +import { LARGE_ARRAY_SIZE, isEven } from './utils'; +import uniqWith from '../src/uniqWith'; + +describe('uniqWith', () => { + it('should work with a `comparator`', () => { + const objects = [ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + { x: 1, y: 2 }, + ]; + const actual = uniqWith(objects, lodashStable.isEqual); + + expect(actual).toEqual([objects[0], objects[1]]); + }); + + it('should preserve the sign of `0`', () => { + const largeArray = lodashStable.times(LARGE_ARRAY_SIZE, (index) => + isEven(index) ? -0 : 0, + ); + + const arrays = [[-0, 0], largeArray]; + const expected = lodashStable.map(arrays, lodashStable.constant(['-0'])); + + const actual = lodashStable.map(arrays, (array) => + lodashStable.map(uniqWith(array, lodashStable.eq), lodashStable.toString), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/uniqWith.test.js b/test/uniqWith.test.js deleted file mode 100644 index bb9e79873d..0000000000 --- a/test/uniqWith.test.js +++ /dev/null @@ -1,28 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { LARGE_ARRAY_SIZE, isEven } from './utils.js'; -import uniqWith from '../uniqWith.js'; - -describe('uniqWith', function() { - it('should work with a `comparator`', function() { - var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }], - actual = uniqWith(objects, lodashStable.isEqual); - - assert.deepStrictEqual(actual, [objects[0], objects[1]]); - }); - - it('should preserve the sign of `0`', function() { - var largeArray = lodashStable.times(LARGE_ARRAY_SIZE, function(index) { - return isEven(index) ? -0 : 0; - }); - - var arrays = [[-0, 0], largeArray], - expected = lodashStable.map(arrays, lodashStable.constant(['-0'])); - - var actual = lodashStable.map(arrays, function(array) { - return lodashStable.map(uniqWith(array, lodashStable.eq), lodashStable.toString); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/uniqueId.spec.js b/test/uniqueId.spec.js new file mode 100644 index 0000000000..ba9742e62c --- /dev/null +++ b/test/uniqueId.spec.js @@ -0,0 +1,19 @@ +import lodashStable from 'lodash'; +import uniqueId from '../src/uniqueId'; + +describe('uniqueId', () => { + it('should generate unique ids', () => { + const actual = lodashStable.times(1000, () => uniqueId()); + + expect(lodashStable.uniq(actual).length).toBe(actual.length); + }); + + it('should return a string value when not providing a `prefix`', () => { + expect(typeof uniqueId()).toBe('string'); + }); + + it('should coerce the prefix argument to a string', () => { + const actual = [uniqueId(3), uniqueId(2), uniqueId(1)]; + expect(/3\d+,2\d+,1\d+/.test(actual)); + }); +}); diff --git a/test/uniqueId.test.js b/test/uniqueId.test.js deleted file mode 100644 index 4b0485b82f..0000000000 --- a/test/uniqueId.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import uniqueId from '../uniqueId.js'; - -describe('uniqueId', function() { - it('should generate unique ids', function() { - var actual = lodashStable.times(1000, function() { - return uniqueId(); - }); - - assert.strictEqual(lodashStable.uniq(actual).length, actual.length); - }); - - it('should return a string value when not providing a `prefix`', function() { - assert.strictEqual(typeof uniqueId(), 'string'); - }); - - it('should coerce the prefix argument to a string', function() { - var actual = [uniqueId(3), uniqueId(2), uniqueId(1)]; - assert.ok(/3\d+,2\d+,1\d+/.test(actual)); - }); -}); diff --git a/test/unset.js b/test/unset.js deleted file mode 100644 index 0aa2f5bd77..0000000000 --- a/test/unset.js +++ /dev/null @@ -1,119 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { symbol, numberProto, stringProto, defineProperty } from './utils.js'; -import unset from '../unset.js'; - -describe('unset', function() { - it('should unset property values', function() { - lodashStable.each(['a', ['a']], function(path) { - var object = { 'a': 1, 'c': 2 }; - assert.strictEqual(unset(object, path), true); - assert.deepStrictEqual(object, { 'c': 2 }); - }); - }); - - it('should preserve the sign of `0`', function() { - var props = [-0, Object(-0), 0, Object(0)], - expected = lodashStable.map(props, lodashStable.constant([true, false])); - - var actual = lodashStable.map(props, function(key) { - var object = { '-0': 'a', '0': 'b' }; - return [unset(object, key), lodashStable.toString(key) in object]; - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should unset symbol keyed property values', function() { - if (Symbol) { - var object = {}; - object[symbol] = 1; - - assert.strictEqual(unset(object, symbol), true); - assert.ok(!(symbol in object)); - } - }); - - it('should unset deep property values', function() { - lodashStable.each(['a.b', ['a', 'b']], function(path) { - var object = { 'a': { 'b': null } }; - assert.strictEqual(unset(object, path), true); - assert.deepStrictEqual(object, { 'a': {} }); - }); - }); - - it('should handle complex paths', function() { - var paths = [ - 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', - ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'] - ]; - - lodashStable.each(paths, function(path) { - var object = { 'a': { '-1.23': { '["b"]': { 'c': { "['d']": { '\ne\n': { 'f': { 'g': 8 } } } } } } } }; - assert.strictEqual(unset(object, path), true); - assert.ok(!('g' in object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f)); - }); - }); - - it('should return `true` for nonexistent paths', function() { - var object = { 'a': { 'b': { 'c': null } } }; - - lodashStable.each(['z', 'a.z', 'a.b.z', 'a.b.c.z'], function(path) { - assert.strictEqual(unset(object, path), true); - }); - - assert.deepStrictEqual(object, { 'a': { 'b': { 'c': null } } }); - }); - - it('should not error when `object` is nullish', function() { - var values = [null, undefined], - expected = [[true, true], [true, true]]; - - var actual = lodashStable.map(values, function(value) { - try { - return [unset(value, 'a.b'), unset(value, ['a', 'b'])]; - } catch (e) { - return e.message; - } - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should follow `path` over non-plain objects', function() { - var object = { 'a': '' }, - paths = ['constructor.prototype.a', ['constructor', 'prototype', 'a']]; - - lodashStable.each(paths, function(path) { - numberProto.a = 1; - - var actual = unset(0, path); - assert.strictEqual(actual, true); - assert.ok(!('a' in numberProto)); - - delete numberProto.a; - }); - - lodashStable.each(['a.replace.b', ['a', 'replace', 'b']], function(path) { - stringProto.replace.b = 1; - - var actual = unset(object, path); - assert.strictEqual(actual, true); - assert.ok(!('a' in stringProto.replace)); - - delete stringProto.replace.b; - }); - }); - - it('should return `false` for non-configurable properties', function() { - var object = {}; - - defineProperty(object, 'a', { - 'configurable': false, - 'enumerable': true, - 'writable': true, - 'value': 1, - }); - assert.strictEqual(unset(object, 'a'), false); - }); -}); diff --git a/test/unset.spec.js b/test/unset.spec.js new file mode 100644 index 0000000000..fa6d568ba5 --- /dev/null +++ b/test/unset.spec.js @@ -0,0 +1,123 @@ +import lodashStable from 'lodash'; +import { symbol, numberProto, stringProto, defineProperty } from './utils'; +import unset from '../src/unset'; + +describe('unset', () => { + it('should unset property values', () => { + lodashStable.each(['a', ['a']], (path) => { + const object = { a: 1, c: 2 }; + expect(unset(object, path)).toBe(true); + expect(object).toEqual({ c: 2 }); + }); + }); + + it('should preserve the sign of `0`', () => { + const props = [-0, Object(-0), 0, Object(0)]; + const expected = lodashStable.map(props, lodashStable.constant([true, false])); + + const actual = lodashStable.map(props, (key) => { + const object = { '-0': 'a', 0: 'b' }; + return [unset(object, key), lodashStable.toString(key) in object]; + }); + + expect(actual).toEqual(expected); + }); + + it('should unset symbol keyed property values', () => { + if (Symbol) { + const object = {}; + object[symbol] = 1; + + expect(unset(object, symbol)).toBe(true); + expect(symbol in object).toBe(false); + } + }); + + it('should unset deep property values', () => { + lodashStable.each(['a.b', ['a', 'b']], (path) => { + const object = { a: { b: null } }; + expect(unset(object, path)).toBe(true); + expect(object).toEqual({ a: {} }); + }); + }); + + it('should handle complex paths', () => { + const paths = [ + 'a[-1.23]["[\\"b\\"]"].c[\'[\\\'d\\\']\'][\ne\n][f].g', + ['a', '-1.23', '["b"]', 'c', "['d']", '\ne\n', 'f', 'g'], + ]; + + lodashStable.each(paths, (path) => { + const object = { + a: { '-1.23': { '["b"]': { c: { "['d']": { '\ne\n': { f: { g: 8 } } } } } } }, + }; + expect(unset(object, path)).toBe(true); + expect('g' in object.a[-1.23]['["b"]'].c["['d']"]['\ne\n'].f).toBe(false); + }); + }); + + it('should return `true` for nonexistent paths', () => { + const object = { a: { b: { c: null } } }; + + lodashStable.each(['z', 'a.z', 'a.b.z', 'a.b.c.z'], (path) => { + expect(unset(object, path)).toBe(true); + }); + + expect(object).toEqual({ a: { b: { c: null } } }); + }); + + it('should not error when `object` is nullish', () => { + const values = [null, undefined]; + const expected = [ + [true, true], + [true, true], + ]; + + const actual = lodashStable.map(values, (value) => { + try { + return [unset(value, 'a.b'), unset(value, ['a', 'b'])]; + } catch (e) { + return e.message; + } + }); + + expect(actual).toEqual(expected); + }); + + it('should follow `path` over non-plain objects', () => { + const object = { a: '' }; + const paths = ['constructor.prototype.a', ['constructor', 'prototype', 'a']]; + + lodashStable.each(paths, (path) => { + numberProto.a = 1; + + const actual = unset(0, path); + expect(actual).toBe(true); + expect('a' in numberProto).toBe(false); + + delete numberProto.a; + }); + + lodashStable.each(['a.replace.b', ['a', 'replace', 'b']], (path) => { + stringProto.replace.b = 1; + + const actual = unset(object, path); + expect(actual).toBe(true); + expect('a' in stringProto.replace).toBe(false); + + delete stringProto.replace.b; + }); + }); + + it('should return `false` for non-configurable properties', () => { + const object = {}; + + defineProperty(object, 'a', { + configurable: false, + enumerable: true, + writable: true, + value: 1, + }); + expect(unset(object, 'a')).toBe(false); + }); +}); diff --git a/test/unzip-and-zip.js b/test/unzip-and-zip.js deleted file mode 100644 index 57cdfd6ef9..0000000000 --- a/test/unzip-and-zip.js +++ /dev/null @@ -1,72 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, falsey, stubArray } from './utils.js'; - -describe('unzip and zip', function() { - lodashStable.each(['unzip', 'zip'], function(methodName, index) { - var func = _[methodName]; - func = lodashStable.bind(index ? func.apply : func.call, func, null); - - var object = { - 'an empty array': [ - [], - [] - ], - '0-tuples': [ - [[], []], - [] - ], - '2-tuples': [ - [['barney', 'fred'], [36, 40]], - [['barney', 36], ['fred', 40]] - ], - '3-tuples': [ - [['barney', 'fred'], [36, 40], [false, true]], - [['barney', 36, false], ['fred', 40, true]] - ] - }; - - lodashStable.forOwn(object, function(pair, key) { - it('`_.' + methodName + '` should work with ' + key, function() { - var actual = func(pair[0]); - assert.deepStrictEqual(actual, pair[1]); - assert.deepStrictEqual(func(actual), actual.length ? pair[0] : []); - }); - }); - - it('`_.' + methodName + '` should work with tuples of different lengths', function() { - var pair = [ - [['barney', 36], ['fred', 40, false]], - [['barney', 'fred'], [36, 40], [undefined, false]] - ]; - - var actual = func(pair[0]); - assert.ok('0' in actual[2]); - assert.deepStrictEqual(actual, pair[1]); - - actual = func(actual); - assert.ok('2' in actual[0]); - assert.deepStrictEqual(actual, [['barney', 36, undefined], ['fred', 40, false]]); - }); - - it('`_.' + methodName + '` should treat falsey values as empty arrays', function() { - var expected = lodashStable.map(falsey, stubArray); - - var actual = lodashStable.map(falsey, function(value) { - return func([value, value, value]); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should ignore values that are not arrays or `arguments` objects', function() { - var array = [[1, 2], [3, 4], null, undefined, { '0': 1 }]; - assert.deepStrictEqual(func(array), [[1, 3], [2, 4]]); - }); - - it('`_.' + methodName + '` should support consuming its return value', function() { - var expected = [['barney', 'fred'], [36, 40]]; - assert.deepStrictEqual(func(func(func(func(expected)))), expected); - }); - }); -}); diff --git a/test/unzip-and-zip.spec.js b/test/unzip-and-zip.spec.js new file mode 100644 index 0000000000..6882036eae --- /dev/null +++ b/test/unzip-and-zip.spec.js @@ -0,0 +1,92 @@ +import lodashStable from 'lodash'; +import { _, falsey, stubArray } from './utils'; + +describe('unzip and zip', () => { + lodashStable.each(['unzip', 'zip'], (methodName, index) => { + let func = _[methodName]; + func = lodashStable.bind(index ? func.apply : func.call, func, null); + + const object = { + 'an empty array': [[], []], + '0-tuples': [[[], []], []], + '2-tuples': [ + [ + ['barney', 'fred'], + [36, 40], + ], + [ + ['barney', 36], + ['fred', 40], + ], + ], + '3-tuples': [ + [ + ['barney', 'fred'], + [36, 40], + [false, true], + ], + [ + ['barney', 36, false], + ['fred', 40, true], + ], + ], + }; + + lodashStable.forOwn(object, (pair, key) => { + it(`\`_.${methodName}\` should work with ${key}`, () => { + const actual = func(pair[0]); + expect(actual).toEqual(pair[1]); + expect(func(actual)).toEqual(actual.length ? pair[0] : []); + }); + }); + + it(`\`_.${methodName}\` should work with tuples of different lengths`, () => { + const pair = [ + [ + ['barney', 36], + ['fred', 40, false], + ], + [ + ['barney', 'fred'], + [36, 40], + [undefined, false], + ], + ]; + + let actual = func(pair[0]); + expect('0' in actual[2]).toBeTruthy(); + expect(actual).toEqual(pair[1]); + + actual = func(actual); + expect('2' in actual[0]).toBeTruthy(); + expect(actual).toEqual([ + ['barney', 36, undefined], + ['fred', 40, false], + ]); + }); + + it(`\`_.${methodName}\` should treat falsey values as empty arrays`, () => { + const expected = lodashStable.map(falsey, stubArray); + + const actual = lodashStable.map(falsey, (value) => func([value, value, value])); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should ignore values that are not arrays or \`arguments\` objects`, () => { + const array = [[1, 2], [3, 4], null, undefined, { 0: 1 }]; + expect(func(array)).toEqual([ + [1, 3], + [2, 4], + ]); + }); + + it(`\`_.${methodName}\` should support consuming its return value`, () => { + const expected = [ + ['barney', 'fred'], + [36, 40], + ]; + expect(func(func(func(func(expected))))).toEqual(expected); + }); + }); +}); diff --git a/test/unzipWith.js b/test/unzipWith.js deleted file mode 100644 index d02d1a76a5..0000000000 --- a/test/unzipWith.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice } from './utils.js'; -import unzipWith from '../unzipWith.js'; -import unzip from '../unzip.js'; - -describe('unzipWith', function() { - it('should unzip arrays combining regrouped elements with `iteratee`', function() { - var array = [[1, 4], [2, 5], [3, 6]]; - - var actual = unzipWith(array, function(a, b, c) { - return a + b + c; - }); - - assert.deepStrictEqual(actual, [6, 15]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - unzipWith([[1, 3, 5], [2, 4, 6]], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [1, 2]); - }); - - it('should perform a basic unzip when `iteratee` is nullish', function() { - var array = [[1, 3], [2, 4]], - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant(unzip(array))); - - var actual = lodashStable.map(values, function(value, index) { - return index ? unzipWith(array, value) : unzipWith(array); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/unzipWith.spec.js b/test/unzipWith.spec.js new file mode 100644 index 0000000000..27c1e0ecf8 --- /dev/null +++ b/test/unzipWith.spec.js @@ -0,0 +1,49 @@ +import lodashStable from 'lodash'; +import { slice } from './utils'; +import unzipWith from '../src/unzipWith'; +import unzip from '../src/unzip'; + +describe('unzipWith', () => { + it('should unzip arrays combining regrouped elements with `iteratee`', () => { + const array = [ + [1, 4], + [2, 5], + [3, 6], + ]; + + const actual = unzipWith(array, (a, b, c) => a + b + c); + + expect(actual).toEqual([6, 15]); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + unzipWith( + [ + [1, 3, 5], + [2, 4, 6], + ], + function () { + args || (args = slice.call(arguments)); + }, + ); + + expect(args).toEqual([1, 2]); + }); + + it('should perform a basic unzip when `iteratee` is nullish', () => { + const array = [ + [1, 3], + [2, 4], + ]; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant(unzip(array))); + + const actual = lodashStable.map(values, (value, index) => + index ? unzipWith(array, value) : unzipWith(array), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/test/update-methods.js b/test/update-methods.js deleted file mode 100644 index 159f4008a2..0000000000 --- a/test/update-methods.js +++ /dev/null @@ -1,25 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _ } from './utils.js'; - -describe('update methods', function() { - lodashStable.each(['update', 'updateWith'], function(methodName) { - var func = _[methodName], - oldValue = 1; - - it('`_.' + methodName + '` should invoke `updater` with the value on `path` of `object`', function() { - var object = { 'a': [{ 'b': { 'c': oldValue } }] }, - expected = oldValue + 1; - - lodashStable.each(['a[0].b.c', ['a', '0', 'b', 'c']], function(path) { - func(object, path, function(n) { - assert.strictEqual(n, oldValue); - return ++n; - }); - - assert.strictEqual(object.a[0].b.c, expected); - object.a[0].b.c = oldValue; - }); - }); - }); -}); diff --git a/test/update-methods.spec.js b/test/update-methods.spec.js new file mode 100644 index 0000000000..fe0bf72792 --- /dev/null +++ b/test/update-methods.spec.js @@ -0,0 +1,24 @@ +import lodashStable from 'lodash'; +import { _ } from './utils'; + +describe('update methods', () => { + lodashStable.each(['update', 'updateWith'], (methodName) => { + const func = _[methodName]; + const oldValue = 1; + + it(`\`_.${methodName}\` should invoke \`updater\` with the value on \`path\` of \`object\``, () => { + const object = { a: [{ b: { c: oldValue } }] }; + const expected = oldValue + 1; + + lodashStable.each(['a[0].b.c', ['a', '0', 'b', 'c']], (path) => { + func(object, path, (n) => { + expect(n).toBe(oldValue); + return ++n; + }); + + expect(object.a[0].b.c).toBe(expected); + object.a[0].b.c = oldValue; + }); + }); + }); +}); diff --git a/test/updateWith.js b/test/updateWith.js deleted file mode 100644 index 7c709f91d9..0000000000 --- a/test/updateWith.js +++ /dev/null @@ -1,19 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { stubThree, stubFour, noop } from './utils.js'; -import updateWith from '../updateWith.js'; - -describe('updateWith', function() { - it('should work with a `customizer` callback', function() { - var actual = updateWith({ '0': {} }, '[0][1][2]', stubThree, function(value) { - return lodashStable.isObject(value) ? undefined : {}; - }); - - assert.deepStrictEqual(actual, { '0': { '1': { '2': 3 } } }); - }); - - it('should work with a `customizer` that returns `undefined`', function() { - var actual = updateWith({}, 'a[0].b.c', stubFour, noop); - assert.deepStrictEqual(actual, { 'a': [{ 'b': { 'c': 4 } }] }); - }); -}); diff --git a/test/updateWith.spec.js b/test/updateWith.spec.js new file mode 100644 index 0000000000..cc132af9c4 --- /dev/null +++ b/test/updateWith.spec.js @@ -0,0 +1,18 @@ +import lodashStable from 'lodash'; +import { stubThree, stubFour, noop } from './utils'; +import updateWith from '../src/updateWith'; + +describe('updateWith', () => { + it('should work with a `customizer` callback', () => { + const actual = updateWith({ 0: {} }, '[0][1][2]', stubThree, (value) => + lodashStable.isObject(value) ? undefined : {}, + ); + + expect(actual).toEqual({ 0: { 1: { 2: 3 } } }); + }); + + it('should work with a `customizer` that returns `undefined`', () => { + const actual = updateWith({}, 'a[0].b.c', stubFour, noop); + expect(actual).toEqual({ a: [{ b: { c: 4 } }] }); + }); +}); diff --git a/test/upperCase.spec.js b/test/upperCase.spec.js new file mode 100644 index 0000000000..a87bb5f6f9 --- /dev/null +++ b/test/upperCase.spec.js @@ -0,0 +1,9 @@ +import upperCase from '../src/upperCase'; + +describe('upperCase', () => { + it('should uppercase as space-separated words', () => { + expect(upperCase('--foo-bar--')).toBe('FOO BAR'); + expect(upperCase('fooBar')).toBe('FOO BAR'); + expect(upperCase('__foo_bar__')).toBe('FOO BAR'); + }); +}); diff --git a/test/upperCase.test.js b/test/upperCase.test.js deleted file mode 100644 index 08c362a0a2..0000000000 --- a/test/upperCase.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import upperCase from '../upperCase.js'; - -describe('upperCase', function() { - it('should uppercase as space-separated words', function() { - assert.strictEqual(upperCase('--foo-bar--'), 'FOO BAR'); - assert.strictEqual(upperCase('fooBar'), 'FOO BAR'); - assert.strictEqual(upperCase('__foo_bar__'), 'FOO BAR'); - }); -}); diff --git a/test/upperFirst.spec.js b/test/upperFirst.spec.js new file mode 100644 index 0000000000..b598595064 --- /dev/null +++ b/test/upperFirst.spec.js @@ -0,0 +1,9 @@ +import upperFirst from '../src/upperFirst'; + +describe('upperFirst', () => { + it('should uppercase only the first character', () => { + expect(upperFirst('fred')).toBe('Fred'); + expect(upperFirst('Fred')).toBe('Fred'); + expect(upperFirst('FRED')).toBe('FRED'); + }); +}); diff --git a/test/upperFirst.test.js b/test/upperFirst.test.js deleted file mode 100644 index b3833310a6..0000000000 --- a/test/upperFirst.test.js +++ /dev/null @@ -1,10 +0,0 @@ -import assert from 'assert'; -import upperFirst from '../upperFirst.js'; - -describe('upperFirst', function() { - it('should uppercase only the first character', function() { - assert.strictEqual(upperFirst('fred'), 'Fred'); - assert.strictEqual(upperFirst('Fred'), 'Fred'); - assert.strictEqual(upperFirst('FRED'), 'FRED'); - }); -}); diff --git a/test/utils.js b/test/utils.js index 19d327a03c..fc4ed1e5cb 100644 --- a/test/utils.js +++ b/test/utils.js @@ -1,255 +1,765 @@ /** Used to detect when a function becomes hot. */ -var HOT_COUNT = 150; +const HOT_COUNT = 150; /** Used as the size to cover large array optimizations. */ -var LARGE_ARRAY_SIZE = 200; +const LARGE_ARRAY_SIZE = 200; /** Used as the `TypeError` message for "Functions" methods. */ -var FUNC_ERROR_TEXT = 'Expected a function'; +const FUNC_ERROR_TEXT = 'Expected a function'; /** Used as the maximum memoize cache size. */ -var MAX_MEMOIZE_SIZE = 500; +const MAX_MEMOIZE_SIZE = 500; /** Used as references for various `Number` constants. */ -var MAX_SAFE_INTEGER = 9007199254740991, - MAX_INTEGER = 1.7976931348623157e+308; +const MAX_SAFE_INTEGER = 9007199254740991; +const MAX_INTEGER = 1.7976931348623157e308; /** Used as references for the maximum length and index of an array. */ -var MAX_ARRAY_LENGTH = 4294967295, - MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1; +const MAX_ARRAY_LENGTH = 4294967295; +const MAX_ARRAY_INDEX = MAX_ARRAY_LENGTH - 1; /** `Object#toString` result references. */ -var funcTag = '[object Function]', - numberTag = '[object Number]', - objectTag = '[object Object]'; +const funcTag = '[object Function]'; +const numberTag = '[object Number]'; +const objectTag = '[object Object]'; /** Used as a reference to the global object. */ -var root = (typeof global === 'object' && global) || this; +const root = (typeof global === 'object' && global) || this; /** Used to store lodash to test for bad extensions/shims. */ -var lodashBizarro = root.lodashBizarro; +let lodashBizarro = root.lodashBizarro; /** Used for native method references. */ -var arrayProto = Array.prototype, - funcProto = Function.prototype, - objectProto = Object.prototype, - numberProto = Number.prototype, - stringProto = String.prototype; +const arrayProto = Array.prototype; +const funcProto = Function.prototype; +const objectProto = Object.prototype; +const numberProto = Number.prototype; +const stringProto = String.prototype; /** Method and object shortcuts. */ -var phantom = root.phantom, - process = root.process, - amd = root.define ? define.amd : undefined, - args = toArgs([1, 2, 3]), - argv = process ? process.argv : undefined, - defineProperty = Object.defineProperty, - document = phantom ? undefined : root.document, - body = root.document ? root.document.body : undefined, - create = Object.create, - fnToString = funcProto.toString, - freeze = Object.freeze, - getSymbols = Object.getOwnPropertySymbols, - identity = function(value) { return value; }, - noop = function() {}, - objToString = objectProto.toString, - params = argv, - push = arrayProto.push, - realm = {}, - slice = arrayProto.slice, - strictArgs = (function() { 'use strict'; return arguments; }(1, 2, 3)); - -var ArrayBuffer = root.ArrayBuffer, - Buffer = root.Buffer, - Map = root.Map, - Promise = root.Promise, - Proxy = root.Proxy, - Set = root.Set, - Symbol = root.Symbol, - Uint8Array = root.Uint8Array, - WeakMap = root.WeakMap, - WeakSet = root.WeakSet; - -var arrayBuffer = ArrayBuffer ? new ArrayBuffer(2) : undefined, - map = Map ? new Map : undefined, - promise = Promise ? Promise.resolve(1) : undefined, - set = Set ? new Set : undefined, - symbol = Symbol ? Symbol('a') : undefined, - weakMap = WeakMap ? new WeakMap : undefined, - weakSet = WeakSet ? new WeakSet : undefined; +const phantom = root.phantom; +const process = root.process; +const amd = root.define ? define.amd : undefined; +const args = toArgs([1, 2, 3]); +const argv = process ? process.argv : undefined; +const defineProperty = Object.defineProperty; +const document = phantom ? undefined : root.document; +const body = root.document ? root.document.body : undefined; +const create = Object.create; +const fnToString = funcProto.toString; +const freeze = Object.freeze; +const getSymbols = Object.getOwnPropertySymbols; +const identity = function (value) { + return value; +}; +const noop = function () {}; +const objToString = objectProto.toString; +let params = argv; +const push = arrayProto.push; +const realm = {}; +const slice = arrayProto.slice; +const strictArgs = (function () { + 'use strict'; + + return arguments; +})(1, 2, 3); + +const ArrayBuffer = root.ArrayBuffer; +const Buffer = root.Buffer; +const Map = root.Map; +const Promise = root.Promise; +const Proxy = root.Proxy; +const Set = root.Set; +const Symbol = root.Symbol; +const Uint8Array = root.Uint8Array; +const WeakMap = root.WeakMap; +const WeakSet = root.WeakSet; + +const arrayBuffer = ArrayBuffer ? new ArrayBuffer(2) : undefined; +const map = Map ? new Map() : undefined; +const promise = Promise ? Promise.resolve(1) : undefined; +const set = Set ? new Set() : undefined; +const symbol = Symbol ? Symbol('a') : undefined; +const weakMap = WeakMap ? new WeakMap() : undefined; +const weakSet = WeakSet ? new WeakSet() : undefined; /** Math helpers. */ -var add = function(x, y) { return x + y; }, - doubled = function(n) { return n * 2; }, - isEven = function(n) { return n % 2 == 0; }, - square = function(n) { return n * n; }; +const add = function (x, y) { + return x + y; +}; +const doubled = function (n) { + return n * 2; +}; +const isEven = function (n) { + return n % 2 == 0; +}; +const square = function (n) { + return n * n; +}; /** Stub functions. */ -var stubA = function() { return 'a'; }, - stubB = function() { return 'b'; }, - stubC = function() { return 'c'; }; +const stubA = function () { + return 'a'; +}; +const stubB = function () { + return 'b'; +}; +const stubC = function () { + return 'c'; +}; -var stubTrue = function() { return true; }, - stubFalse = function() { return false; }; +const stubTrue = function () { + return true; +}; +const stubFalse = function () { + return false; +}; -var stubNaN = function() { return NaN; }, - stubNull = function() { return null; }; +const stubNaN = function () { + return NaN; +}; +const stubNull = function () { + return null; +}; -var stubZero = function() { return 0; }, - stubOne = function() { return 1; }, - stubTwo = function() { return 2; }, - stubThree = function() { return 3; }, - stubFour = function() { return 4; }; +const stubZero = function () { + return 0; +}; +const stubOne = function () { + return 1; +}; +const stubTwo = function () { + return 2; +}; +const stubThree = function () { + return 3; +}; +const stubFour = function () { + return 4; +}; -var stubArray = function() { return []; }, - stubObject = function() { return {}; }, - stubString = function() { return ''; }; +const stubArray = function () { + return []; +}; +const stubObject = function () { + return {}; +}; +const stubString = function () { + return ''; +}; /** List of Latin Unicode letters. */ -var burredLetters = [ - // Latin-1 Supplement letters. - '\xc0', '\xc1', '\xc2', '\xc3', '\xc4', '\xc5', '\xc6', '\xc7', '\xc8', '\xc9', '\xca', '\xcb', '\xcc', '\xcd', '\xce', '\xcf', - '\xd0', '\xd1', '\xd2', '\xd3', '\xd4', '\xd5', '\xd6', '\xd8', '\xd9', '\xda', '\xdb', '\xdc', '\xdd', '\xde', '\xdf', - '\xe0', '\xe1', '\xe2', '\xe3', '\xe4', '\xe5', '\xe6', '\xe7', '\xe8', '\xe9', '\xea', '\xeb', '\xec', '\xed', '\xee', '\xef', - '\xf0', '\xf1', '\xf2', '\xf3', '\xf4', '\xf5', '\xf6', '\xf8', '\xf9', '\xfa', '\xfb', '\xfc', '\xfd', '\xfe', '\xff', - // Latin Extended-A letters. - '\u0100', '\u0101', '\u0102', '\u0103', '\u0104', '\u0105', '\u0106', '\u0107', '\u0108', '\u0109', '\u010a', '\u010b', '\u010c', '\u010d', '\u010e', '\u010f', - '\u0110', '\u0111', '\u0112', '\u0113', '\u0114', '\u0115', '\u0116', '\u0117', '\u0118', '\u0119', '\u011a', '\u011b', '\u011c', '\u011d', '\u011e', '\u011f', - '\u0120', '\u0121', '\u0122', '\u0123', '\u0124', '\u0125', '\u0126', '\u0127', '\u0128', '\u0129', '\u012a', '\u012b', '\u012c', '\u012d', '\u012e', '\u012f', - '\u0130', '\u0131', '\u0132', '\u0133', '\u0134', '\u0135', '\u0136', '\u0137', '\u0138', '\u0139', '\u013a', '\u013b', '\u013c', '\u013d', '\u013e', '\u013f', - '\u0140', '\u0141', '\u0142', '\u0143', '\u0144', '\u0145', '\u0146', '\u0147', '\u0148', '\u0149', '\u014a', '\u014b', '\u014c', '\u014d', '\u014e', '\u014f', - '\u0150', '\u0151', '\u0152', '\u0153', '\u0154', '\u0155', '\u0156', '\u0157', '\u0158', '\u0159', '\u015a', '\u015b', '\u015c', '\u015d', '\u015e', '\u015f', - '\u0160', '\u0161', '\u0162', '\u0163', '\u0164', '\u0165', '\u0166', '\u0167', '\u0168', '\u0169', '\u016a', '\u016b', '\u016c', '\u016d', '\u016e', '\u016f', - '\u0170', '\u0171', '\u0172', '\u0173', '\u0174', '\u0175', '\u0176', '\u0177', '\u0178', '\u0179', '\u017a', '\u017b', '\u017c', '\u017d', '\u017e', '\u017f' +const burredLetters = [ + // Latin-1 Supplement letters. + '\xc0', + '\xc1', + '\xc2', + '\xc3', + '\xc4', + '\xc5', + '\xc6', + '\xc7', + '\xc8', + '\xc9', + '\xca', + '\xcb', + '\xcc', + '\xcd', + '\xce', + '\xcf', + '\xd0', + '\xd1', + '\xd2', + '\xd3', + '\xd4', + '\xd5', + '\xd6', + '\xd8', + '\xd9', + '\xda', + '\xdb', + '\xdc', + '\xdd', + '\xde', + '\xdf', + '\xe0', + '\xe1', + '\xe2', + '\xe3', + '\xe4', + '\xe5', + '\xe6', + '\xe7', + '\xe8', + '\xe9', + '\xea', + '\xeb', + '\xec', + '\xed', + '\xee', + '\xef', + '\xf0', + '\xf1', + '\xf2', + '\xf3', + '\xf4', + '\xf5', + '\xf6', + '\xf8', + '\xf9', + '\xfa', + '\xfb', + '\xfc', + '\xfd', + '\xfe', + '\xff', + // Latin Extended-A letters. + '\u0100', + '\u0101', + '\u0102', + '\u0103', + '\u0104', + '\u0105', + '\u0106', + '\u0107', + '\u0108', + '\u0109', + '\u010a', + '\u010b', + '\u010c', + '\u010d', + '\u010e', + '\u010f', + '\u0110', + '\u0111', + '\u0112', + '\u0113', + '\u0114', + '\u0115', + '\u0116', + '\u0117', + '\u0118', + '\u0119', + '\u011a', + '\u011b', + '\u011c', + '\u011d', + '\u011e', + '\u011f', + '\u0120', + '\u0121', + '\u0122', + '\u0123', + '\u0124', + '\u0125', + '\u0126', + '\u0127', + '\u0128', + '\u0129', + '\u012a', + '\u012b', + '\u012c', + '\u012d', + '\u012e', + '\u012f', + '\u0130', + '\u0131', + '\u0132', + '\u0133', + '\u0134', + '\u0135', + '\u0136', + '\u0137', + '\u0138', + '\u0139', + '\u013a', + '\u013b', + '\u013c', + '\u013d', + '\u013e', + '\u013f', + '\u0140', + '\u0141', + '\u0142', + '\u0143', + '\u0144', + '\u0145', + '\u0146', + '\u0147', + '\u0148', + '\u0149', + '\u014a', + '\u014b', + '\u014c', + '\u014d', + '\u014e', + '\u014f', + '\u0150', + '\u0151', + '\u0152', + '\u0153', + '\u0154', + '\u0155', + '\u0156', + '\u0157', + '\u0158', + '\u0159', + '\u015a', + '\u015b', + '\u015c', + '\u015d', + '\u015e', + '\u015f', + '\u0160', + '\u0161', + '\u0162', + '\u0163', + '\u0164', + '\u0165', + '\u0166', + '\u0167', + '\u0168', + '\u0169', + '\u016a', + '\u016b', + '\u016c', + '\u016d', + '\u016e', + '\u016f', + '\u0170', + '\u0171', + '\u0172', + '\u0173', + '\u0174', + '\u0175', + '\u0176', + '\u0177', + '\u0178', + '\u0179', + '\u017a', + '\u017b', + '\u017c', + '\u017d', + '\u017e', + '\u017f', ]; /** List of combining diacritical marks. */ -var comboMarks = [ - '\u0300', '\u0301', '\u0302', '\u0303', '\u0304', '\u0305', '\u0306', '\u0307', '\u0308', '\u0309', '\u030a', '\u030b', '\u030c', '\u030d', '\u030e', '\u030f', - '\u0310', '\u0311', '\u0312', '\u0313', '\u0314', '\u0315', '\u0316', '\u0317', '\u0318', '\u0319', '\u031a', '\u031b', '\u031c', '\u031d', '\u031e', '\u031f', - '\u0320', '\u0321', '\u0322', '\u0323', '\u0324', '\u0325', '\u0326', '\u0327', '\u0328', '\u0329', '\u032a', '\u032b', '\u032c', '\u032d', '\u032e', '\u032f', - '\u0330', '\u0331', '\u0332', '\u0333', '\u0334', '\u0335', '\u0336', '\u0337', '\u0338', '\u0339', '\u033a', '\u033b', '\u033c', '\u033d', '\u033e', '\u033f', - '\u0340', '\u0341', '\u0342', '\u0343', '\u0344', '\u0345', '\u0346', '\u0347', '\u0348', '\u0349', '\u034a', '\u034b', '\u034c', '\u034d', '\u034e', '\u034f', - '\u0350', '\u0351', '\u0352', '\u0353', '\u0354', '\u0355', '\u0356', '\u0357', '\u0358', '\u0359', '\u035a', '\u035b', '\u035c', '\u035d', '\u035e', '\u035f', - '\u0360', '\u0361', '\u0362', '\u0363', '\u0364', '\u0365', '\u0366', '\u0367', '\u0368', '\u0369', '\u036a', '\u036b', '\u036c', '\u036d', '\u036e', '\u036f', - '\ufe20', '\ufe21', '\ufe22', '\ufe23' +const comboMarks = [ + '\u0300', + '\u0301', + '\u0302', + '\u0303', + '\u0304', + '\u0305', + '\u0306', + '\u0307', + '\u0308', + '\u0309', + '\u030a', + '\u030b', + '\u030c', + '\u030d', + '\u030e', + '\u030f', + '\u0310', + '\u0311', + '\u0312', + '\u0313', + '\u0314', + '\u0315', + '\u0316', + '\u0317', + '\u0318', + '\u0319', + '\u031a', + '\u031b', + '\u031c', + '\u031d', + '\u031e', + '\u031f', + '\u0320', + '\u0321', + '\u0322', + '\u0323', + '\u0324', + '\u0325', + '\u0326', + '\u0327', + '\u0328', + '\u0329', + '\u032a', + '\u032b', + '\u032c', + '\u032d', + '\u032e', + '\u032f', + '\u0330', + '\u0331', + '\u0332', + '\u0333', + '\u0334', + '\u0335', + '\u0336', + '\u0337', + '\u0338', + '\u0339', + '\u033a', + '\u033b', + '\u033c', + '\u033d', + '\u033e', + '\u033f', + '\u0340', + '\u0341', + '\u0342', + '\u0343', + '\u0344', + '\u0345', + '\u0346', + '\u0347', + '\u0348', + '\u0349', + '\u034a', + '\u034b', + '\u034c', + '\u034d', + '\u034e', + '\u034f', + '\u0350', + '\u0351', + '\u0352', + '\u0353', + '\u0354', + '\u0355', + '\u0356', + '\u0357', + '\u0358', + '\u0359', + '\u035a', + '\u035b', + '\u035c', + '\u035d', + '\u035e', + '\u035f', + '\u0360', + '\u0361', + '\u0362', + '\u0363', + '\u0364', + '\u0365', + '\u0366', + '\u0367', + '\u0368', + '\u0369', + '\u036a', + '\u036b', + '\u036c', + '\u036d', + '\u036e', + '\u036f', + '\ufe20', + '\ufe21', + '\ufe22', + '\ufe23', ]; /** List of converted Latin Unicode letters. */ -var deburredLetters = [ - // Converted Latin-1 Supplement letters. - 'A', 'A', 'A', 'A', 'A', 'A', 'Ae', 'C', 'E', 'E', 'E', 'E', 'I', 'I', 'I', - 'I', 'D', 'N', 'O', 'O', 'O', 'O', 'O', 'O', 'U', 'U', 'U', 'U', 'Y', 'Th', - 'ss', 'a', 'a', 'a', 'a', 'a', 'a', 'ae', 'c', 'e', 'e', 'e', 'e', 'i', 'i', 'i', - 'i', 'd', 'n', 'o', 'o', 'o', 'o', 'o', 'o', 'u', 'u', 'u', 'u', 'y', 'th', 'y', - // Converted Latin Extended-A letters. - 'A', 'a', 'A', 'a', 'A', 'a', 'C', 'c', 'C', 'c', 'C', 'c', 'C', 'c', - 'D', 'd', 'D', 'd', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', 'E', 'e', - 'G', 'g', 'G', 'g', 'G', 'g', 'G', 'g', 'H', 'h', 'H', 'h', - 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'I', 'i', 'IJ', 'ij', 'J', 'j', - 'K', 'k', 'k', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', 'L', 'l', - 'N', 'n', 'N', 'n', 'N', 'n', "'n", 'N', 'n', - 'O', 'o', 'O', 'o', 'O', 'o', 'Oe', 'oe', - 'R', 'r', 'R', 'r', 'R', 'r', 'S', 's', 'S', 's', 'S', 's', 'S', 's', - 'T', 't', 'T', 't', 'T', 't', - 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', 'U', 'u', - 'W', 'w', 'Y', 'y', 'Y', 'Z', 'z', 'Z', 'z', 'Z', 'z', 's' +const deburredLetters = [ + // Converted Latin-1 Supplement letters. + 'A', + 'A', + 'A', + 'A', + 'A', + 'A', + 'Ae', + 'C', + 'E', + 'E', + 'E', + 'E', + 'I', + 'I', + 'I', + 'I', + 'D', + 'N', + 'O', + 'O', + 'O', + 'O', + 'O', + 'O', + 'U', + 'U', + 'U', + 'U', + 'Y', + 'Th', + 'ss', + 'a', + 'a', + 'a', + 'a', + 'a', + 'a', + 'ae', + 'c', + 'e', + 'e', + 'e', + 'e', + 'i', + 'i', + 'i', + 'i', + 'd', + 'n', + 'o', + 'o', + 'o', + 'o', + 'o', + 'o', + 'u', + 'u', + 'u', + 'u', + 'y', + 'th', + 'y', + // Converted Latin Extended-A letters. + 'A', + 'a', + 'A', + 'a', + 'A', + 'a', + 'C', + 'c', + 'C', + 'c', + 'C', + 'c', + 'C', + 'c', + 'D', + 'd', + 'D', + 'd', + 'E', + 'e', + 'E', + 'e', + 'E', + 'e', + 'E', + 'e', + 'E', + 'e', + 'G', + 'g', + 'G', + 'g', + 'G', + 'g', + 'G', + 'g', + 'H', + 'h', + 'H', + 'h', + 'I', + 'i', + 'I', + 'i', + 'I', + 'i', + 'I', + 'i', + 'I', + 'i', + 'IJ', + 'ij', + 'J', + 'j', + 'K', + 'k', + 'k', + 'L', + 'l', + 'L', + 'l', + 'L', + 'l', + 'L', + 'l', + 'L', + 'l', + 'N', + 'n', + 'N', + 'n', + 'N', + 'n', + "'n", + 'N', + 'n', + 'O', + 'o', + 'O', + 'o', + 'O', + 'o', + 'Oe', + 'oe', + 'R', + 'r', + 'R', + 'r', + 'R', + 'r', + 'S', + 's', + 'S', + 's', + 'S', + 's', + 'S', + 's', + 'T', + 't', + 'T', + 't', + 'T', + 't', + 'U', + 'u', + 'U', + 'u', + 'U', + 'u', + 'U', + 'u', + 'U', + 'u', + 'U', + 'u', + 'W', + 'w', + 'Y', + 'y', + 'Y', + 'Z', + 'z', + 'Z', + 'z', + 'Z', + 'z', + 's', ]; /** Used to provide falsey values to methods. */ -var falsey = [, null, undefined, false, 0, NaN, '']; +const falsey = [, null, undefined, false, 0, NaN, '']; /** Used to specify the emoji style glyph variant of characters. */ -var emojiVar = '\ufe0f'; +const emojiVar = '\ufe0f'; /** Used to provide empty values to methods. */ -var empties = [[], {}].concat(falsey.slice(1)); +const empties = [[], {}].concat(falsey.slice(1)); /** Used to test error objects. */ -var errors = [ - new Error, - new EvalError, - new RangeError, - new ReferenceError, - new SyntaxError, - new TypeError, - new URIError +const errors = [ + new Error(), + new EvalError(), + new RangeError(), + new ReferenceError(), + new SyntaxError(), + new TypeError(), + new URIError(), ]; /** List of fitzpatrick modifiers. */ -var fitzModifiers = [ - '\ud83c\udffb', - '\ud83c\udffc', - '\ud83c\udffd', - '\ud83c\udffe', - '\ud83c\udfff' +const fitzModifiers = [ + '\ud83c\udffb', + '\ud83c\udffc', + '\ud83c\udffd', + '\ud83c\udffe', + '\ud83c\udfff', ]; /** Used to provide primitive values to methods. */ -var primitives = [null, undefined, false, true, 1, NaN, 'a']; +const primitives = [null, undefined, false, true, 1, NaN, 'a']; /** Used to check whether methods support typed arrays. */ -var typedArrays = [ - 'Float32Array', - 'Float64Array', - 'Int8Array', - 'Int16Array', - 'Int32Array', - 'Uint8Array', - 'Uint8ClampedArray', - 'Uint16Array', - 'Uint32Array' +const typedArrays = [ + 'Float32Array', + 'Float64Array', + 'Int8Array', + 'Int16Array', + 'Int32Array', + 'Uint8Array', + 'Uint8ClampedArray', + 'Uint16Array', + 'Uint32Array', ]; /** Used to check whether methods support array views. */ -var arrayViews = typedArrays.concat('DataView'); +const arrayViews = typedArrays.concat('DataView'); /** The file path of the lodash file to test. */ -var filePath = (function() { - var min = 2, - result = params || []; - - if (phantom) { - min = 0; - result = params = phantom.args || require('system').args; - } - var last = result[result.length - 1]; - result = (result.length > min && !/test(?:\.js)?$/.test(last)) ? last : '../node_modules/lodash/lodash.js'; - - if (!amd) { - try { - result = require('fs').realpathSync(result); - } catch (e) {} - - try { - result = require.resolve(result); - } catch (e) {} - } - return result; -}()); +const filePath = (function () { + let min = 2; + let result = params || []; + + if (phantom) { + min = 0; + result = params = phantom.args || require('system').args; + } + const last = result[result.length - 1]; + result = + result.length > min && !/test(?:\.js)?$/.test(last) + ? last + : '../node_modules/lodash/lodash'; + + if (!amd) { + try { + result = require('fs').realpathSync(result); + } catch (e) {} + + try { + result = require.resolve(result); + } catch (e) {} + } + return result; +})(); /** The `ui` object. */ -var ui = root.ui || (root.ui = { - 'buildPath': filePath, - 'loaderPath': '', - 'isModularize': /\b(?:amd|commonjs|es|node|npm|(index|main)\.js)\b/.test(filePath), - 'isStrict': /\bes\b/.test(filePath) || 'default' in require(filePath), - 'urlParams': {} -}); +const ui = + root.ui || + (root.ui = { + buildPath: filePath, + loaderPath: '', + isModularize: /\b(?:amd|commonjs|es|node|npm|(index|main)\.js)\b/.test(filePath), + isStrict: /\bes\b/.test(filePath) || 'default' in require(filePath), + urlParams: {}, + }); /** The basename of the lodash file to test. */ -var basename = /[\w.-]+$/.exec(filePath)[0]; +const basename = /[\w.-]+$/.exec(filePath)[0]; /** Used to indicate testing a modularized build. */ -var isModularize = ui.isModularize; +const isModularize = ui.isModularize; /** Detect if testing `npm` modules. */ -var isNpm = isModularize && /\bnpm\b/.test([ui.buildPath, ui.urlParams.build]); +const isNpm = isModularize && /\bnpm\b/.test([ui.buildPath, ui.urlParams.build]); /** Detect if running in PhantomJS. */ -var isPhantom = phantom || (typeof callPhantom === 'function'); +const isPhantom = phantom || typeof callPhantom === 'function'; /** Detect if lodash is in strict mode. */ -var isStrict = ui.isStrict; +const isStrict = ui.isStrict; /*--------------------------------------------------------------------------*/ @@ -262,82 +772,110 @@ setProperty(root, 'setTimeout', setTimeout); /*--------------------------------------------------------------------------*/ /** Used to test Web Workers. */ -var Worker = !(ui.isForeign || ui.isSauceLabs || isModularize) && - (document && document.origin != 'null') && root.Worker; +const Worker = + !(ui.isForeign || ui.isSauceLabs || isModularize) && + document && + document.origin != 'null' && + root.Worker; /** Poison the free variable `root` in Node.js */ try { - defineProperty(global.root, 'root', { - 'configurable': false, - 'enumerable': false, - 'get': function() { throw new ReferenceError; } - }); + defineProperty(global.root, 'root', { + configurable: false, + enumerable: false, + get: function () { + throw new ReferenceError(); + }, + }); } catch (e) {} /** Load stable Lodash. */ -var lodashStable = root.lodashStable; +let lodashStable = root.lodashStable; if (!lodashStable) { - try { - lodashStable = interopRequire('../node_modules/lodash/lodash.js'); - } catch (e) { - console.log('Error: The stable lodash dev dependency should be at least a version behind master branch.'); - } - lodashStable = lodashStable.noConflict(); + try { + lodashStable = interopRequire('../node_modules/lodash/lodash'); + } catch (e) { + console.log( + 'Error: The stable lodash dev dependency should be at least a version behind master branch.', + ); + } + lodashStable = lodashStable.noConflict(); } /** The `lodash` function to test. */ -var _ = root._ || (root._ = interopRequire(filePath)); +const _ = root._ || (root._ = interopRequire(filePath)); /** Used to test pseudo private map caches. */ -var mapCaches = (function() { - var MapCache = _.memoize.Cache; - var result = { - 'Hash': new MapCache().__data__.hash.constructor, - 'MapCache': MapCache - }; - _.isMatchWith({ 'a': 1 }, { 'a': 1 }, function() { - var stack = lodashStable.last(arguments); - result.ListCache = stack.__data__.constructor; - result.Stack = stack.constructor; - }); - return result; -}()); +const mapCaches = (function () { + const MapCache = _.memoize.Cache; + const result = { + Hash: new MapCache().__data__.hash.constructor, + MapCache: MapCache, + }; + _.isMatchWith({ a: 1 }, { a: 1 }, function () { + const stack = lodashStable.last(arguments); + result.ListCache = stack.__data__.constructor; + result.Stack = stack.constructor; + }); + return result; +})(); /** Used to detect instrumented istanbul code coverage runs. */ -var coverage = root.__coverage__ || root[lodashStable.find(lodashStable.keys(root), function(key) { - return /^(?:\$\$cov_\d+\$\$)$/.test(key); -})]; +const coverage = + root.__coverage__ || + root[lodashStable.find(lodashStable.keys(root), (key) => /^(?:\$\$cov_\d+\$\$)$/.test(key))]; /** Used to test async functions. */ -var asyncFunc = lodashStable.attempt(function() { - return Function('return async () => {}'); -}); +const asyncFunc = lodashStable.attempt(() => Function('return async () => {}')); /** Used to test generator functions. */ -var genFunc = lodashStable.attempt(function() { - return Function('return function*(){}'); -}); +const genFunc = lodashStable.attempt(() => Function('return function*(){}')); /** Used to restore the `_` reference. */ -var oldDash = root._; +const oldDash = root._; /** * Used to check for problems removing whitespace. For a whitespace reference, * see [V8's unit test](https://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/whitespaces.js). */ -var whitespace = lodashStable.filter([ - // Basic whitespace characters. - ' ', '\t', '\x0b', '\f', '\xa0', '\ufeff', - - // Line terminators. - '\n', '\r', '\u2028', '\u2029', - - // Unicode category "Zs" space separators. - '\u1680', '\u180e', '\u2000', '\u2001', '\u2002', '\u2003', '\u2004', '\u2005', - '\u2006', '\u2007', '\u2008', '\u2009', '\u200a', '\u202f', '\u205f', '\u3000' -], -function(chr) { return /\s/.exec(chr); }) -.join(''); +const whitespace = lodashStable + .filter( + [ + // Basic whitespace characters. + ' ', + '\t', + '\x0b', + '\f', + '\xa0', + '\ufeff', + + // Line terminators. + '\n', + '\r', + '\u2028', + '\u2029', + + // Unicode category "Zs" space separators. + '\u1680', + '\u180e', + '\u2000', + '\u2001', + '\u2002', + '\u2003', + '\u2004', + '\u2005', + '\u2006', + '\u2007', + '\u2008', + '\u2009', + '\u200a', + '\u202f', + '\u205f', + '\u3000', + ], + (chr) => /\s/.exec(chr), + ) + .join(''); /** * Creates a custom error object. @@ -347,12 +885,12 @@ function(chr) { return /\s/.exec(chr); }) * @param {string} message The error message. */ function CustomError(message) { - this.name = 'CustomError'; - this.message = message; + this.name = 'CustomError'; + this.message = message; } CustomError.prototype = lodashStable.create(Error.prototype, { - 'constructor': CustomError + constructor: CustomError, }); /** @@ -362,9 +900,9 @@ CustomError.prototype = lodashStable.create(Error.prototype, { * @param {Object} object The object to empty. */ function emptyObject(object) { - lodashStable.forOwn(object, function(value, key, object) { - delete object[key]; - }); + lodashStable.forOwn(object, (value, key, object) => { + delete object[key]; + }); } /** @@ -375,19 +913,19 @@ function emptyObject(object) { * @returns {*} Returns the unwrapped value. */ function getUnwrappedValue(wrapper) { - var index = -1, - actions = wrapper.__actions__, - length = actions.length, - result = wrapper.__wrapped__; - - while (++index < length) { - var args = [result], - action = actions[index]; - - push.apply(args, action.args); - result = action.func.apply(action.thisArg, args); - } - return result; + let index = -1; + const actions = wrapper.__actions__; + const length = actions.length; + let result = wrapper.__wrapped__; + + while (++index < length) { + const args = [result]; + const action = actions[index]; + + push.apply(args, action.args); + result = action.func.apply(action.thisArg, args); + } + return result; } /** @@ -399,8 +937,8 @@ function getUnwrappedValue(wrapper) { * @returns {*} Returns the resolved module. */ function interopRequire(id) { - var result = require(id); - return 'default' in result ? result['default'] : result; + const result = require(id); + return 'default' in result ? result.default : result; } /** @@ -416,17 +954,17 @@ function interopRequire(id) { * @param {*} value The property value. */ function setProperty(object, key, value) { - try { - defineProperty(object, key, { - 'configurable': true, - 'enumerable': false, - 'writable': true, - 'value': value - }); - } catch (e) { - object[key] = value; - } - return object; + try { + defineProperty(object, key, { + configurable: true, + enumerable: false, + writable: true, + value: value, + }); + } catch (e) { + object[key] = value; + } + return object; } /** @@ -437,10 +975,10 @@ function setProperty(object, key, value) { * @param {number} [count=1] The number of tests to skip. */ function skipAssert(assert, count) { - count || (count = 1); - while (count--) { - assert.ok(true, 'test skipped'); - } + count || (count = 1); + while (count--) { + expect(true, 'test skipped'); + } } /** @@ -451,349 +989,369 @@ function skipAssert(assert, count) { * @returns {Object} Returns the converted `arguments` object. */ function toArgs(array) { - return (function() { return arguments; }.apply(undefined, array)); + return function () { + return arguments; + }.apply(undefined, array); } /*--------------------------------------------------------------------------*/ // Add bizarro values. -(function() { - return; // fixme - if (document || (typeof require !== 'function')) { - return; - } - var nativeString = fnToString.call(toString), - reToString = /toString/g; - - function createToString(funcName) { - return lodashStable.constant(nativeString.replace(reToString, funcName)); - } - - // Allow bypassing native checks. - setProperty(funcProto, 'toString', function wrapper() { - setProperty(funcProto, 'toString', fnToString); - var result = lodashStable.has(this, 'toString') ? this.toString() : fnToString.call(this); - setProperty(funcProto, 'toString', wrapper); - return result; - }); - - // Add prototype extensions. - funcProto._method = noop; - - // Set bad shims. - setProperty(Object, 'create', undefined); - setProperty(Object, 'getOwnPropertySymbols', undefined); - - var _propertyIsEnumerable = objectProto.propertyIsEnumerable; - setProperty(objectProto, 'propertyIsEnumerable', function(key) { - return !(key == 'valueOf' && this && this.valueOf === 1) && _propertyIsEnumerable.call(this, key); - }); - - if (Buffer) { - defineProperty(root, 'Buffer', { - 'configurable': true, - 'enumerable': true, - 'get': function get() { - var caller = get.caller, - name = caller ? caller.name : ''; - - if (!(name == 'runInContext' || name.length == 1 || /\b_\.isBuffer\b/.test(caller))) { - return Buffer; - } - } +(function () { + return; // fixme + if (document || typeof require !== 'function') { + return; + } + const nativeString = fnToString.call(toString); + const reToString = /toString/g; + + function createToString(funcName) { + return lodashStable.constant(nativeString.replace(reToString, funcName)); + } + + // Allow bypassing native checks. + setProperty(funcProto, 'toString', function wrapper() { + setProperty(funcProto, 'toString', fnToString); + const result = lodashStable.has(this, 'toString') ? this.toString() : fnToString.call(this); + setProperty(funcProto, 'toString', wrapper); + return result; }); - } - if (Map) { - setProperty(root, 'Map', (function() { - var count = 0; - return function() { - if (count++) { - return new Map; - } + + // Add prototype extensions. + funcProto._method = noop; + + // Set bad shims. + setProperty(Object, 'create', undefined); + setProperty(Object, 'getOwnPropertySymbols', undefined); + + const _propertyIsEnumerable = objectProto.propertyIsEnumerable; + setProperty(objectProto, 'propertyIsEnumerable', function (key) { + return ( + !(key == 'valueOf' && this && this.valueOf === 1) && + _propertyIsEnumerable.call(this, key) + ); + }); + + if (Buffer) { + defineProperty(root, 'Buffer', { + configurable: true, + enumerable: true, + get: function get() { + const caller = get.caller; + const name = caller ? caller.name : ''; + + if ( + !(name == 'runInContext' || name.length == 1 || /\b_\.isBuffer\b/.test(caller)) + ) { + return Buffer; + } + }, + }); + } + if (Map) { + setProperty( + root, + 'Map', + (function () { + let count = 0; + return function () { + if (count++) { + return new Map(); + } + setProperty(root, 'Map', Map); + return {}; + }; + })(), + ); + + setProperty(root.Map, 'toString', createToString('Map')); + } + setProperty(root, 'Promise', noop); + setProperty(root, 'Set', noop); + setProperty(root, 'Symbol', undefined); + setProperty(root, 'WeakMap', noop); + + // Fake `WinRTError`. + setProperty(root, 'WinRTError', Error); + + // Clear cache so lodash can be reloaded. + emptyObject(require.cache); + + // Load lodash and expose it to the bad extensions/shims. + lodashBizarro = interopRequire(filePath); + root._ = oldDash; + + // Restore built-in methods. + setProperty(Object, 'create', create); + setProperty(objectProto, 'propertyIsEnumerable', _propertyIsEnumerable); + setProperty(root, 'Buffer', Buffer); + + if (getSymbols) { + Object.getOwnPropertySymbols = getSymbols; + } else { + delete Object.getOwnPropertySymbols; + } + if (Map) { setProperty(root, 'Map', Map); - return {}; - }; - }())); - - setProperty(root.Map, 'toString', createToString('Map')); - } - setProperty(root, 'Promise', noop); - setProperty(root, 'Set', noop); - setProperty(root, 'Symbol', undefined); - setProperty(root, 'WeakMap', noop); - - // Fake `WinRTError`. - setProperty(root, 'WinRTError', Error); - - // Clear cache so lodash can be reloaded. - emptyObject(require.cache); - - // Load lodash and expose it to the bad extensions/shims. - lodashBizarro = interopRequire(filePath); - root._ = oldDash; - - // Restore built-in methods. - setProperty(Object, 'create', create); - setProperty(objectProto, 'propertyIsEnumerable', _propertyIsEnumerable); - setProperty(root, 'Buffer', Buffer); - - if (getSymbols) { - Object.getOwnPropertySymbols = getSymbols; - } else { - delete Object.getOwnPropertySymbols; - } - if (Map) { - setProperty(root, 'Map', Map); - } else { - delete root.Map; - } - if (Promise) { - setProperty(root, 'Promise', Promise); - } else { - delete root.Promise; - } - if (Set) { - setProperty(root, 'Set', Set); - } else { - delete root.Set; - } - if (Symbol) { - setProperty(root, 'Symbol', Symbol); - } else { - delete root.Symbol; - } - if (WeakMap) { - setProperty(root, 'WeakMap', WeakMap); - } else { - delete root.WeakMap; - } - delete root.WinRTError; - delete funcProto._method; -}()); + } else { + delete root.Map; + } + if (Promise) { + setProperty(root, 'Promise', Promise); + } else { + delete root.Promise; + } + if (Set) { + setProperty(root, 'Set', Set); + } else { + delete root.Set; + } + if (Symbol) { + setProperty(root, 'Symbol', Symbol); + } else { + delete root.Symbol; + } + if (WeakMap) { + setProperty(root, 'WeakMap', WeakMap); + } else { + delete root.WeakMap; + } + delete root.WinRTError; + delete funcProto._method; +})(); // Add other realm values from the `vm` module. -lodashStable.attempt(function() { - lodashStable.assign(realm, require('vm').runInNewContext([ - '(function() {', - ' var noop = function() {},', - ' root = this;', - '', - ' var object = {', - " 'ArrayBuffer': root.ArrayBuffer,", - " 'arguments': (function() { return arguments; }(1, 2, 3)),", - " 'array': [1],", - " 'arrayBuffer': root.ArrayBuffer ? new root.ArrayBuffer : undefined,", - " 'boolean': Object(false),", - " 'date': new Date,", - " 'errors': [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError],", - " 'function': noop,", - " 'map': root.Map ? new root.Map : undefined,", - " 'nan': NaN,", - " 'null': null,", - " 'number': Object(0),", - " 'object': { 'a': 1 },", - " 'promise': root.Promise ? Promise.resolve(1) : undefined,", - " 'regexp': /x/,", - " 'set': root.Set ? new root.Set : undefined,", - " 'string': Object('a'),", - " 'symbol': root.Symbol ? root.Symbol() : undefined,", - " 'undefined': undefined,", - " 'weakMap': root.WeakMap ? new root.WeakMap : undefined,", - " 'weakSet': root.WeakSet ? new root.WeakSet : undefined", - ' };', - '', - " ['" + arrayViews.join("', '") + "'].forEach(function(type) {", - ' var Ctor = root[type]', - ' object[type] = Ctor;', - ' object[type.toLowerCase()] = Ctor ? new Ctor(new ArrayBuffer(24)) : undefined;', - ' });', - '', - ' return object;', - '}());' - ].join('\n'))); +lodashStable.attempt(() => { + lodashStable.assign( + realm, + require('vm').runInNewContext( + [ + '(function() {', + ' var noop = function() {},', + ' root = this;', + '', + ' var object = {', + " 'ArrayBuffer': root.ArrayBuffer,", + " 'arguments': (function() { return arguments; }(1, 2, 3)),", + " 'array': [1],", + " 'arrayBuffer': root.ArrayBuffer ? new root.ArrayBuffer : undefined,", + " 'boolean': Object(false),", + " 'date': new Date,", + " 'errors': [new Error, new EvalError, new RangeError, new ReferenceError, new SyntaxError, new TypeError, new URIError],", + " 'function': noop,", + " 'map': root.Map ? new root.Map : undefined,", + " 'nan': NaN,", + " 'null': null,", + " 'number': Object(0),", + " 'object': { 'a': 1 },", + " 'promise': root.Promise ? Promise.resolve(1) : undefined,", + " 'regexp': /x/,", + " 'set': root.Set ? new root.Set : undefined,", + " 'string': Object('a'),", + " 'symbol': root.Symbol ? root.Symbol() : undefined,", + " 'undefined': undefined,", + " 'weakMap': root.WeakMap ? new root.WeakMap : undefined,", + " 'weakSet': root.WeakSet ? new root.WeakSet : undefined", + ' };', + '', + ` ['${arrayViews.join("', '")}'].forEach(function(type) {`, + ' var Ctor = root[type]', + ' object[type] = Ctor;', + ' object[type.toLowerCase()] = Ctor ? new Ctor(new ArrayBuffer(24)) : undefined;', + ' });', + '', + ' return object;', + '}());', + ].join('\n'), + ), + ); }); // Add other realm values from an iframe. -lodashStable.attempt(function() { - _._realm = realm; - - var iframe = document.createElement('iframe'); - iframe.frameBorder = iframe.height = iframe.width = 0; - body.appendChild(iframe); - - var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc; - idoc.write([ - '', - '', - '', - '', - '' - ].join('\n')); - - idoc.close(); - delete _._realm; +lodashStable.attempt(() => { + _._realm = realm; + + const iframe = document.createElement('iframe'); + iframe.frameBorder = iframe.height = iframe.width = 0; + body.appendChild(iframe); + + var idoc = (idoc = iframe.contentDocument || iframe.contentWindow).document || idoc; + idoc.write( + [ + '', + '', + '', + '', + '', + ].join('\n'), + ); + + idoc.close(); + delete _._realm; }); // Add a web worker. -lodashStable.attempt(function() { - var worker = new Worker('./asset/worker.js?t=' + (+new Date)); - worker.addEventListener('message', function(e) { - _._VERSION = e.data || ''; - }, false); - - worker.postMessage(ui.buildPath); +lodashStable.attempt(() => { + const worker = new Worker(`./asset/worker.js?t=${+new Date()}`); + worker.addEventListener( + 'message', + (e) => { + _._VERSION = e.data || ''; + }, + false, + ); + + worker.postMessage(ui.buildPath); }); // Expose internal modules for better code coverage. -lodashStable.attempt(function() { - var path = require('path'), - basePath = path.dirname(filePath); - - if (isModularize && !(amd || isNpm)) { - lodashStable.each([ - 'baseEach', - 'isIndex', - 'isIterateeCall', - 'memoizeCapped' - ], function(funcName) { - _['_' + funcName] = interopRequire(path.join(basePath, '_' + funcName)); - }); - } +lodashStable.attempt(() => { + const path = require('path'); + const basePath = path.dirname(filePath); + + if (isModularize && !(amd || isNpm)) { + lodashStable.each( + ['baseEach', 'isIndex', 'isIterateeCall', 'memoizeCapped'], + (funcName) => { + _[`_${funcName}`] = interopRequire(path.join(basePath, `_${funcName}`)); + }, + ); + } }); export { - HOT_COUNT, - LARGE_ARRAY_SIZE, - FUNC_ERROR_TEXT, - MAX_MEMOIZE_SIZE, - MAX_SAFE_INTEGER, - MAX_INTEGER, - MAX_ARRAY_LENGTH, - MAX_ARRAY_INDEX, - funcTag, - numberTag, - objectTag, - lodashBizarro, - arrayProto, - funcProto, - objectProto, - numberProto, - stringProto, - phantom, - amd, - args, - argv, - defineProperty, - document, - body, - create, - fnToString, - freeze, - getSymbols, - identity, - noop, - objToString, - params, - push, - realm, - root, - slice, - strictArgs, - arrayBuffer, - map, - promise, - set, - symbol, - weakMap, - weakSet, - add, - doubled, - isEven, - square, - stubA, - stubB, - stubC, - stubTrue, - stubFalse, - stubNaN, - stubNull, - stubZero, - stubOne, - stubTwo, - stubThree, - stubFour, - stubArray, - stubObject, - stubString, - burredLetters, - comboMarks, - deburredLetters, - falsey, - emojiVar, - empties, - errors, - fitzModifiers, - primitives, - typedArrays, - arrayViews, - filePath, - ui, - basename, - isModularize, - isNpm, - isPhantom, - isStrict, - Worker, - lodashStable, - _, - mapCaches, - coverage, - asyncFunc, - genFunc, - oldDash, - whitespace, - CustomError, - emptyObject, - getUnwrappedValue, - interopRequire, - setProperty, - skipAssert, - toArgs + HOT_COUNT, + LARGE_ARRAY_SIZE, + FUNC_ERROR_TEXT, + MAX_MEMOIZE_SIZE, + MAX_SAFE_INTEGER, + MAX_INTEGER, + MAX_ARRAY_LENGTH, + MAX_ARRAY_INDEX, + funcTag, + numberTag, + objectTag, + lodashBizarro, + arrayProto, + funcProto, + objectProto, + numberProto, + stringProto, + phantom, + amd, + args, + argv, + defineProperty, + document, + body, + create, + fnToString, + freeze, + getSymbols, + identity, + noop, + objToString, + params, + push, + realm, + root, + slice, + strictArgs, + arrayBuffer, + map, + promise, + set, + symbol, + weakMap, + weakSet, + add, + doubled, + isEven, + square, + stubA, + stubB, + stubC, + stubTrue, + stubFalse, + stubNaN, + stubNull, + stubZero, + stubOne, + stubTwo, + stubThree, + stubFour, + stubArray, + stubObject, + stubString, + burredLetters, + comboMarks, + deburredLetters, + falsey, + emojiVar, + empties, + errors, + fitzModifiers, + primitives, + typedArrays, + arrayViews, + filePath, + ui, + basename, + isModularize, + isNpm, + isPhantom, + isStrict, + Worker, + lodashStable, + _, + mapCaches, + coverage, + asyncFunc, + genFunc, + oldDash, + whitespace, + CustomError, + emptyObject, + getUnwrappedValue, + interopRequire, + setProperty, + skipAssert, + toArgs, }; diff --git a/test/values-methods.js b/test/values-methods.js deleted file mode 100644 index b17f3c48b6..0000000000 --- a/test/values-methods.js +++ /dev/null @@ -1,47 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, args, strictArgs } from './utils.js'; - -describe('values methods', function() { - lodashStable.each(['values', 'valuesIn'], function(methodName) { - var func = _[methodName], - isValues = methodName == 'values'; - - it('`_.' + methodName + '` should get string keyed values of `object`', function() { - var object = { 'a': 1, 'b': 2 }, - actual = func(object).sort(); - - assert.deepStrictEqual(actual, [1, 2]); - }); - - it('`_.' + methodName + '` should work with an object that has a `length` property', function() { - var object = { '0': 'a', '1': 'b', 'length': 2 }, - actual = func(object).sort(); - - assert.deepStrictEqual(actual, [2, 'a', 'b']); - }); - - it('`_.' + methodName + '` should ' + (isValues ? 'not ' : '') + 'include inherited string keyed property values', function() { - function Foo() { - this.a = 1; - } - Foo.prototype.b = 2; - - var expected = isValues ? [1] : [1, 2], - actual = func(new Foo).sort(); - - assert.deepStrictEqual(actual, expected); - }); - - it('`_.' + methodName + '` should work with `arguments` objects', function() { - var values = [args, strictArgs], - expected = lodashStable.map(values, lodashStable.constant([1, 2, 3])); - - var actual = lodashStable.map(values, function(value) { - return func(value).sort(); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); -}); diff --git a/test/values-methods.spec.js b/test/values-methods.spec.js new file mode 100644 index 0000000000..90d56a5889 --- /dev/null +++ b/test/values-methods.spec.js @@ -0,0 +1,46 @@ +import lodashStable from 'lodash'; +import { _, args, strictArgs } from './utils'; + +describe('values methods', () => { + lodashStable.each(['values', 'valuesIn'], (methodName) => { + const func = _[methodName]; + const isValues = methodName === 'values'; + + it(`\`_.${methodName}\` should get string keyed values of \`object\``, () => { + const object = { a: 1, b: 2 }; + const actual = func(object).sort(); + + expect(actual).toEqual([1, 2]); + }); + + it(`\`_.${methodName}\` should work with an object that has a \`length\` property`, () => { + const object = { 0: 'a', 1: 'b', length: 2 }; + const actual = func(object).sort(); + + expect(actual).toEqual([2, 'a', 'b']); + }); + + it(`\`_.${methodName}\` should ${ + isValues ? 'not ' : '' + }include inherited string keyed property values`, () => { + function Foo() { + this.a = 1; + } + Foo.prototype.b = 2; + + const expected = isValues ? [1] : [1, 2]; + const actual = func(new Foo()).sort(); + + expect(actual).toEqual(expected); + }); + + it(`\`_.${methodName}\` should work with \`arguments\` objects`, () => { + const values = [args, strictArgs]; + const expected = lodashStable.map(values, lodashStable.constant([1, 2, 3])); + + const actual = lodashStable.map(values, (value) => func(value).sort()); + + expect(actual).toEqual(expected); + }); + }); +}); diff --git a/test/without.spec.js b/test/without.spec.js new file mode 100644 index 0000000000..53557d9cff --- /dev/null +++ b/test/without.spec.js @@ -0,0 +1,22 @@ +import without from '../src/without'; + +describe('without', () => { + it('should return the difference of values', () => { + const actual = without([2, 1, 2, 3], 1, 2); + expect(actual).toEqual([3]); + }); + + it('should use strict equality to determine the values to reject', () => { + const object1 = { a: 1 }; + const object2 = { b: 2 }; + const array = [object1, object2]; + + expect(without(array, { a: 1 })).toEqual(array); + expect(without(array, object1)).toEqual([object2]); + }); + + it('should remove all occurrences of each value from an array', () => { + const array = [1, 2, 3, 1, 2, 3]; + expect(without(array, 1, 2), [3).toEqual(3]); + }); +}); diff --git a/test/without.test.js b/test/without.test.js deleted file mode 100644 index bad3329414..0000000000 --- a/test/without.test.js +++ /dev/null @@ -1,23 +0,0 @@ -import assert from 'assert'; -import without from '../without.js'; - -describe('without', function() { - it('should return the difference of values', function() { - var actual = without([2, 1, 2, 3], 1, 2); - assert.deepStrictEqual(actual, [3]); - }); - - it('should use strict equality to determine the values to reject', function() { - var object1 = { 'a': 1 }, - object2 = { 'b': 2 }, - array = [object1, object2]; - - assert.deepStrictEqual(without(array, { 'a': 1 }), array); - assert.deepStrictEqual(without(array, object1), [object2]); - }); - - it('should remove all occurrences of each value from an array', function() { - var array = [1, 2, 3, 1, 2, 3]; - assert.deepStrictEqual(without(array, 1, 2), [3, 3]); - }); -}); diff --git a/test/words.spec.js b/test/words.spec.js new file mode 100644 index 0000000000..3c8e35e494 --- /dev/null +++ b/test/words.spec.js @@ -0,0 +1,116 @@ +import lodashStable from 'lodash'; +import { burredLetters, stubArray } from './utils'; +import words from '../src/words'; + +describe('words', () => { + it('should match words containing Latin Unicode letters', () => { + const expected = lodashStable.map(burredLetters, (letter) => [letter]); + + const actual = lodashStable.map(burredLetters, (letter) => words(letter)); + + expect(actual).toEqual(expected); + }); + + it('should support a `pattern`', () => { + expect(words('abcd', /ab|cd/g)).toEqual(['ab', 'cd']); + expect(Array.from(words('abcd', 'ab|cd'))).toEqual(['ab']); + }); + + it('should work with compound words', () => { + expect(words('12ft')).toEqual(['12', 'ft']); + expect(words('aeiouAreVowels')).toEqual(['aeiou', 'Are', 'Vowels']); + expect(words('enable 6h format')).toEqual(['enable', '6', 'h', 'format']); + expect(words('enable 24H format')).toEqual(['enable', '24', 'H', 'format']); + expect(words('isISO8601')).toEqual(['is', 'ISO', '8601']); + expect(words('LETTERSAeiouAreVowels')).toEqual(['LETTERS', 'Aeiou', 'Are', 'Vowels']); + expect(words('tooLegit2Quit')).toEqual(['too', 'Legit', '2', 'Quit']); + expect(words('walk500Miles')).toEqual(['walk', '500', 'Miles']); + expect(words('xhr2Request')).toEqual(['xhr', '2', 'Request']); + expect(words('XMLHttp')).toEqual(['XML', 'Http']); + expect(words('XmlHTTP')).toEqual(['Xml', 'HTTP']); + expect(words('XmlHttp')).toEqual(['Xml', 'Http']); + }); + + it('should work with compound words containing diacritical marks', () => { + expect(words('LETTERSÆiouAreVowels')).toEqual(['LETTERS', 'Æiou', 'Are', 'Vowels']); + expect(words('æiouAreVowels')).toEqual(['æiou', 'Are', 'Vowels']); + expect(words('æiou2Consonants')).toEqual(['æiou', '2', 'Consonants']); + }); + + it('should not treat contractions as separate words', () => { + const postfixes = ['d', 'll', 'm', 're', 's', 't', 've']; + + lodashStable.each(["'", '\u2019'], (apos) => { + lodashStable.times(2, (index) => { + const actual = lodashStable.map(postfixes, (postfix) => { + const string = `a b${apos}${postfix} c`; + return words(string[index ? 'toUpperCase' : 'toLowerCase']()); + }); + + const expected = lodashStable.map(postfixes, (postfix) => { + const words = ['a', `b${apos}${postfix}`, 'c']; + return lodashStable.map(words, (word) => + word[index ? 'toUpperCase' : 'toLowerCase'](), + ); + }); + + expect(actual).toEqual(expected); + }); + }); + }); + + it('should not treat ordinal numbers as separate words', () => { + const ordinals = ['1st', '2nd', '3rd', '4th']; + + lodashStable.times(2, (index) => { + const expected = lodashStable.map(ordinals, (ordinal) => [ + ordinal[index ? 'toUpperCase' : 'toLowerCase'](), + ]); + + const actual = lodashStable.map(expected, (expectedWords) => words(expectedWords[0])); + + expect(actual).toEqual(expected); + }); + }); + + it('should not treat mathematical operators as words', () => { + const operators = ['\xac', '\xb1', '\xd7', '\xf7']; + const expected = lodashStable.map(operators, stubArray); + const actual = lodashStable.map(operators, words); + + expect(actual).toEqual(expected); + }); + + it('should not treat punctuation as words', () => { + const marks = [ + '\u2012', + '\u2013', + '\u2014', + '\u2015', + '\u2024', + '\u2025', + '\u2026', + '\u205d', + '\u205e', + ]; + + const expected = lodashStable.map(marks, stubArray); + const actual = lodashStable.map(marks, words); + + expect(actual).toEqual(expected); + }); + + it('should prevent ReDoS', () => { + const largeWordLen = 50000; + const largeWord = 'A'.repeat(largeWordLen); + const maxMs = 1000; + const startTime = lodashStable.now(); + + expect(words(`${largeWord}ÆiouAreVowels`)).toEqual([largeWord, 'Æiou', 'Are', 'Vowels']); + + const endTime = lodashStable.now(); + const timeSpent = endTime - startTime; + + expect(timeSpent).toBeLessThan(maxMs); + }); +}); diff --git a/test/words.test.js b/test/words.test.js deleted file mode 100644 index 5eb1043bef..0000000000 --- a/test/words.test.js +++ /dev/null @@ -1,117 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { burredLetters, _, stubArray } from './utils.js'; -import words from '../words.js' - -describe('words', function() { - it('should match words containing Latin Unicode letters', function() { - var expected = lodashStable.map(burredLetters, function(letter) { - return [letter]; - }); - - var actual = lodashStable.map(burredLetters, function(letter) { - return words(letter); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should support a `pattern`', function() { - assert.deepStrictEqual(words('abcd', /ab|cd/g), ['ab', 'cd']); - assert.deepStrictEqual(Array.from(words('abcd', 'ab|cd')), ['ab']); - }); - - it('should work with compound words', function() { - assert.deepStrictEqual(words('12ft'), ['12', 'ft']); - assert.deepStrictEqual(words('aeiouAreVowels'), ['aeiou', 'Are', 'Vowels']); - assert.deepStrictEqual(words('enable 6h format'), ['enable', '6', 'h', 'format']); - assert.deepStrictEqual(words('enable 24H format'), ['enable', '24', 'H', 'format']); - assert.deepStrictEqual(words('isISO8601'), ['is', 'ISO', '8601']); - assert.deepStrictEqual(words('LETTERSAeiouAreVowels'), ['LETTERS', 'Aeiou', 'Are', 'Vowels']); - assert.deepStrictEqual(words('tooLegit2Quit'), ['too', 'Legit', '2', 'Quit']); - assert.deepStrictEqual(words('walk500Miles'), ['walk', '500', 'Miles']); - assert.deepStrictEqual(words('xhr2Request'), ['xhr', '2', 'Request']); - assert.deepStrictEqual(words('XMLHttp'), ['XML', 'Http']); - assert.deepStrictEqual(words('XmlHTTP'), ['Xml', 'HTTP']); - assert.deepStrictEqual(words('XmlHttp'), ['Xml', 'Http']); - }); - - it('should work with compound words containing diacritical marks', function() { - assert.deepStrictEqual(words('LETTERSÆiouAreVowels'), ['LETTERS', 'Æiou', 'Are', 'Vowels']); - assert.deepStrictEqual(words('æiouAreVowels'), ['æiou', 'Are', 'Vowels']); - assert.deepStrictEqual(words('æiou2Consonants'), ['æiou', '2', 'Consonants']); - }); - - it('should not treat contractions as separate words', function() { - var postfixes = ['d', 'll', 'm', 're', 's', 't', 've']; - - lodashStable.each(["'", '\u2019'], function(apos) { - lodashStable.times(2, function(index) { - var actual = lodashStable.map(postfixes, function(postfix) { - var string = 'a b' + apos + postfix + ' c'; - return words(string[index ? 'toUpperCase' : 'toLowerCase']()); - }); - - var expected = lodashStable.map(postfixes, function(postfix) { - var words = ['a', 'b' + apos + postfix, 'c']; - return lodashStable.map(words, function(word) { - return word[index ? 'toUpperCase' : 'toLowerCase'](); - }); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - }); - - it('should not treat ordinal numbers as separate words', function() { - var ordinals = ['1st', '2nd', '3rd', '4th']; - - lodashStable.times(2, function(index) { - var expected = lodashStable.map(ordinals, function(ordinal) { - return [ordinal[index ? 'toUpperCase' : 'toLowerCase']()]; - }); - - var actual = lodashStable.map(expected, function(expectedWords) { - return words(expectedWords[0]); - }); - - assert.deepStrictEqual(actual, expected); - }); - }); - - it('should not treat mathematical operators as words', function() { - var operators = ['\xac', '\xb1', '\xd7', '\xf7'], - expected = lodashStable.map(operators, stubArray), - actual = lodashStable.map(operators, words); - - assert.deepStrictEqual(actual, expected); - }); - - it('should not treat punctuation as words', function() { - var marks = [ - '\u2012', '\u2013', '\u2014', '\u2015', - '\u2024', '\u2025', '\u2026', - '\u205d', '\u205e' - ]; - - var expected = lodashStable.map(marks, stubArray), - actual = lodashStable.map(marks, words); - - assert.deepStrictEqual(actual, expected); - }); - - it('should prevent ReDoS', function() { - var largeWordLen = 50000, - largeWord = 'A'.repeat(largeWordLen), - maxMs = 1000, - startTime = lodashStable.now(); - - assert.deepStrictEqual(words(largeWord + 'ÆiouAreVowels'), [largeWord, 'Æiou', 'Are', 'Vowels']); - - var endTime = lodashStable.now(), - timeSpent = endTime - startTime; - - assert.ok(timeSpent < maxMs, 'operation took ' + timeSpent + 'ms'); - }); -}); diff --git a/test/wrap.js b/test/wrap.js deleted file mode 100644 index 7a6bd5a453..0000000000 --- a/test/wrap.js +++ /dev/null @@ -1,46 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { noop, slice, stubA } from './utils.js'; -import wrap from '../wrap.js'; - -describe('wrap', function() { - it('should create a wrapped function', function() { - var p = wrap(lodashStable.escape, function(func, text) { - return '

' + func(text) + '

'; - }); - - assert.strictEqual(p('fred, barney, & pebbles'), '

fred, barney, & pebbles

'); - }); - - it('should provide correct `wrapper` arguments', function() { - var args; - - var wrapped = wrap(noop, function() { - args || (args = slice.call(arguments)); - }); - - wrapped(1, 2, 3); - assert.deepStrictEqual(args, [noop, 1, 2, 3]); - }); - - it('should use `_.identity` when `wrapper` is nullish', function() { - var values = [, null, undefined], - expected = lodashStable.map(values, stubA); - - var actual = lodashStable.map(values, function(value, index) { - var wrapped = index ? wrap('a', value) : wrap('a'); - return wrapped('b', 'c'); - }); - - assert.deepStrictEqual(actual, expected); - }); - - it('should use `this` binding of function', function() { - var p = wrap(lodashStable.escape, function(func) { - return '

' + func(this.text) + '

'; - }); - - var object = { 'p': p, 'text': 'fred, barney, & pebbles' }; - assert.strictEqual(object.p(), '

fred, barney, & pebbles

'); - }); -}); diff --git a/test/wrap.spec.js b/test/wrap.spec.js new file mode 100644 index 0000000000..1d12230eff --- /dev/null +++ b/test/wrap.spec.js @@ -0,0 +1,43 @@ +import lodashStable from 'lodash'; +import { noop, slice, stubA } from './utils'; +import wrap from '../src/wrap'; + +describe('wrap', () => { + it('should create a wrapped function', () => { + const p = wrap(lodashStable.escape, (func, text) => `

${func(text)}

`); + + expect(p('fred, barney, & pebbles')).toBe('

fred, barney & pebbles

'); + }); + + it('should provide correct `wrapper` arguments', () => { + let args; + + const wrapped = wrap(noop, function () { + args || (args = slice.call(arguments)); + }); + + wrapped(1, 2, 3); + expect(args).toEqual([noop, 1, 2, 3]); + }); + + it('should use `_.identity` when `wrapper` is nullish', () => { + const values = [, null, undefined]; + const expected = lodashStable.map(values, stubA); + + const actual = lodashStable.map(values, (value, index) => { + const wrapped = index ? wrap('a', value) : wrap('a'); + return wrapped('b', 'c'); + }); + + expect(actual).toEqual(expected); + }); + + it('should use `this` binding of function', () => { + const p = wrap(lodashStable.escape, function (func) { + return `

${func(this.text)}

`; + }); + + const object = { p: p, text: 'fred, barney, & pebbles' }; + expect(object.p()).toBe('

fred, barney & pebbles

'); + }); +}); diff --git a/test/xor-methods.js b/test/xor-methods.js deleted file mode 100644 index 11c31b07ad..0000000000 --- a/test/xor-methods.js +++ /dev/null @@ -1,70 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, args, LARGE_ARRAY_SIZE } from './utils.js'; - -describe('xor methods', function() { - lodashStable.each(['xor', 'xorBy', 'xorWith'], function(methodName) { - var func = _[methodName]; - - it('`_.' + methodName + '` should return the symmetric difference of two arrays', function() { - var actual = func([2, 1], [2, 3]); - assert.deepStrictEqual(actual, [1, 3]); - }); - - it('`_.' + methodName + '` should return the symmetric difference of multiple arrays', function() { - var actual = func([2, 1], [2, 3], [3, 4]); - assert.deepStrictEqual(actual, [1, 4]); - - actual = func([1, 2], [2, 1], [1, 2]); - assert.deepStrictEqual(actual, []); - }); - - it('`_.' + methodName + '` should return an empty array when comparing the same array', function() { - var array = [1], - actual = func(array, array, array); - - assert.deepStrictEqual(actual, []); - }); - - it('`_.' + methodName + '` should return an array of unique values', function() { - var actual = func([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]); - assert.deepStrictEqual(actual, [1, 4]); - - actual = func([1, 1]); - assert.deepStrictEqual(actual, [1]); - }); - - it('`_.' + methodName + '` should return a new array when a single array is given', function() { - var array = [1]; - assert.notStrictEqual(func(array), array); - }); - - it('`_.' + methodName + '` should ignore individual secondary arguments', function() { - var array = [0]; - assert.deepStrictEqual(func(array, 3, null, { '0': 1 }), array); - }); - - it('`_.' + methodName + '` should ignore values that are not arrays or `arguments` objects', function() { - var array = [1, 2]; - assert.deepStrictEqual(func(array, 3, { '0': 1 }, null), array); - assert.deepStrictEqual(func(null, array, null, [2, 3]), [1, 3]); - assert.deepStrictEqual(func(array, null, args, null), [3]); - }); - - it('`_.' + methodName + '` should return a wrapped value when chaining', function() { - var wrapped = _([1, 2, 3])[methodName]([5, 2, 1, 4]); - assert.ok(wrapped instanceof _); - }); - - it('`_.' + methodName + '` should work when in a lazy sequence before `head` or `last`', function() { - var array = lodashStable.range(LARGE_ARRAY_SIZE + 1), - wrapped = _(array).slice(1)[methodName]([LARGE_ARRAY_SIZE, LARGE_ARRAY_SIZE + 1]); - - var actual = lodashStable.map(['head', 'last'], function(methodName) { - return wrapped[methodName](); - }); - - assert.deepEqual(actual, [1, LARGE_ARRAY_SIZE + 1]); - }); - }); -}); diff --git a/test/xor-methods.spec.js b/test/xor-methods.spec.js new file mode 100644 index 0000000000..7111b49700 --- /dev/null +++ b/test/xor-methods.spec.js @@ -0,0 +1,71 @@ +import lodashStable from 'lodash'; +import { _, args, LARGE_ARRAY_SIZE } from './utils'; + +describe('xor methods', () => { + lodashStable.each(['xor', 'xorBy', 'xorWith'], (methodName) => { + const func = _[methodName]; + + it(`\`_.${methodName}\` should return the symmetric difference of two arrays`, () => { + const actual = func([2, 1], [2, 3]); + expect(actual).toEqual([1, 3]); + }); + + it(`\`_.${methodName}\` should return the symmetric difference of multiple arrays`, () => { + let actual = func([2, 1], [2, 3], [3, 4]); + expect(actual).toEqual([1, 4]); + + actual = func([1, 2], [2, 1], [1, 2]); + expect(actual).toEqual([]); + }); + + it(`\`_.${methodName}\` should return an empty array when comparing the same array`, () => { + const array = [1]; + const actual = func(array, array, array); + + expect(actual).toEqual([]); + }); + + it(`\`_.${methodName}\` should return an array of unique values`, () => { + let actual = func([1, 1, 2, 5], [2, 2, 3, 5], [3, 4, 5, 5]); + expect(actual).toEqual([1, 4]); + + actual = func([1, 1]); + expect(actual).toEqual([1]); + }); + + it(`\`_.${methodName}\` should return a new array when a single array is given`, () => { + const array = [1]; + expect(func(array)).not.toBe(array); + }); + + it(`\`_.${methodName}\` should ignore individual secondary arguments`, () => { + const array = [0]; + expect(func(array, 3, null, { 0: 1 })).toEqual(array); + }); + + it(`\`_.${methodName}\` should ignore values that are not arrays or \`arguments\` objects`, () => { + const array = [1, 2]; + expect(func(array, 3, { 0: 1 }, null)).toEqual(array); + expect(func(null, array, null, [2, 3])).toEqual([1, 3]); + expect(func(array, null, args, null)).toEqual([3]); + }); + + it(`\`_.${methodName}\` should return a wrapped value when chaining`, () => { + const wrapped = _([1, 2, 3])[methodName]([5, 2, 1, 4]); + expect(wrapped instanceof _).toBeTruthy(); + }); + + it(`\`_.${methodName}\` should work when in a lazy sequence before \`head\` or \`last\``, () => { + const array = lodashStable.range(LARGE_ARRAY_SIZE + 1); + const wrapped = _(array) + .slice(1) + [methodName]([LARGE_ARRAY_SIZE, LARGE_ARRAY_SIZE + 1]); + + const actual = lodashStable.map(['head', 'last'], (methodName) => + wrapped[methodName](), + ); + + expect(actual).toEqual([1, LARGE_ARRAY_SIZE + 1]); + }); + }); +}); diff --git a/test/xorBy.js b/test/xorBy.js deleted file mode 100644 index 02b9250c64..0000000000 --- a/test/xorBy.js +++ /dev/null @@ -1,23 +0,0 @@ -import assert from 'assert'; -import { slice } from './utils.js'; -import xorBy from '../xorBy.js'; - -describe('xorBy', function() { - it('should accept an `iteratee`', function() { - var actual = xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); - assert.deepStrictEqual(actual, [1.2, 3.4]); - - actual = xorBy([{ 'x': 1 }], [{ 'x': 2 }, { 'x': 1 }], 'x'); - assert.deepStrictEqual(actual, [{ 'x': 2 }]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - xorBy([2.1, 1.2], [2.3, 3.4], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [2.3]); - }); -}); diff --git a/test/xorBy.spec.js b/test/xorBy.spec.js new file mode 100644 index 0000000000..7eff0a1765 --- /dev/null +++ b/test/xorBy.spec.js @@ -0,0 +1,22 @@ +import { slice } from './utils'; +import xorBy from '../src/xorBy'; + +describe('xorBy', () => { + it('should accept an `iteratee`', () => { + let actual = xorBy([2.1, 1.2], [2.3, 3.4], Math.floor); + expect(actual).toEqual([1.2, 3.4]); + + actual = xorBy([{ x: 1 }], [{ x: 2 }, { x: 1 }], 'x'); + expect(actual).toEqual([{ x: 2 }]); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + xorBy([2.1, 1.2], [2.3, 3.4], function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([2.3]); + }); +}); diff --git a/test/xorWith.spec.js b/test/xorWith.spec.js new file mode 100644 index 0000000000..4668194f5e --- /dev/null +++ b/test/xorWith.spec.js @@ -0,0 +1,18 @@ +import lodashStable from 'lodash'; +import xorWith from '../src/xorWith'; + +describe('xorWith', () => { + it('should work with a `comparator`', () => { + const objects = [ + { x: 1, y: 2 }, + { x: 2, y: 1 }, + ]; + const others = [ + { x: 1, y: 1 }, + { x: 1, y: 2 }, + ]; + const actual = xorWith(objects, others, lodashStable.isEqual); + + expect(actual).toEqual([objects[1], others[0]]); + }); +}); diff --git a/test/xorWith.test.js b/test/xorWith.test.js deleted file mode 100644 index 83551e2cde..0000000000 --- a/test/xorWith.test.js +++ /dev/null @@ -1,13 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import xorWith from '../xorWith.js'; - -describe('xorWith', function() { - it('should work with a `comparator`', function() { - var objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }], - others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }], - actual = xorWith(objects, others, lodashStable.isEqual); - - assert.deepStrictEqual(actual, [objects[1], others[0]]); - }); -}); diff --git a/test/zipObject-methods.js b/test/zipObject-methods.js deleted file mode 100644 index 02c4fbaa7c..0000000000 --- a/test/zipObject-methods.js +++ /dev/null @@ -1,39 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { _, LARGE_ARRAY_SIZE, square, isEven } from './utils.js'; - -describe('zipObject methods', function() { - lodashStable.each(['zipObject', 'zipObjectDeep'], function(methodName) { - var func = _[methodName], - object = { 'barney': 36, 'fred': 40 }, - isDeep = methodName == 'zipObjectDeep'; - - it('`_.' + methodName + '` should zip together key/value arrays into an object', function() { - var actual = func(['barney', 'fred'], [36, 40]); - assert.deepStrictEqual(actual, object); - }); - - it('`_.' + methodName + '` should ignore extra `values`', function() { - assert.deepStrictEqual(func(['a'], [1, 2]), { 'a': 1 }); - }); - - it('`_.' + methodName + '` should assign `undefined` values for extra `keys`', function() { - assert.deepStrictEqual(func(['a', 'b'], [1]), { 'a': 1, 'b': undefined }); - }); - - it('`_.' + methodName + '` should ' + (isDeep ? '' : 'not ') + 'support deep paths', function() { - lodashStable.each(['a.b.c', ['a', 'b', 'c']], function(path, index) { - var expected = isDeep ? ({ 'a': { 'b': { 'c': 1 } } }) : (index ? { 'a,b,c': 1 } : { 'a.b.c': 1 }); - assert.deepStrictEqual(func([path], [1]), expected); - }); - }); - - it('`_.' + methodName + '` should work in a lazy sequence', function() { - var values = lodashStable.range(LARGE_ARRAY_SIZE), - props = lodashStable.map(values, function(value) { return 'key' + value; }), - actual = _(props)[methodName](values).map(square).filter(isEven).take().value(); - - assert.deepEqual(actual, _.take(_.filter(_.map(func(props, values), square), isEven))); - }); - }); -}); diff --git a/test/zipObject-methods.spec.js b/test/zipObject-methods.spec.js new file mode 100644 index 0000000000..5e4d450c08 --- /dev/null +++ b/test/zipObject-methods.spec.js @@ -0,0 +1,34 @@ +import lodashStable from 'lodash'; +import { _, LARGE_ARRAY_SIZE, square, isEven } from './utils'; + +describe('zipObject methods', () => { + lodashStable.each(['zipObject', 'zipObjectDeep'], (methodName) => { + const func = _[methodName]; + const object = { barney: 36, fred: 40 }; + const isDeep = methodName === 'zipObjectDeep'; + + it(`\`_.${methodName}\` should zip together key/value arrays into an object`, () => { + const actual = func(['barney', 'fred'], [36, 40]); + expect(actual).toEqual(object); + }); + + it(`\`_.${methodName}\` should ignore extra \`values\``, () => { + expect(func(['a'], [1, 2])).toEqual({ a: 1 }); + }); + + it(`\`_.${methodName}\` should assign \`undefined\` values for extra \`keys\``, () => { + expect(func(['a', 'b'], [1])).toEqual({ a: 1, b: undefined }); + }); + + it(`\`_.${methodName}\` should ${isDeep ? '' : 'not '}support deep paths`, () => { + lodashStable.each(['a.b.c', ['a', 'b', 'c']], (path, index) => { + const expected = isDeep + ? { a: { b: { c: 1 } } } + : index + ? { 'a,b,c': 1 } + : { 'a.b.c': 1 }; + expect(func([path], [1])).toEqual(expected); + }); + }); + }); +}); diff --git a/test/zipWith.js b/test/zipWith.js deleted file mode 100644 index f6faaa4602..0000000000 --- a/test/zipWith.js +++ /dev/null @@ -1,48 +0,0 @@ -import assert from 'assert'; -import lodashStable from 'lodash'; -import { slice } from './utils.js'; -import zipWith from '../zipWith.js'; -import zip from '../zip.js'; - -describe('zipWith', function() { - it('should zip arrays combining grouped elements with `iteratee`', function() { - var array1 = [1, 2, 3], - array2 = [4, 5, 6], - array3 = [7, 8, 9]; - - var actual = zipWith(array1, array2, array3, function(a, b, c) { - return a + b + c; - }); - - assert.deepStrictEqual(actual, [12, 15, 18]); - - var actual = zipWith(array1, [], function(a, b) { - return a + (b || 0); - }); - - assert.deepStrictEqual(actual, [1, 2, 3]); - }); - - it('should provide correct `iteratee` arguments', function() { - var args; - - zipWith([1, 2], [3, 4], [5, 6], function() { - args || (args = slice.call(arguments)); - }); - - assert.deepStrictEqual(args, [1, 3, 5]); - }); - - it('should perform a basic zip when `iteratee` is nullish', function() { - var array1 = [1, 2], - array2 = [3, 4], - values = [, null, undefined], - expected = lodashStable.map(values, lodashStable.constant(zip(array1, array2))); - - var actual = lodashStable.map(values, function(value, index) { - return index ? zipWith(array1, array2, value) : zipWith(array1, array2); - }); - - assert.deepStrictEqual(actual, expected); - }); -}); diff --git a/test/zipWith.spec.js b/test/zipWith.spec.js new file mode 100644 index 0000000000..087a86e6a1 --- /dev/null +++ b/test/zipWith.spec.js @@ -0,0 +1,41 @@ +import lodashStable from 'lodash'; +import { slice } from './utils'; +import zipWith from '../src/zipWith'; +import zip from '../src/zip'; + +describe('zipWith', () => { + it('should zip arrays combining grouped elements with `iteratee`', () => { + const array1 = [1, 2, 3]; + const array2 = [4, 5, 6]; + const array3 = [7, 8, 9]; + + let actual = zipWith(array1, array2, array3, (a, b, c) => a + b + c); + expect(actual).toEqual([12, 15, 18]); + + actual = zipWith(array1, [], (a, b) => a + (b || 0)); + expect(actual).toEqual([1, 2, 3]); + }); + + it('should provide correct `iteratee` arguments', () => { + let args; + + zipWith([1, 2], [3, 4], [5, 6], function () { + args || (args = slice.call(arguments)); + }); + + expect(args).toEqual([1, 3, 5]); + }); + + it('should perform a basic zip when `iteratee` is nullish', () => { + const array1 = [1, 2]; + const array2 = [3, 4]; + const values = [, null, undefined]; + const expected = lodashStable.map(values, lodashStable.constant(zip(array1, array2))); + + const actual = lodashStable.map(values, (value, index) => + index ? zipWith(array1, array2, value) : zipWith(array1, array2), + ); + + expect(actual).toEqual(expected); + }); +}); diff --git a/throttle.js b/throttle.js deleted file mode 100644 index 6fa0ba8cab..0000000000 --- a/throttle.js +++ /dev/null @@ -1,70 +0,0 @@ -import debounce from './debounce.js' -import isObject from './isObject.js' - -/** - * Creates a throttled function that only invokes `func` at most once per - * every `wait` milliseconds (or once per browser frame). The throttled function - * comes with a `cancel` method to cancel delayed `func` invocations and a - * `flush` method to immediately invoke them. Provide `options` to indicate - * whether `func` should be invoked on the leading and/or trailing edge of the - * `wait` timeout. The `func` is invoked with the last arguments provided to the - * throttled function. Subsequent calls to the throttled function return the - * result of the last `func` invocation. - * - * **Note:** If `leading` and `trailing` options are `true`, `func` is - * invoked on the trailing edge of the timeout only if the throttled function - * is invoked more than once during the `wait` timeout. - * - * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred - * until the next tick, similar to `setTimeout` with a timeout of `0`. - * - * If `wait` is omitted in an environment with `requestAnimationFrame`, `func` - * invocation will be deferred until the next frame is drawn (typically about - * 16ms). - * - * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/) - * for details over the differences between `throttle` and `debounce`. - * - * @since 0.1.0 - * @category Function - * @param {Function} func The function to throttle. - * @param {number} [wait=0] - * The number of milliseconds to throttle invocations to; if omitted, - * `requestAnimationFrame` is used (if available). - * @param {Object} [options={}] The options object. - * @param {boolean} [options.leading=true] - * Specify invoking on the leading edge of the timeout. - * @param {boolean} [options.trailing=true] - * Specify invoking on the trailing edge of the timeout. - * @returns {Function} Returns the new throttled function. - * @example - * - * // Avoid excessively updating the position while scrolling. - * jQuery(window).on('scroll', throttle(updatePosition, 100)) - * - * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes. - * const throttled = throttle(renewToken, 300000, { 'trailing': false }) - * jQuery(element).on('click', throttled) - * - * // Cancel the trailing throttled invocation. - * jQuery(window).on('popstate', throttled.cancel) - */ -function throttle(func, wait, options) { - let leading = true - let trailing = true - - if (typeof func !== 'function') { - throw new TypeError('Expected a function') - } - if (isObject(options)) { - leading = 'leading' in options ? !!options.leading : leading - trailing = 'trailing' in options ? !!options.trailing : trailing - } - return debounce(func, wait, { - leading, - trailing, - 'maxWait': wait - }) -} - -export default throttle diff --git a/times.js b/times.js deleted file mode 100644 index 47804627d3..0000000000 --- a/times.js +++ /dev/null @@ -1,42 +0,0 @@ -/** Used as references for various `Number` constants. */ -const MAX_SAFE_INTEGER = 9007199254740991 - -/** Used as references for the maximum length and index of an array. */ -const MAX_ARRAY_LENGTH = 4294967295 - -/** - * Invokes the iteratee `n` times, returning an array of the results of - * each invocation. The iteratee is invoked with one argument: (index). - * - * @since 0.1.0 - * @category Util - * @param {number} n The number of times to invoke `iteratee`. - * @param {Function} iteratee The function invoked per iteration. - * @returns {Array} Returns the array of results. - * @example - * - * times(3, String) - * // => ['0', '1', '2'] - * - * times(4, () => 0) - * // => [0, 0, 0, 0] - */ -function times(n, iteratee) { - if (n < 1 || n > MAX_SAFE_INTEGER) { - return [] - } - let index = -1 - const length = Math.min(n, MAX_ARRAY_LENGTH) - const result = new Array(length) - while (++index < length) { - result[index] = iteratee(index) - } - index = MAX_ARRAY_LENGTH - n -= MAX_ARRAY_LENGTH - while (++index < n) { - iteratee(index) - } - return result -} - -export default times diff --git a/toArray.js b/toArray.js deleted file mode 100644 index 48c132b4d4..0000000000 --- a/toArray.js +++ /dev/null @@ -1,55 +0,0 @@ -import copyArray from './.internal/copyArray.js' -import getTag from './.internal/getTag.js' -import isArrayLike from './isArrayLike.js' -import isString from './isString.js' -import iteratorToArray from './.internal/iteratorToArray.js' -import mapToArray from './.internal/mapToArray.js' -import setToArray from './.internal/setToArray.js' -import stringToArray from './.internal/stringToArray.js' -import values from './values.js' - -/** `Object#toString` result references. */ -const mapTag = '[object Map]' -const setTag = '[object Set]' - -/** Built-in value references. */ -const symIterator = Symbol.iterator - -/** - * Converts `value` to an array. - * - * @since 0.1.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {Array} Returns the converted array. - * @example - * - * toArray({ 'a': 1, 'b': 2 }) - * // => [1, 2] - * - * toArray('abc') - * // => ['a', 'b', 'c'] - * - * toArray(1) - * // => [] - * - * toArray(null) - * // => [] - */ -function toArray(value) { - if (!value) { - return [] - } - if (isArrayLike(value)) { - return isString(value) ? stringToArray(value) : copyArray(value) - } - if (symIterator && value[symIterator]) { - return iteratorToArray(value[symIterator]()) - } - const tag = getTag(value) - const func = tag == mapTag ? mapToArray : (tag == setTag ? setToArray : values) - - return func(value) -} - -export default toArray diff --git a/toFinite.js b/toFinite.js deleted file mode 100644 index 7718142ea0..0000000000 --- a/toFinite.js +++ /dev/null @@ -1,40 +0,0 @@ -import toNumber from './toNumber.js' - -/** Used as references for various `Number` constants. */ -const INFINITY = 1 / 0 -const MAX_INTEGER = 1.7976931348623157e+308 - -/** - * Converts `value` to a finite number. - * - * @since 4.12.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted number. - * @example - * - * toFinite(3.2) - * // => 3.2 - * - * toFinite(Number.MIN_VALUE) - * // => 5e-324 - * - * toFinite(Infinity) - * // => 1.7976931348623157e+308 - * - * toFinite('3.2') - * // => 3.2 - */ -function toFinite(value) { - if (!value) { - return value === 0 ? value : 0 - } - value = toNumber(value) - if (value === INFINITY || value === -INFINITY) { - const sign = (value < 0 ? -1 : 1) - return sign * MAX_INTEGER - } - return value === value ? value : 0 -} - -export default toFinite diff --git a/toInteger.js b/toInteger.js deleted file mode 100644 index b09a8b9822..0000000000 --- a/toInteger.js +++ /dev/null @@ -1,35 +0,0 @@ -import toFinite from './toFinite.js' - -/** - * Converts `value` to an integer. - * - * **Note:** This method is loosely based on - * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger). - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @see isInteger, isNumber, toNumber - * @example - * - * toInteger(3.2) - * // => 3 - * - * toInteger(Number.MIN_VALUE) - * // => 0 - * - * toInteger(Infinity) - * // => 1.7976931348623157e+308 - * - * toInteger('3.2') - * // => 3 - */ -function toInteger(value) { - const result = toFinite(value) - const remainder = result % 1 - - return remainder ? result - remainder : result -} - -export default toInteger diff --git a/toLength.js b/toLength.js deleted file mode 100644 index 4425c10750..0000000000 --- a/toLength.js +++ /dev/null @@ -1,45 +0,0 @@ -import toInteger from './toInteger.js' - -/** Used as references for the maximum length and index of an array. */ -const MAX_ARRAY_LENGTH = 4294967295 - -/** - * Converts `value` to an integer suitable for use as the length of an - * array-like object. - * - * **Note:** This method is based on - * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength). - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * toLength(3.2) - * // => 3 - * - * toLength(Number.MIN_VALUE) - * // => 0 - * - * toLength(Infinity) - * // => 4294967295 - * - * toLength('3.2') - * // => 3 - */ -function toLength(value) { - if (!value) { - return 0 - } - value = toInteger(value) - if (value < 0) { - return 0 - } - if (value > MAX_ARRAY_LENGTH) { - return MAX_ARRAY_LENGTH - } - return value -} - -export default toLength diff --git a/toNumber.js b/toNumber.js deleted file mode 100644 index d4bbca4d14..0000000000 --- a/toNumber.js +++ /dev/null @@ -1,65 +0,0 @@ -import isObject from './isObject.js' -import isSymbol from './isSymbol.js' - -/** Used as references for various `Number` constants. */ -const NAN = 0 / 0 - -/** Used to match leading and trailing whitespace. */ -const reTrim = /^\s+|\s+$/g - -/** Used to detect bad signed hexadecimal string values. */ -const reIsBadHex = /^[-+]0x[0-9a-f]+$/i - -/** Used to detect binary string values. */ -const reIsBinary = /^0b[01]+$/i - -/** Used to detect octal string values. */ -const reIsOctal = /^0o[0-7]+$/i - -/** Built-in method references without a dependency on `root`. */ -const freeParseInt = parseInt - -/** - * Converts `value` to a number. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to process. - * @returns {number} Returns the number. - * @see isInteger, toInteger, isNumber - * @example - * - * toNumber(3.2) - * // => 3.2 - * - * toNumber(Number.MIN_VALUE) - * // => 5e-324 - * - * toNumber(Infinity) - * // => Infinity - * - * toNumber('3.2') - * // => 3.2 - */ -function toNumber(value) { - if (typeof value === 'number') { - return value - } - if (isSymbol(value)) { - return NAN - } - if (isObject(value)) { - const other = typeof value.valueOf === 'function' ? value.valueOf() : value - value = isObject(other) ? `${other}` : other - } - if (typeof value !== 'string') { - return value === 0 ? value : +value - } - value = value.replace(reTrim, '') - const isBinary = reIsBinary.test(value) - return (isBinary || reIsOctal.test(value)) - ? freeParseInt(value.slice(2), isBinary ? 2 : 8) - : (reIsBadHex.test(value) ? NAN : +value) -} - -export default toNumber diff --git a/toPath.js b/toPath.js deleted file mode 100644 index 1937937816..0000000000 --- a/toPath.js +++ /dev/null @@ -1,29 +0,0 @@ -import map from './map.js' -import copyArray from './.internal/copyArray.js' -import isSymbol from './isSymbol.js' -import stringToPath from './.internal/stringToPath.js' -import toKey from './.internal/toKey.js' - -/** - * Converts `value` to a property path array. - * - * @since 4.0.0 - * @category Util - * @param {*} value The value to convert. - * @returns {Array} Returns the new property path array. - * @example - * - * toPath('a.b.c') - * // => ['a', 'b', 'c'] - * - * toPath('a[0].b.c') - * // => ['a', '0', 'b', 'c'] - */ -function toPath(value) { - if (Array.isArray(value)) { - return map(value, toKey) - } - return isSymbol(value) ? [value] : copyArray(stringToPath(value)) -} - -export default toPath diff --git a/toPlainObject.js b/toPlainObject.js deleted file mode 100644 index b19a251dc6..0000000000 --- a/toPlainObject.js +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Converts `value` to a plain object flattening inherited enumerable string - * keyed properties of `value` to own properties of the plain object. - * - * @since 3.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {Object} Returns the converted plain object. - * @example - * - * function Foo() { - * this.b = 2 - * } - * - * Foo.prototype.c = 3 - * - * assign({ 'a': 1 }, new Foo) - * // => { 'a': 1, 'b': 2 } - * - * assign({ 'a': 1 }, toPlainObject(new Foo)) - * // => { 'a': 1, 'b': 2, 'c': 3 } - */ -function toPlainObject(value) { - value = Object(value) - const result = {} - for (const key in value) { - result[key] = value[key] - } - return result -} - -export default toPlainObject diff --git a/toSafeInteger.js b/toSafeInteger.js deleted file mode 100644 index 2cf3ee94d1..0000000000 --- a/toSafeInteger.js +++ /dev/null @@ -1,42 +0,0 @@ -import toInteger from './toInteger.js' - -/** Used as references for various `Number` constants. */ -const MAX_SAFE_INTEGER = 9007199254740991 - -/** - * Converts `value` to a safe integer. A safe integer can be compared and - * represented correctly. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {number} Returns the converted integer. - * @example - * - * toSafeInteger(3.2) - * // => 3 - * - * toSafeInteger(Number.MIN_VALUE) - * // => 0 - * - * toSafeInteger(Infinity) - * // => 9007199254740991 - * - * toSafeInteger('3.2') - * // => 3 - */ -function toSafeInteger(value) { - if (!value) { - return value === 0 ? value : 0 - } - value = toInteger(value) - if (value < -MAX_SAFE_INTEGER) { - return -MAX_SAFE_INTEGER - } - if (value > MAX_SAFE_INTEGER) { - return MAX_SAFE_INTEGER - } - return value -} - -export default toSafeInteger diff --git a/toString.js b/toString.js deleted file mode 100644 index 6ba676506d..0000000000 --- a/toString.js +++ /dev/null @@ -1,44 +0,0 @@ -import isSymbol from './isSymbol.js' - -/** Used as references for various `Number` constants. */ -const INFINITY = 1 / 0 - -/** - * Converts `value` to a string. An empty string is returned for `null` - * and `undefined` values. The sign of `-0` is preserved. - * - * @since 4.0.0 - * @category Lang - * @param {*} value The value to convert. - * @returns {string} Returns the converted string. - * @example - * - * toString(null) - * // => '' - * - * toString(-0) - * // => '-0' - * - * toString([1, 2, 3]) - * // => '1,2,3' - */ -function toString(value) { - if (value == null) { - return '' - } - // Exit early for strings to avoid a performance hit in some environments. - if (typeof value === 'string') { - return value - } - if (Array.isArray(value)) { - // Recursively convert values (susceptible to call stack limits). - return `${value.map((other) => other == null ? other : toString(other))}` - } - if (isSymbol(value)) { - return value.toString() - } - const result = `${value}` - return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result -} - -export default toString diff --git a/transform.js b/transform.js deleted file mode 100644 index b421cb9bea..0000000000 --- a/transform.js +++ /dev/null @@ -1,59 +0,0 @@ -import arrayEach from './.internal/arrayEach.js' -import baseForOwn from './.internal/baseForOwn.js' -import isBuffer from './isBuffer.js' -import isObject from './isObject.js' -import isTypedArray from './isTypedArray.js' - -/** - * An alternative to `reduce` this method transforms `object` to a new - * `accumulator` object which is the result of running each of its own - * enumerable string keyed properties thru `iteratee`, with each invocation - * potentially mutating the `accumulator` object. If `accumulator` is not - * provided, a new object with the same `[[Prototype]]` will be used. The - * iteratee is invoked with four arguments: (accumulator, value, key, object). - * Iteratee functions may exit iteration early by explicitly returning `false`. - * - * @since 1.3.0 - * @category Object - * @param {Object} object The object to iterate over. - * @param {Function} iteratee The function invoked per iteration. - * @param {*} [accumulator] The custom accumulator value. - * @returns {*} Returns the accumulated value. - * @see reduce, reduceRight - * @example - * - * transform([2, 3, 4], (result, n) => { - * result.push(n *= n) - * return n % 2 == 0 - * }, []) - * // => [4, 9] - * - * transform({ 'a': 1, 'b': 2, 'c': 1 }, (result, value, key) => { - * (result[value] || (result[value] = [])).push(key) - * }, {}) - * // => { '1': ['a', 'c'], '2': ['b'] } - */ -function transform(object, iteratee, accumulator) { - const isArr = Array.isArray(object) - const isArrLike = isArr || isBuffer(object) || isTypedArray(object) - - if (accumulator == null) { - const Ctor = object && object.constructor - if (isArrLike) { - accumulator = isArr ? new Ctor : [] - } - else if (isObject(object)) { - accumulator = typeof Ctor === 'function' - ? Object.create(Object.getPrototypeOf(object)) - : {} - } - else { - accumulator = {} - } - } - (isArrLike ? arrayEach : baseForOwn)(object, (value, index, object) => - iteratee(accumulator, value, index, object)) - return accumulator -} - -export default transform diff --git a/trim.js b/trim.js deleted file mode 100644 index b7ac73171d..0000000000 --- a/trim.js +++ /dev/null @@ -1,38 +0,0 @@ -import castSlice from './.internal/castSlice.js' -import charsEndIndex from './.internal/charsEndIndex.js' -import charsStartIndex from './.internal/charsStartIndex.js' -import stringToArray from './.internal/stringToArray.js' - -/** - * Removes leading and trailing whitespace or specified characters from `string`. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to trim. - * @param {string} [chars=whitespace] The characters to trim. - * @returns {string} Returns the trimmed string. - * @see trimEnd, trimStart - * @example - * - * trim(' abc ') - * // => 'abc' - * - * trim('-_-abc-_-', '_-') - * // => 'abc' - */ -function trim(string, chars) { - if (string && chars === undefined) { - return string.trim() - } - if (!string || !chars) { - return (string || '') - } - const strSymbols = stringToArray(string) - const chrSymbols = stringToArray(chars) - const start = charsStartIndex(strSymbols, chrSymbols) - const end = charsEndIndex(strSymbols, chrSymbols) + 1 - - return castSlice(strSymbols, start, end).join('') -} - -export default trim diff --git a/trimEnd.js b/trimEnd.js deleted file mode 100644 index b802521baf..0000000000 --- a/trimEnd.js +++ /dev/null @@ -1,36 +0,0 @@ -import castSlice from './.internal/castSlice.js' -import charsEndIndex from './.internal/charsEndIndex.js' -import stringToArray from './.internal/stringToArray.js' - -const methodName = ''.trimRight ? 'trimRight': 'trimEnd' - -/** - * Removes trailing whitespace or specified characters from `string`. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to trim. - * @param {string} [chars=whitespace] The characters to trim. - * @returns {string} Returns the trimmed string. - * @see trim, trimStart - * @example - * - * trimEnd(' abc ') - * // => ' abc' - * - * trimEnd('-_-abc-_-', '_-') - * // => '-_-abc' - */ -function trimEnd(string, chars) { - if (string && chars === undefined) { - return string[methodName]() - } - if (!string || !chars) { - return (string || '') - } - const strSymbols = stringToArray(string) - const end = charsEndIndex(strSymbols, stringToArray(chars)) + 1 - return castSlice(strSymbols, 0, end).join('') -} - -export default trimEnd diff --git a/trimStart.js b/trimStart.js deleted file mode 100644 index 6138a88e8f..0000000000 --- a/trimStart.js +++ /dev/null @@ -1,36 +0,0 @@ -import castSlice from './.internal/castSlice.js' -import charsStartIndex from './.internal/charsStartIndex.js' -import stringToArray from './.internal/stringToArray.js' - -const methodName = ''.trimLeft ? 'trimLeft' : 'trimStart' - -/** - * Removes leading whitespace or specified characters from `string`. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to trim. - * @param {string} [chars=whitespace] The characters to trim. - * @returns {string} Returns the trimmed string. - * @see trim, trimEnd - * @example - * - * trimStart(' abc ') - * // => 'abc ' - * - * trimStart('-_-abc-_-', '_-') - * // => 'abc-_-' - */ -function trimStart(string, chars) { - if (string && chars === undefined) { - return string[methodName]() - } - if (!string || !chars) { - return (string || '') - } - const strSymbols = stringToArray(string) - const start = charsStartIndex(strSymbols, stringToArray(chars)) - return castSlice(strSymbols, start).join('') -} - -export default trimStart diff --git a/truncate.js b/truncate.js deleted file mode 100644 index f3a928cdc1..0000000000 --- a/truncate.js +++ /dev/null @@ -1,113 +0,0 @@ -import baseToString from './.internal/baseToString.js' -import castSlice from './.internal/castSlice.js' -import hasUnicode from './.internal/hasUnicode.js' -import isObject from './isObject.js' -import isRegExp from './isRegExp.js' -import stringSize from './.internal/stringSize.js' -import stringToArray from './.internal/stringToArray.js' -import toString from './toString.js' - -/** Used as default options for `truncate`. */ -const DEFAULT_TRUNC_LENGTH = 30 -const DEFAULT_TRUNC_OMISSION = '...' - -/** Used to match `RegExp` flags from their coerced string values. */ -const reFlags = /\w*$/ - -/** - * Truncates `string` if it's longer than the given maximum string length. - * The last characters of the truncated string are replaced with the omission - * string which defaults to "...". - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to truncate. - * @param {Object} [options={}] The options object. - * @param {number} [options.length=30] The maximum string length. - * @param {string} [options.omission='...'] The string to indicate text is omitted. - * @param {RegExp|string} [options.separator] The separator pattern to truncate to. - * @returns {string} Returns the truncated string. - * @see replace - * @example - * - * truncate('hi-diddly-ho there, neighborino') - * // => 'hi-diddly-ho there, neighbo...' - * - * truncate('hi-diddly-ho there, neighborino', { - * 'length': 24, - * 'separator': ' ' - * }) - * // => 'hi-diddly-ho there,...' - * - * truncate('hi-diddly-ho there, neighborino', { - * 'length': 24, - * 'separator': /,? +/ - * }) - * // => 'hi-diddly-ho there...' - * - * truncate('hi-diddly-ho there, neighborino', { - * 'omission': ' [...]' - * }) - * // => 'hi-diddly-ho there, neig [...]' - */ -function truncate(string, options) { - let separator - let length = DEFAULT_TRUNC_LENGTH - let omission = DEFAULT_TRUNC_OMISSION - - if (isObject(options)) { - separator = 'separator' in options ? options.separator : separator - length = 'length' in options ? options.length : length - omission = 'omission' in options ? baseToString(options.omission) : omission - } - - string = toString(string) - - let strSymbols - let strLength = string.length - if (hasUnicode(string)) { - strSymbols = stringToArray(string) - strLength = strSymbols.length - } - if (length >= strLength) { - return string - } - let end = length - stringSize(omission) - if (end < 1) { - return omission - } - let result = strSymbols - ? castSlice(strSymbols, 0, end).join('') - : string.slice(0, end) - - if (separator === undefined) { - return result + omission - } - if (strSymbols) { - end += (result.length - end) - } - if (isRegExp(separator)) { - if (string.slice(end).search(separator)) { - let match - let newEnd - const substring = result - - if (!separator.global) { - separator = RegExp(separator.source, `${reFlags.exec(separator) || ''}g`) - } - separator.lastIndex = 0 - while ((match = separator.exec(substring))) { - newEnd = match.index - } - result = result.slice(0, newEnd === undefined ? end : newEnd) - } - } else if (string.indexOf(baseToString(separator), end) != end) { - const index = result.lastIndexOf(separator) - if (index > -1) { - result = result.slice(0, index) - } - } - return result + omission -} - -export default truncate diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000000..1983db0b6f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "declaration": true, + "declarationMap": true, + "emitDeclarationOnly": true, + "esModuleInterop": true, + "lib": ["dom", "es2015", "es2016", "es2017", "es2018", "es2019", "es2020"], + "module": "commonjs", + "moduleResolution": "node", + "target": "es2020", + // Enhance strictness. + "noImplicitReturns": true, + "noUnusedLocals": true, + "strict": true, + } +} diff --git a/unescape.js b/unescape.js deleted file mode 100644 index f28d0b3ffc..0000000000 --- a/unescape.js +++ /dev/null @@ -1,38 +0,0 @@ -/** Used to map HTML entities to characters. */ -const htmlUnescapes = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - ''': "'" -} - -/** Used to match HTML entities and HTML characters. */ -const reEscapedHtml = /&(?:amp|lt|gt|quot|#(0+)?39);/g -const reHasEscapedHtml = RegExp(reEscapedHtml.source) - -/** - * The inverse of `escape`this method converts the HTML entities - * `&`, `<`, `>`, `"` and `'` in `string` to - * their corresponding characters. - * - * **Note:** No other HTML entities are unescaped. To unescape additional - * HTML entities use a third-party library like [_he_](https://mths.be/he). - * - * @since 0.6.0 - * @category String - * @param {string} [string=''] The string to unescape. - * @returns {string} Returns the unescaped string. - * @see escape, escapeRegExp - * @example - * - * unescape('fred, barney, & pebbles') - * // => 'fred, barney, & pebbles' - */ -function unescape(string) { - return (string && reHasEscapedHtml.test(string)) - ? string.replace(reEscapedHtml, (entity) => (htmlUnescapes[entity] || "'")) - : (string || '') -} - -export default unescape diff --git a/union.js b/union.js deleted file mode 100644 index 77b3b6b72c..0000000000 --- a/union.js +++ /dev/null @@ -1,24 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' -import baseUniq from './.internal/baseUniq.js' -import isArrayLikeObject from './isArrayLikeObject.js' - -/** - * Creates an array of unique values, in order, from all given arrays using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of combined values. - * @see difference, unionBy, unionWith, without, xor, xorBy - * @example - * - * union([2, 3], [1, 2]) - * // => [2, 3, 1] - */ -function union(...arrays) { - return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true)) -} - -export default union diff --git a/unionBy.js b/unionBy.js deleted file mode 100644 index 893433907f..0000000000 --- a/unionBy.js +++ /dev/null @@ -1,32 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' -import baseUniq from './.internal/baseUniq.js' -import isArrayLikeObject from './isArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `union` except that it accepts `iteratee` which is - * invoked for each element of each `arrays` to generate the criterion by - * which uniqueness is computed. Result values are chosen from the first - * array in which the value occurs. The iteratee is invoked with one argument: - * (value). - * - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns the new array of combined values. - * @see difference, union, unionWith, without, xor, xorBy - * @example - * - * unionBy([2.1], [1.2, 2.3], Math.floor) - * // => [2.1, 1.2] - */ -function unionBy(...arrays) { - let iteratee = last(arrays) - if (isArrayLikeObject(iteratee)) { - iteratee = undefined - } - return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), iteratee) -} - -export default unionBy diff --git a/unionWith.js b/unionWith.js deleted file mode 100644 index c5c3be9328..0000000000 --- a/unionWith.js +++ /dev/null @@ -1,32 +0,0 @@ -import baseFlatten from './.internal/baseFlatten.js' -import baseUniq from './.internal/baseUniq.js' -import isArrayLikeObject from './isArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `union` except that it accepts `comparator` which - * is invoked to compare elements of `arrays`. Result values are chosen from - * the first array in which the value occurs. The comparator is invoked - * with two arguments: (arrVal, othVal). - * - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of combined values. - * @see difference, union, unionBy, without, xor, xorBy - * @example - * - * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] - * const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }] - * - * unionWith(objects, others, isEqual) - * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] - */ -function unionWith(...arrays) { - let comparator = last(arrays) - comparator = typeof comparator === 'function' ? comparator : undefined - return baseUniq(baseFlatten(arrays, 1, isArrayLikeObject, true), undefined, comparator) -} - -export default unionWith diff --git a/uniq.js b/uniq.js deleted file mode 100644 index 68be2d0fd1..0000000000 --- a/uniq.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseUniq from './.internal/baseUniq.js' - -/** - * Creates a duplicate-free version of an array, using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons, in which only the first occurrence of each element - * is kept. The order of result values is determined by the order they occur - * in the array. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @returns {Array} Returns the new duplicate free array. - * @see uniqBy, uniqWith - * @example - * - * uniq([2, 1, 2]) - * // => [2, 1] - */ -function uniq(array) { - return (array != null && array.length) - ? baseUniq(array) - : [] -} - -export default uniq diff --git a/uniqBy.js b/uniqBy.js deleted file mode 100644 index dc24a7ce3c..0000000000 --- a/uniqBy.js +++ /dev/null @@ -1,27 +0,0 @@ -import baseUniq from './.internal/baseUniq.js' - -/** - * This method is like `uniq` except that it accepts `iteratee` which is - * invoked for each element in `array` to generate the criterion by which - * uniqueness is computed. The order of result values is determined by the - * order they occur in the array. The iteratee is invoked with one argument: - * (value). - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @see uniq, uniqWith - * @example - * - * uniqBy([2.1, 1.2, 2.3], Math.floor) - * // => [2.1, 1.2] - */ -function uniqBy(array, iteratee) { - return (array != null && array.length) - ? baseUniq(array, iteratee) - : [] -} - -export default uniqBy diff --git a/uniqWith.js b/uniqWith.js deleted file mode 100644 index 77feac13f2..0000000000 --- a/uniqWith.js +++ /dev/null @@ -1,29 +0,0 @@ -import baseUniq from './.internal/baseUniq.js' - -/** - * This method is like `uniq` except that it accepts `comparator` which - * is invoked to compare elements of `array`. The order of result values is - * determined by the order they occur in the array. The comparator is invoked - * with two arguments: (arrVal, othVal). - * - * @since 4.0.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new duplicate free array. - * @see uniq, uniqBy - * @example - * - * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }, { 'x': 1, 'y': 2 }] - * - * uniqWith(objects, isEqual) - * // => [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] - */ -function uniqWith(array, comparator) { - comparator = typeof comparator === 'function' ? comparator : undefined - return (array != null && array.length) - ? baseUniq(array, undefined, comparator) - : [] -} - -export default uniqWith diff --git a/uniqueId.js b/uniqueId.js deleted file mode 100644 index 934ece2905..0000000000 --- a/uniqueId.js +++ /dev/null @@ -1,33 +0,0 @@ -/** Used to generate unique IDs. */ -const idCounter = {} - -/** - * Generates a unique ID. If `prefix` is given, the ID is appended to it. - * - * @since 0.1.0 - * @category Util - * @param {string} [prefix=''] The value to prefix the ID with. - * @returns {string} Returns the unique ID. - * @see random - * @example - * - * uniqueId('contact_') - * // => 'contact_104' - * - * uniqueId() - * // => '105' - */ -function uniqueId(prefix='$lodash$') { - if (!idCounter[prefix]) { - idCounter[prefix] = 0 - } - - const id =++idCounter[prefix] - if (prefix === '$lodash$') { - return `${id}` - } - - return `${prefix}${id}` -} - -export default uniqueId diff --git a/unset.js b/unset.js deleted file mode 100644 index 15ddd563d5..0000000000 --- a/unset.js +++ /dev/null @@ -1,33 +0,0 @@ -import baseUnset from './.internal/baseUnset.js' - -/** - * Removes the property at `path` of `object`. - * - * **Note:** This method mutates `object`. - * - * @since 4.0.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to unset. - * @returns {boolean} Returns `true` if the property is deleted, else `false`. - * @see get, has, set - * @example - * - * const object = { 'a': [{ 'b': { 'c': 7 } }] } - * unset(object, 'a[0].b.c') - * // => true - * - * console.log(object) - * // => { 'a': [{ 'b': {} }] } - * - * unset(object, ['a', '0', 'b', 'c']) - * // => true - * - * console.log(object) - * // => { 'a': [{ 'b': {} }] } - */ -function unset(object, path) { - return object == null ? true : baseUnset(object, path) -} - -export default unset diff --git a/unzip.js b/unzip.js deleted file mode 100644 index a49220be0c..0000000000 --- a/unzip.js +++ /dev/null @@ -1,43 +0,0 @@ -import filter from './filter.js' -import map from './map.js' -import baseProperty from './.internal/baseProperty.js' -import isArrayLikeObject from './isArrayLikeObject.js' - -/** - * This method is like `zip` except that it accepts an array of grouped - * elements and creates an array regrouping the elements to their pre-zip - * configuration. - * - * @since 1.2.0 - * @category Array - * @param {Array} array The array of grouped elements to process. - * @returns {Array} Returns the new array of regrouped elements. - * @see unzipWith, zip, zipObject, zipObjectDeep, zipWith - * @example - * - * const zipped = zip(['a', 'b'], [1, 2], [true, false]) - * // => [['a', 1, true], ['b', 2, false]] - * - * unzip(zipped) - * // => [['a', 'b'], [1, 2], [true, false]] - */ -function unzip(array) { - if (!(array != null && array.length)) { - return [] - } - let length = 0 - array = filter(array, (group) => { - if (isArrayLikeObject(group)) { - length = Math.max(group.length, length) - return true - } - }) - let index = -1 - const result = new Array(length) - while (++index < length) { - result[index] = map(array, baseProperty(index)) - } - return result -} - -export default unzip diff --git a/unzipWith.js b/unzipWith.js deleted file mode 100644 index 7c2aeef289..0000000000 --- a/unzipWith.js +++ /dev/null @@ -1,31 +0,0 @@ -import map from './map.js' -import unzip from './unzip.js' - -/** - * This method is like `unzip` except that it accepts `iteratee` to specify - * how regrouped values should be combined. The iteratee is invoked with the - * elements of each group: (...group). - * - * @since 3.8.0 - * @category Array - * @param {Array} array The array of grouped elements to process. - * @param {Function} iteratee The function to combine - * regrouped values. - * @returns {Array} Returns the new array of regrouped elements. - * @example - * - * const zipped = zip([1, 2], [10, 20], [100, 200]) - * // => [[1, 10, 100], [2, 20, 200]] - * - * unzipWith(zipped, add) - * // => [3, 30, 300] - */ -function unzipWith(array, iteratee) { - if (!(array != null && array.length)) { - return [] - } - const result = unzip(array) - return map(result, (group) => iteratee.apply(undefined, group)) -} - -export default unzipWith diff --git a/update.js b/update.js deleted file mode 100644 index 75d3a94b65..0000000000 --- a/update.js +++ /dev/null @@ -1,32 +0,0 @@ -import baseUpdate from './.internal/baseUpdate.js' - -/** - * This method is like `set` except that it accepts `updater` to produce the - * value to set. Use `updateWith` to customize `path` creation. The `updater` - * is invoked with one argument: (value). - * - * **Note:** This method mutates `object`. - * - * @since 4.6.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {Function} updater The function to produce the updated value. - * @returns {Object} Returns `object`. - * @example - * - * const object = { 'a': [{ 'b': { 'c': 3 } }] } - * - * update(object, 'a[0].b.c', n => n * n) - * console.log(object.a[0].b.c) - * // => 9 - * - * update(object, 'x[0].y.z', n => n ? n + 1 : 0) - * console.log(object.x[0].y.z) - * // => 0 - */ -function update(object, path, updater) { - return object == null ? object : baseUpdate(object, path, updater) -} - -export default update diff --git a/updateWith.js b/updateWith.js deleted file mode 100644 index 60a37e1479..0000000000 --- a/updateWith.js +++ /dev/null @@ -1,30 +0,0 @@ -import baseUpdate from './.internal/baseUpdate.js' - -/** - * This method is like `update` except that it accepts `customizer` which is - * invoked to produce the objects of `path`. If `customizer` returns `undefined` - * path creation is handled by the method instead. The `customizer` is invoked - * with three arguments: (nsValue, key, nsObject). - * - * **Note:** This method mutates `object`. - * - * @since 4.6.0 - * @category Object - * @param {Object} object The object to modify. - * @param {Array|string} path The path of the property to set. - * @param {Function} updater The function to produce the updated value. - * @param {Function} [customizer] The function to customize assigned values. - * @returns {Object} Returns `object`. - * @example - * - * const object = {} - * - * updateWith(object, '[0][1]', () => 'a', Object) - * // => { '0': { '1': 'a' } } - */ -function updateWith(object, path, updater, customizer) { - customizer = typeof customizer === 'function' ? customizer : undefined - return object == null ? object : baseUpdate(object, path, updater, customizer) -} - -export default updateWith diff --git a/upperCase.js b/upperCase.js deleted file mode 100644 index db14da836a..0000000000 --- a/upperCase.js +++ /dev/null @@ -1,29 +0,0 @@ -import words from './words.js' -import toString from './toString.js' - -/** - * Converts `string`, as space separated words, to upper case. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the upper cased string. - * @see camelCase, kebabCase, lowerCase, snakeCase, startCase, upperFirst - * @example - * - * upperCase('--foo-bar') - * // => 'FOO BAR' - * - * upperCase('fooBar') - * // => 'FOO BAR' - * - * upperCase('__foo_bar__') - * // => 'FOO BAR' - */ -const upperCase = (string) => ( - words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => ( - result + (index ? ' ' : '') + word.toUpperCase() - ), '') -) - -export default upperCase diff --git a/upperFirst.js b/upperFirst.js deleted file mode 100644 index 2c07717cc8..0000000000 --- a/upperFirst.js +++ /dev/null @@ -1,21 +0,0 @@ -import createCaseFirst from './.internal/createCaseFirst.js' - -/** - * Converts the first character of `string` to upper case. - * - * @since 4.0.0 - * @category String - * @param {string} [string=''] The string to convert. - * @returns {string} Returns the converted string. - * @see camelCase, kebabCase, lowerCase, snakeCase, startCase, upperCase - * @example - * - * upperFirst('fred') - * // => 'Fred' - * - * upperFirst('FRED') - * // => 'FRED' - */ -const upperFirst = createCaseFirst('toUpperCase') - -export default upperFirst diff --git a/values.js b/values.js deleted file mode 100644 index 8d111807e3..0000000000 --- a/values.js +++ /dev/null @@ -1,33 +0,0 @@ -import baseValues from './.internal/baseValues.js' -import keys from './keys.js' - -/** - * Creates an array of the own enumerable string keyed property values of `object`. - * - * **Note:** Non-object values are coerced to objects. - * - * @since 0.1.0 - * @category Object - * @param {Object} object The object to query. - * @returns {Array} Returns the array of property values. - * @see keys, valuesIn - * @example - * - * function Foo() { - * this.a = 1 - * this.b = 2 - * } - * - * Foo.prototype.c = 3 - * - * values(new Foo) - * // => [1, 2] (iteration order is not guaranteed) - * - * values('hi') - * // => ['h', 'i'] - */ -function values(object) { - return object == null ? [] : baseValues(object, keys(object)) -} - -export default values diff --git a/without.js b/without.js deleted file mode 100644 index 60d506b375..0000000000 --- a/without.js +++ /dev/null @@ -1,26 +0,0 @@ -import baseDifference from './.internal/baseDifference.js' -import isArrayLikeObject from './isArrayLikeObject.js' - -/** - * Creates an array excluding all given values using - * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero) - * for equality comparisons. - * - * **Note:** Unlike `pull`, this method returns a new array. - * - * @since 0.1.0 - * @category Array - * @param {Array} array The array to inspect. - * @param {...*} [values] The values to exclude. - * @returns {Array} Returns the new array of filtered values. - * @see difference, union, unionBy, unionWith, xor, xorBy, xorWith - * @example - * - * without([2, 1, 2, 3], 1, 2) - * // => [3] - */ -function without(array, ...values) { - return isArrayLikeObject(array) ? baseDifference(array, values) : [] -} - -export default without diff --git a/words.js b/words.js deleted file mode 100644 index fef05f80aa..0000000000 --- a/words.js +++ /dev/null @@ -1,38 +0,0 @@ -import unicodeWords from './.internal/unicodeWords.js' - -const hasUnicodeWord = RegExp.prototype.test.bind( - /[a-z][A-Z]|[A-Z]{2}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/ -) - -/** Used to match words composed of alphanumeric characters. */ -const reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g - -function asciiWords(string) { - return string.match(reAsciiWord) -} - -/** - * Splits `string` into an array of its words. - * - * @since 3.0.0 - * @category String - * @param {string} [string=''] The string to inspect. - * @param {RegExp|string} [pattern] The pattern to match words. - * @returns {Array} Returns the words of `string`. - * @example - * - * words('fred, barney, & pebbles') - * // => ['fred', 'barney', 'pebbles'] - * - * words('fred, barney, & pebbles', /[^, ]+/g) - * // => ['fred', 'barney', '&', 'pebbles'] - */ -function words(string, pattern) { - if (pattern === undefined) { - const result = hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string) - return result || [] - } - return string.match(pattern) || [] -} - -export default words diff --git a/xor.js b/xor.js deleted file mode 100644 index 8b5e8688e8..0000000000 --- a/xor.js +++ /dev/null @@ -1,24 +0,0 @@ -import baseXor from './.internal/baseXor.js' -import isArrayLikeObject from './isArrayLikeObject.js' - -/** - * Creates an array of unique values that is the - * [symmetric difference](https://en.wikipedia.org/wiki/Symmetric_difference) - * of the given arrays. The order of result values is determined by the order - * they occur in the arrays. - * - * @since 2.4.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @returns {Array} Returns the new array of filtered values. - * @see difference, union, unionBy, unionWith, without, xorBy, xorWith - * @example - * - * xor([2, 1], [2, 3]) - * // => [1, 3] - */ -function xor(...arrays) { - return baseXor(arrays.filter(isArrayLikeObject)) -} - -export default xor diff --git a/xorBy.js b/xorBy.js deleted file mode 100644 index 87b75a3559..0000000000 --- a/xorBy.js +++ /dev/null @@ -1,31 +0,0 @@ -import baseXor from './.internal/baseXor.js' -import isArrayLikeObject from './isArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `xor` except that it accepts `iteratee` which is - * invoked for each element of each `arrays` to generate the criterion by - * which they're compared. The order of result values is determined - * by the order they occur in the arrays. The iteratee is invoked with one - * argument: (value). - * - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} iteratee The iteratee invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @see difference, union, unionBy, unionWith, without, xor, xorWith - * @example - * - * xorBy([2.1, 1.2], [2.3, 3.4], Math.floor) - * // => [1.2, 3.4] - */ -function xorBy(...arrays) { - let iteratee = last(arrays) - if (isArrayLikeObject(iteratee)) { - iteratee = undefined - } - return baseXor(arrays.filter(isArrayLikeObject), iteratee) -} - -export default xorBy diff --git a/xorWith.js b/xorWith.js deleted file mode 100644 index 97775d7061..0000000000 --- a/xorWith.js +++ /dev/null @@ -1,31 +0,0 @@ -import baseXor from './.internal/baseXor.js' -import isArrayLikeObject from './isArrayLikeObject.js' -import last from './last.js' - -/** - * This method is like `xor` except that it accepts `comparator` which is - * invoked to compare elements of `arrays`. The order of result values is - * determined by the order they occur in the arrays. The comparator is invoked - * with two arguments: (arrVal, othVal). - * - * @since 4.0.0 - * @category Array - * @param {...Array} [arrays] The arrays to inspect. - * @param {Function} [comparator] The comparator invoked per element. - * @returns {Array} Returns the new array of filtered values. - * @see difference, union, unionBy, unionWith, without, xor, xorBy - * @example - * - * const objects = [{ 'x': 1, 'y': 2 }, { 'x': 2, 'y': 1 }] - * const others = [{ 'x': 1, 'y': 1 }, { 'x': 1, 'y': 2 }] - * - * xorWith(objects, others, isEqual) - * // => [{ 'x': 2, 'y': 1 }, { 'x': 1, 'y': 1 }] - */ -function xorWith(...arrays) { - let comparator = last(arrays) - comparator = typeof comparator === 'function' ? comparator : undefined - return baseXor(arrays.filter(isArrayLikeObject), undefined, comparator) -} - -export default xorWith diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000000..23543146e6 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,3233 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 +# bun ./bun.lockb --hash: BC0324C3235CD8FE-1006a2fa0f028724-4FE78A49017D14BC-edf3b77dcc7be244 + + +"@aashutoshrathi/word-wrap@^1.2.3": + version "1.2.6" + resolved "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz" + integrity sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA== + +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13": + version "7.22.13" + resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz" + integrity sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w== + dependencies: + "@babel/highlight" "^7.22.13" + chalk "^2.4.2" + +"@babel/helper-validator-identifier@^7.22.20": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz" + integrity sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A== + +"@babel/highlight@^7.22.13": + version "7.22.20" + resolved "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz" + integrity sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg== + dependencies: + "@babel/helper-validator-identifier" "^7.22.20" + chalk "^2.4.2" + js-tokens "^4.0.0" + +"@commitlint/cli@17.7.1": + version "17.7.1" + resolved "https://registry.npmjs.org/@commitlint/cli/-/cli-17.7.1.tgz" + integrity sha512-BCm/AT06SNCQtvFv921iNhudOHuY16LswT0R3OeolVGLk8oP+Rk9TfQfgjH7QPMjhvp76bNqGFEcpKojxUNW1g== + dependencies: + "@commitlint/format" "^17.4.4" + "@commitlint/lint" "^17.7.0" + "@commitlint/load" "^17.7.1" + "@commitlint/read" "^17.5.1" + "@commitlint/types" "^17.4.4" + execa "^5.0.0" + lodash.isfunction "^3.0.9" + resolve-from "5.0.0" + resolve-global "1.0.0" + yargs "^17.0.0" + +"@commitlint/config-conventional@17.7.0": + version "17.7.0" + resolved "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-17.7.0.tgz" + integrity sha512-iicqh2o6et+9kWaqsQiEYZzfLbtoWv9uZl8kbI8EGfnc0HeGafQBF7AJ0ylN9D/2kj6txltsdyQs8+2fTMwWEw== + dependencies: + conventional-changelog-conventionalcommits "^6.1.0" + +"@commitlint/config-validator@^17.6.7": + version "17.6.7" + resolved "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-17.6.7.tgz" + integrity sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ== + dependencies: + "@commitlint/types" "^17.4.4" + ajv "^8.11.0" + +"@commitlint/ensure@^17.6.7": + version "17.6.7" + resolved "https://registry.npmjs.org/@commitlint/ensure/-/ensure-17.6.7.tgz" + integrity sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw== + dependencies: + "@commitlint/types" "^17.4.4" + lodash.camelcase "^4.3.0" + lodash.kebabcase "^4.1.1" + lodash.snakecase "^4.1.1" + lodash.startcase "^4.4.0" + lodash.upperfirst "^4.3.1" + +"@commitlint/execute-rule@^17.4.0": + version "17.4.0" + resolved "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-17.4.0.tgz" + integrity sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA== + +"@commitlint/format@^17.4.4": + version "17.4.4" + resolved "https://registry.npmjs.org/@commitlint/format/-/format-17.4.4.tgz" + integrity sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ== + dependencies: + "@commitlint/types" "^17.4.4" + chalk "^4.1.0" + +"@commitlint/is-ignored@^17.7.0": + version "17.7.0" + resolved "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-17.7.0.tgz" + integrity sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw== + dependencies: + "@commitlint/types" "^17.4.4" + semver "7.5.4" + +"@commitlint/lint@^17.7.0": + version "17.7.0" + resolved "https://registry.npmjs.org/@commitlint/lint/-/lint-17.7.0.tgz" + integrity sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA== + dependencies: + "@commitlint/is-ignored" "^17.7.0" + "@commitlint/parse" "^17.7.0" + "@commitlint/rules" "^17.7.0" + "@commitlint/types" "^17.4.4" + +"@commitlint/load@^17.7.1": + version "17.7.1" + resolved "https://registry.npmjs.org/@commitlint/load/-/load-17.7.1.tgz" + integrity sha512-S/QSOjE1ztdogYj61p6n3UbkUvweR17FQ0zDbNtoTLc+Hz7vvfS7ehoTMQ27hPSjVBpp7SzEcOQu081RLjKHJQ== + dependencies: + "@commitlint/config-validator" "^17.6.7" + "@commitlint/execute-rule" "^17.4.0" + "@commitlint/resolve-extends" "^17.6.7" + "@commitlint/types" "^17.4.4" + "@types/node" "20.4.7" + chalk "^4.1.0" + cosmiconfig "^8.0.0" + cosmiconfig-typescript-loader "^4.0.0" + lodash.isplainobject "^4.0.6" + lodash.merge "^4.6.2" + lodash.uniq "^4.5.0" + resolve-from "^5.0.0" + ts-node "^10.8.1" + typescript "^4.6.4 || ^5.0.0" + +"@commitlint/message@^17.4.2": + version "17.4.2" + resolved "https://registry.npmjs.org/@commitlint/message/-/message-17.4.2.tgz" + integrity sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q== + +"@commitlint/parse@^17.7.0": + version "17.7.0" + resolved "https://registry.npmjs.org/@commitlint/parse/-/parse-17.7.0.tgz" + integrity sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag== + dependencies: + "@commitlint/types" "^17.4.4" + conventional-changelog-angular "^6.0.0" + conventional-commits-parser "^4.0.0" + +"@commitlint/read@^17.5.1": + version "17.5.1" + resolved "https://registry.npmjs.org/@commitlint/read/-/read-17.5.1.tgz" + integrity sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg== + dependencies: + "@commitlint/top-level" "^17.4.0" + "@commitlint/types" "^17.4.4" + fs-extra "^11.0.0" + git-raw-commits "^2.0.11" + minimist "^1.2.6" + +"@commitlint/resolve-extends@^17.6.7": + version "17.6.7" + resolved "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-17.6.7.tgz" + integrity sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg== + dependencies: + "@commitlint/config-validator" "^17.6.7" + "@commitlint/types" "^17.4.4" + import-fresh "^3.0.0" + lodash.mergewith "^4.6.2" + resolve-from "^5.0.0" + resolve-global "^1.0.0" + +"@commitlint/rules@^17.7.0": + version "17.7.0" + resolved "https://registry.npmjs.org/@commitlint/rules/-/rules-17.7.0.tgz" + integrity sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA== + dependencies: + "@commitlint/ensure" "^17.6.7" + "@commitlint/message" "^17.4.2" + "@commitlint/to-lines" "^17.4.0" + "@commitlint/types" "^17.4.4" + execa "^5.0.0" + +"@commitlint/to-lines@^17.4.0": + version "17.4.0" + resolved "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-17.4.0.tgz" + integrity sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg== + +"@commitlint/top-level@^17.4.0": + version "17.4.0" + resolved "https://registry.npmjs.org/@commitlint/top-level/-/top-level-17.4.0.tgz" + integrity sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g== + dependencies: + find-up "^5.0.0" + +"@commitlint/types@^17.4.4": + version "17.4.4" + resolved "https://registry.npmjs.org/@commitlint/types/-/types-17.4.4.tgz" + integrity sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ== + dependencies: + chalk "^4.1.0" + +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + +"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.4.0": + version "4.4.0" + resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz" + integrity sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA== + dependencies: + eslint-visitor-keys "^3.3.0" + +"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.1": + version "4.8.1" + resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.8.1.tgz" + integrity sha512-PWiOzLIUAjN/w5K17PoF4n6sKBw0gqLHPhywmYHP4t1VFQQVYeb1yWsJwnMVEMl3tUHME7X/SJPZLmtG7XBDxQ== + +"@eslint/eslintrc@^2.1.2": + version "2.1.2" + resolved "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz" + integrity sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^9.6.0" + globals "^13.19.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@8.49.0": + version "8.49.0" + resolved "https://registry.npmjs.org/@eslint/js/-/js-8.49.0.tgz" + integrity sha512-1S8uAY/MTJqVx0SC4epBq+N2yhuwtNwLbJYNZyhL2pO1ZVKn5HFXav5T41Ryzy9K9V7ZId2JB2oy/W4aCd9/2w== + +"@humanwhocodes/config-array@^0.11.11": + version "0.11.11" + resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.11.tgz" + integrity sha512-N2brEuAadi0CcdeMXUkhbZB84eskAc8MEX1By6qEchoVywSgXPIjou4rYsl0V3Hj0ZnuGycGCjdNgockbzeWNA== + dependencies: + "@humanwhocodes/object-schema" "^1.2.1" + debug "^4.1.1" + minimatch "^3.0.5" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/object-schema@^1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz" + integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== + +"@jest/expect-utils@^29.7.0": + version "29.7.0" + resolved "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz" + integrity sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA== + dependencies: + jest-get-type "^29.6.3" + +"@jest/schemas@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz" + integrity sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA== + dependencies: + "@sinclair/typebox" "^0.27.8" + +"@jest/types@^29.6.3": + version "29.6.3" + resolved "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz" + integrity sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw== + dependencies: + "@jest/schemas" "^29.6.3" + "@types/istanbul-lib-coverage" "^2.0.0" + "@types/istanbul-reports" "^3.0.0" + "@types/node" "*" + "@types/yargs" "^17.0.8" + chalk "^4.0.0" + +"@jridgewell/resolve-uri@^3.0.3": + version "3.1.1" + resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/sourcemap-codec@^1.4.10": + version "1.4.15" + resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": + version "1.2.8" + resolved "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + +"@pkgr/utils@^2.3.1": + version "2.4.2" + resolved "https://registry.npmjs.org/@pkgr/utils/-/utils-2.4.2.tgz" + integrity sha512-POgTXhjrTfbTV63DiFXav4lBHiICLKKwDeaKn9Nphwj7WH6m0hMMCaJkMyRWjgtPFyRKRVoMXXjczsTQRDEhYw== + dependencies: + cross-spawn "^7.0.3" + fast-glob "^3.3.0" + is-glob "^4.0.3" + open "^9.1.0" + picocolors "^1.0.0" + tslib "^2.6.0" + +"@sinclair/typebox@^0.27.8": + version "0.27.8" + resolved "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz" + integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== + +"@tsconfig/node10@^1.0.7": + version "1.0.9" + resolved "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz" + integrity sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + +"@types/eslint@8.44.2", "@types/eslint@>=8.0.0": + version "8.44.2" + resolved "https://registry.npmjs.org/@types/eslint/-/eslint-8.44.2.tgz" + integrity sha512-sdPRb9K6iL5XZOmBubg8yiFp5yS/JdUDQsq5e6h95km91MCYMuvp7mh1fjPEYUhvHepKpZOjnEaMBR4PxjWDzg== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*": + version "1.0.1" + resolved "https://registry.npmjs.org/@types/estree/-/estree-1.0.1.tgz" + integrity sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== + +"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0": + version "2.0.4" + resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz" + integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== + +"@types/istanbul-lib-report@*": + version "3.0.0" + resolved "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz" + integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== + dependencies: + "@types/istanbul-lib-coverage" "*" + +"@types/istanbul-reports@^3.0.0": + version "3.0.1" + resolved "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz" + integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== + dependencies: + "@types/istanbul-lib-report" "*" + +"@types/jest@29.5.5": + version "29.5.5" + resolved "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz" + integrity sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg== + dependencies: + expect "^29.0.0" + pretty-format "^29.0.0" + +"@types/json-schema@*", "@types/json-schema@^7.0.12": + version "7.0.13" + resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.13.tgz" + integrity sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== + +"@types/json5@^0.0.29": + version "0.0.29" + resolved "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz" + integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== + +"@types/minimist@^1.2.0": + version "1.2.2" + resolved "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz" + integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== + +"@types/node@*", "@types/node@20.4.7": + version "20.4.7" + resolved "https://registry.npmjs.org/@types/node/-/node-20.4.7.tgz" + integrity sha512-bUBrPjEry2QUTsnuEjzjbS7voGWCc30W0qzgMf90GPeDGFRakvrz47ju+oqDAKCXLUCe39u57/ORMl/O/04/9g== + +"@types/node@*": + version "20.6.2" + resolved "https://registry.npmjs.org/@types/node/-/node-20.6.2.tgz" + integrity sha512-Y+/1vGBHV/cYk6OI1Na/LHzwnlNCAfU3ZNGrc1LdRe/LAIbdDPTTv/HU3M7yXN448aTVDq3eKRm2cg7iKLb8gw== + +"@types/normalize-package-data@^2.4.0": + version "2.4.1" + resolved "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz" + integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== + +"@types/semver@^7.5.0": + version "7.5.2" + resolved "https://registry.npmjs.org/@types/semver/-/semver-7.5.2.tgz" + integrity sha512-7aqorHYgdNO4DM36stTiGO3DvKoex9TQRwsJU6vMaFGyqpBA1MNZkz+PG3gaNUPpTAOYhT1WR7M1JyA3fbS9Cw== + +"@types/stack-utils@^2.0.0": + version "2.0.1" + resolved "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz" + integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== + +"@types/yargs@^17.0.8": + version "17.0.24" + resolved "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz" + integrity sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw== + dependencies: + "@types/yargs-parser" "*" + +"@types/yargs-parser@*": + version "21.0.0" + resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz" + integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== + +"@typescript-eslint/eslint-plugin@6.7.0", "@typescript-eslint/eslint-plugin@^5.13.0 || ^6.0.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.7.0.tgz" + integrity sha512-gUqtknHm0TDs1LhY12K2NA3Rmlmp88jK9Tx8vGZMfHeNMLE3GH2e9TRub+y+SOjuYgtOmok+wt1AyDPZqxbNag== + dependencies: + "@eslint-community/regexpp" "^4.5.1" + "@typescript-eslint/scope-manager" "6.7.0" + "@typescript-eslint/type-utils" "6.7.0" + "@typescript-eslint/utils" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + debug "^4.3.4" + graphemer "^1.4.0" + ignore "^5.2.4" + natural-compare "^1.4.0" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/parser@6.7.0", "@typescript-eslint/parser@^5.0.0 || ^6.0.0", "@typescript-eslint/parser@^6.0.0 || ^6.0.0-alpha": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.7.0.tgz" + integrity sha512-jZKYwqNpNm5kzPVP5z1JXAuxjtl2uG+5NpaMocFPTNC2EdYIgbXIPImObOkhbONxtFTTdoZstLZefbaK+wXZng== + dependencies: + "@typescript-eslint/scope-manager" "6.7.0" + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/typescript-estree" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + debug "^4.3.4" + +"@typescript-eslint/scope-manager@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.7.0.tgz" + integrity sha512-lAT1Uau20lQyjoLUQ5FUMSX/dS07qux9rYd5FGzKz/Kf8W8ccuvMyldb8hadHdK/qOI7aikvQWqulnEq2nCEYA== + dependencies: + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + +"@typescript-eslint/type-utils@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.7.0.tgz" + integrity sha512-f/QabJgDAlpSz3qduCyQT0Fw7hHpmhOzY/Rv6zO3yO+HVIdPfIWhrQoAyG+uZVtWAIS85zAyzgAFfyEr+MgBpg== + dependencies: + "@typescript-eslint/typescript-estree" "6.7.0" + "@typescript-eslint/utils" "6.7.0" + debug "^4.3.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/types@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.7.0.tgz" + integrity sha512-ihPfvOp7pOcN/ysoj0RpBPOx3HQTJTrIN8UZK+WFd3/iDeFHHqeyYxa4hQk4rMhsz9H9mXpR61IzwlBVGXtl9Q== + +"@typescript-eslint/typescript-estree@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.7.0.tgz" + integrity sha512-dPvkXj3n6e9yd/0LfojNU8VMUGHWiLuBZvbM6V6QYD+2qxqInE7J+J/ieY2iGwR9ivf/R/haWGkIj04WVUeiSQ== + dependencies: + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/visitor-keys" "6.7.0" + debug "^4.3.4" + globby "^11.1.0" + is-glob "^4.0.3" + semver "^7.5.4" + ts-api-utils "^1.0.1" + +"@typescript-eslint/utils@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.7.0.tgz" + integrity sha512-MfCq3cM0vh2slSikQYqK2Gq52gvOhe57vD2RM3V4gQRZYX4rDPnKLu5p6cm89+LJiGlwEXU8hkYxhqqEC/V3qA== + dependencies: + "@eslint-community/eslint-utils" "^4.4.0" + "@types/json-schema" "^7.0.12" + "@types/semver" "^7.5.0" + "@typescript-eslint/scope-manager" "6.7.0" + "@typescript-eslint/types" "6.7.0" + "@typescript-eslint/typescript-estree" "6.7.0" + semver "^7.5.4" + +"@typescript-eslint/visitor-keys@6.7.0": + version "6.7.0" + resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.7.0.tgz" + integrity sha512-/C1RVgKFDmGMcVGeD8HjKv2bd72oI1KxQDeY8uc66gw9R0OK0eMq48cA+jv9/2Ag6cdrsUGySm1yzYmfz0hxwQ== + dependencies: + "@typescript-eslint/types" "6.7.0" + eslint-visitor-keys "^3.4.1" + +JSONStream@^1.3.5: + version "1.3.5" + resolved "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz" + integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== + dependencies: + jsonparse "^1.2.0" + through ">=2.2.7 <3" + +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8.4.1, acorn@^8.9.0: + version "8.10.0" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz" + integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn-walk@^8.1.1: + version "8.2.0" + resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz" + integrity sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ajv@^8.11.0: + version "8.12.0" + resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" + integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== + dependencies: + fast-deep-equal "^3.1.1" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.2.2" + +ansi-escapes@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz" + integrity sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA== + dependencies: + type-fest "^1.0.2" + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== + +ansi-styles@^3.2.1: + version "3.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz" + integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== + dependencies: + color-convert "^1.9.0" + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + +ansi-styles@^6.0.0, ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== + +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +array-buffer-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz" + integrity sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A== + dependencies: + call-bind "^1.0.2" + is-array-buffer "^3.0.1" + +array-ify@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/array-ify/-/array-ify-1.0.0.tgz" + integrity sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng== + +array-includes@^3.1.6: + version "3.1.7" + resolved "https://registry.npmjs.org/array-includes/-/array-includes-3.1.7.tgz" + integrity sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-string "^1.0.7" + +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + +array.prototype.findlastindex@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.3.tgz" + integrity sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + get-intrinsic "^1.2.1" + +array.prototype.flat@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.2.tgz" + integrity sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +array.prototype.flatmap@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.2.tgz" + integrity sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + es-shim-unscopables "^1.0.0" + +arraybuffer.prototype.slice@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz" + integrity sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw== + dependencies: + array-buffer-byte-length "^1.0.0" + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + is-array-buffer "^3.0.2" + is-shared-array-buffer "^1.0.2" + +arrify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz" + integrity sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA== + +available-typed-arrays@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz" + integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +big-integer@^1.6.44: + version "1.6.51" + resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz" + integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg== + +bplist-parser@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.2.0.tgz" + integrity sha512-z0M+byMThzQmD9NILRniCUXYsYpjwnlO8N5uCFaCqIOpqRsJCrQL9NK3JsD67CN5a08nF5oIL2bD6loTdHOuKw== + dependencies: + big-integer "^1.6.44" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +bundle-name@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/bundle-name/-/bundle-name-3.0.0.tgz" + integrity sha512-PKA4BeSvBpQKQ8iPOGCSiell+N8P+Tf1DlwqmYhpe2gAhKPHn8EYOxVT+ShuGmhg8lN8XiSlS80yiExKXrURlw== + dependencies: + run-applescript "^5.0.0" + +call-bind@^1.0.0, call-bind@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz" + integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== + dependencies: + function-bind "^1.1.1" + get-intrinsic "^1.0.2" + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camelcase@^5.3.1: + version "5.3.1" + resolved "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz" + integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== + +camelcase-keys@^6.2.2: + version "6.2.2" + resolved "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz" + integrity sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg== + dependencies: + camelcase "^5.3.1" + map-obj "^4.0.0" + quick-lru "^4.0.1" + +chalk@^2.4.2: + version "2.4.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chalk@^4.0.0, chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chalk@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + +ci-info@^3.2.0: + version "3.8.0" + resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz" + integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw== + +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-truncate@^3.1.0: + version "3.1.0" + resolved "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz" + integrity sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA== + dependencies: + slice-ansi "^5.0.0" + string-width "^5.0.0" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^1.9.0: + version "1.9.3" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz" + integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== + dependencies: + color-name "1.1.3" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@1.1.3: + version "1.1.3" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz" + integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.20: + version "2.0.20" + resolved "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@11.0.0: + version "11.0.0" + resolved "https://registry.npmjs.org/commander/-/commander-11.0.0.tgz" + integrity sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ== + +compare-func@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/compare-func/-/compare-func-2.0.0.tgz" + integrity sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA== + dependencies: + array-ify "^1.0.0" + dot-prop "^5.1.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +confusing-browser-globals@^1.0.10: + version "1.0.11" + resolved "https://registry.npmjs.org/confusing-browser-globals/-/confusing-browser-globals-1.0.11.tgz" + integrity sha512-JsPKdmh8ZkmnHxDk55FZ1TqVLvEQTvoByJZRN9jzI0UjxK/QgAmsphz7PGtqgPieQZ/CQcHWXCR7ATDNhGe+YA== + +conventional-changelog-angular@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-6.0.0.tgz" + integrity sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg== + dependencies: + compare-func "^2.0.0" + +conventional-changelog-conventionalcommits@^6.1.0: + version "6.1.0" + resolved "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-6.1.0.tgz" + integrity sha512-3cS3GEtR78zTfMzk0AizXKKIdN4OvSh7ibNz6/DPbhWWQu7LqE/8+/GqSodV+sywUR2gpJAdP/1JFf4XtN7Zpw== + dependencies: + compare-func "^2.0.0" + +conventional-commits-parser@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-4.0.0.tgz" + integrity sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg== + dependencies: + JSONStream "^1.3.5" + is-text-path "^1.0.1" + meow "^8.1.2" + split2 "^3.2.2" + +cosmiconfig@>=7, cosmiconfig@^8.0.0: + version "8.3.6" + resolved "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-8.3.6.tgz" + integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== + dependencies: + import-fresh "^3.3.0" + js-yaml "^4.1.0" + parse-json "^5.2.0" + path-type "^4.0.0" + +cosmiconfig-typescript-loader@^4.0.0: + version "4.4.0" + resolved "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-4.4.0.tgz" + integrity sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw== + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== + +cross-spawn@^7.0.2, cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +dargs@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/dargs/-/dargs-7.0.0.tgz" + integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== + +debug@^3.2.7: + version "3.2.7" + resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + +debug@4.3.4, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^1.1.0: + version "1.2.0" + resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + +decamelize-keys@^1.1.0: + version "1.1.1" + resolved "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz" + integrity sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg== + dependencies: + decamelize "^1.1.0" + map-obj "^1.0.0" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +default-browser@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/default-browser/-/default-browser-4.0.0.tgz" + integrity sha512-wX5pXO1+BrhMkSbROFsyxUm0i/cJEScyNhA4PPxc41ICuv05ZZB/MX28s8aZx6xjmatvebIapF6hLEKEcpneUA== + dependencies: + bundle-name "^3.0.0" + default-browser-id "^3.0.0" + execa "^7.1.1" + titleize "^3.0.0" + +default-browser-id@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/default-browser-id/-/default-browser-id-3.0.0.tgz" + integrity sha512-OZ1y3y0SqSICtE8DE4S8YOE9UZOJ8wO16fKWVP5J1Qz42kV9jcnMVFrEE/noXb/ss3Q4pZIH79kxofzyNNtUNA== + dependencies: + bplist-parser "^0.2.0" + untildify "^4.0.0" + +define-data-property@^1.0.1: + version "1.1.0" + resolved "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.0.tgz" + integrity sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +define-lazy-prop@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz" + integrity sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg== + +define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: + version "1.2.1" + resolved "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + +diff-sequences@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz" + integrity sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q== + +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + +doctrine@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz" + integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== + dependencies: + esutils "^2.0.2" + +doctrine@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz" + integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== + dependencies: + esutils "^2.0.2" + +dot-prop@^5.1.0: + version "5.3.0" + resolved "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz" + integrity sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q== + dependencies: + is-obj "^2.0.0" + +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + +error-ex@^1.3.1: + version "1.3.2" + resolved "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz" + integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== + dependencies: + is-arrayish "^0.2.1" + +es-abstract@^1.22.1: + version "1.22.2" + resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.2.tgz" + integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA== + dependencies: + array-buffer-byte-length "^1.0.0" + arraybuffer.prototype.slice "^1.0.2" + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + es-set-tostringtag "^2.0.1" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.1" + get-symbol-description "^1.0.0" + globalthis "^1.0.3" + gopd "^1.0.1" + has "^1.0.3" + has-property-descriptors "^1.0.0" + has-proto "^1.0.1" + has-symbols "^1.0.3" + internal-slot "^1.0.5" + is-array-buffer "^3.0.2" + is-callable "^1.2.7" + is-negative-zero "^2.0.2" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.2" + is-string "^1.0.7" + is-typed-array "^1.1.12" + is-weakref "^1.0.2" + object-inspect "^1.12.3" + object-keys "^1.1.1" + object.assign "^4.1.4" + regexp.prototype.flags "^1.5.1" + safe-array-concat "^1.0.1" + safe-regex-test "^1.0.0" + string.prototype.trim "^1.2.8" + string.prototype.trimend "^1.0.7" + string.prototype.trimstart "^1.0.7" + typed-array-buffer "^1.0.0" + typed-array-byte-length "^1.0.0" + typed-array-byte-offset "^1.0.0" + typed-array-length "^1.0.4" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.11" + +es-set-tostringtag@^2.0.1: + version "2.0.1" + resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz" + integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg== + dependencies: + get-intrinsic "^1.1.3" + has "^1.0.3" + has-tostringtag "^1.0.0" + +es-shim-unscopables@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz" + integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== + dependencies: + has "^1.0.3" + +es-to-primitive@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" + integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== + dependencies: + is-callable "^1.1.4" + is-date-object "^1.0.1" + is-symbol "^1.0.2" + +escalade@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz" + integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== + +escape-string-regexp@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz" + integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== + +escape-string-regexp@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz" + integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint@8.49.0, eslint@>=7.0.0, eslint@>=8.0.0, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.32.0 || ^8.2.0": + version "8.49.0" + resolved "https://registry.npmjs.org/eslint/-/eslint-8.49.0.tgz" + integrity sha512-jw03ENfm6VJI0jA9U+8H5zfl5b+FvuU3YYvZRdZHOlU2ggJkxrlkJH4HcDrZpj6YwD8kuYqvQM8LyesoazrSOQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.2" + "@eslint/js" "8.49.0" + "@humanwhocodes/config-array" "^0.11.11" + "@humanwhocodes/module-importer" "^1.0.1" + "@nodelib/fs.walk" "^1.2.8" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.2" + debug "^4.3.2" + doctrine "^3.0.0" + escape-string-regexp "^4.0.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.3" + espree "^9.6.1" + esquery "^1.4.2" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^6.0.1" + find-up "^5.0.0" + glob-parent "^6.0.2" + globals "^13.19.0" + graphemer "^1.4.0" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + is-path-inside "^3.0.3" + js-yaml "^4.1.0" + json-stable-stringify-without-jsonify "^1.0.1" + levn "^0.4.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + strip-ansi "^6.0.1" + text-table "^0.2.0" + +eslint-config-airbnb-base@15.0.0, eslint-config-airbnb-base@^15.0.0: + version "15.0.0" + resolved "https://registry.npmjs.org/eslint-config-airbnb-base/-/eslint-config-airbnb-base-15.0.0.tgz" + integrity sha512-xaX3z4ZZIcFLvh2oUNvcX5oEofXda7giYmuplVxoOg5A7EXJMrUyqRgR+mhDhPK8LZ4PttFOBvCYDbX3sUoUig== + dependencies: + confusing-browser-globals "^1.0.10" + object.assign "^4.1.2" + object.entries "^1.1.5" + semver "^6.3.0" + +eslint-config-airbnb-typescript@17.1.0: + version "17.1.0" + resolved "https://registry.npmjs.org/eslint-config-airbnb-typescript/-/eslint-config-airbnb-typescript-17.1.0.tgz" + integrity sha512-GPxI5URre6dDpJ0CtcthSZVBAfI+Uw7un5OYNVxP2EYi3H81Jw701yFP7AU+/vCE7xBtFmjge7kfhhk4+RAiig== + dependencies: + eslint-config-airbnb-base "^15.0.0" + +eslint-config-prettier@9.0.0: + version "9.0.0" + resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== + +eslint-import-resolver-node@^0.3.7: + version "0.3.9" + resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== + dependencies: + debug "^3.2.7" + is-core-module "^2.13.0" + resolve "^1.22.4" + +eslint-module-utils@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.8.0.tgz" + integrity sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw== + dependencies: + debug "^3.2.7" + +eslint-plugin-import@2.28.1, eslint-plugin-import@^2.25.2, eslint-plugin-import@^2.25.3: + version "2.28.1" + resolved "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.28.1.tgz" + integrity sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A== + dependencies: + array-includes "^3.1.6" + array.prototype.findlastindex "^1.2.2" + array.prototype.flat "^1.3.1" + array.prototype.flatmap "^1.3.1" + debug "^3.2.7" + doctrine "^2.1.0" + eslint-import-resolver-node "^0.3.7" + eslint-module-utils "^2.8.0" + has "^1.0.3" + is-core-module "^2.13.0" + is-glob "^4.0.3" + minimatch "^3.1.2" + object.fromentries "^2.0.6" + object.groupby "^1.0.0" + object.values "^1.1.6" + semver "^6.3.1" + tsconfig-paths "^3.14.2" + +eslint-plugin-prettier@5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-5.0.0.tgz" + integrity sha512-AgaZCVuYDXHUGxj/ZGu1u8H8CYgDY3iG6w5kUFw4AzMVXzB7VvbKgYR4nATIN+OvUrghMbiDLeimVjVY5ilq3w== + dependencies: + prettier-linter-helpers "^1.0.0" + synckit "^0.8.5" + +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +espree@^9.6.0, espree@^9.6.1: + version "9.6.1" + resolved "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz" + integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== + dependencies: + acorn "^8.9.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^3.4.1" + +esquery@^1.4.2: + version "1.5.0" + resolved "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz" + integrity sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +eventemitter3@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz" + integrity sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA== + +execa@^5.0.0: + version "5.1.1" + resolved "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz" + integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.0" + human-signals "^2.1.0" + is-stream "^2.0.0" + merge-stream "^2.0.0" + npm-run-path "^4.0.1" + onetime "^5.1.2" + signal-exit "^3.0.3" + strip-final-newline "^2.0.0" + +execa@7.2.0, execa@^7.1.1: + version "7.2.0" + resolved "https://registry.npmjs.org/execa/-/execa-7.2.0.tgz" + integrity sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA== + dependencies: + cross-spawn "^7.0.3" + get-stream "^6.0.1" + human-signals "^4.3.0" + is-stream "^3.0.0" + merge-stream "^2.0.0" + npm-run-path "^5.1.0" + onetime "^6.0.0" + signal-exit "^3.0.7" + strip-final-newline "^3.0.0" + +expect@^29.0.0: + version "29.7.0" + resolved "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz" + integrity sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw== + dependencies: + "@jest/expect-utils" "^29.7.0" + jest-get-type "^29.6.3" + jest-matcher-utils "^29.7.0" + jest-message-util "^29.7.0" + jest-util "^29.7.0" + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-diff@^1.1.2: + version "1.3.0" + resolved "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz" + integrity sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw== + +fast-glob@^3.2.9, fast-glob@^3.3.0: + version "3.3.1" + resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz" + integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +fastq@^1.6.0: + version "1.15.0" + resolved "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz" + integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw== + dependencies: + reusify "^1.0.4" + +file-entry-cache@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz" + integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== + dependencies: + flat-cache "^3.0.4" + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +find-up@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^3.0.4: + version "3.1.0" + resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.0.tgz" + integrity sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew== + dependencies: + flatted "^3.2.7" + keyv "^4.5.3" + rimraf "^3.0.2" + +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +for-each@^0.3.3: + version "0.3.3" + resolved "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz" + integrity sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw== + dependencies: + is-callable "^1.1.3" + +fs-extra@^11.0.0: + version "11.1.1" + resolved "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz" + integrity sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +function-bind@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz" + integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== + +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + +functions-have-names@^1.2.3: + version "1.2.3" + resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" + integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz" + integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-proto "^1.0.1" + has-symbols "^1.0.3" + +get-stream@^6.0.0, get-stream@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz" + integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== + +get-symbol-description@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz" + integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.1" + +git-raw-commits@^2.0.11: + version "2.0.11" + resolved "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-2.0.11.tgz" + integrity sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A== + dependencies: + dargs "^7.0.0" + lodash "^4.17.15" + meow "^8.0.0" + split2 "^3.0.0" + through2 "^4.0.0" + +glob@^7.1.3: + version "7.2.3" + resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob-parent@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +global-dirs@^0.1.1: + version "0.1.1" + resolved "https://registry.npmjs.org/global-dirs/-/global-dirs-0.1.1.tgz" + integrity sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg== + dependencies: + ini "^1.3.4" + +globals@^13.19.0: + version "13.21.0" + resolved "https://registry.npmjs.org/globals/-/globals-13.21.0.tgz" + integrity sha512-ybyme3s4yy/t/3s35bewwXKOf7cvzfreG2lH0lZl0JB7I4GxRP2ghxOK/Nb9EkRXdbBXZLfq/p/0W2JUONB/Gg== + dependencies: + type-fest "^0.20.2" + +globalthis@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz" + integrity sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA== + dependencies: + define-properties "^1.1.3" + +globby@^11.1.0: + version "11.1.0" + resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +graphemer@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz" + integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag== + +hard-rejection@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz" + integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== + +has@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has/-/has-1.0.3.tgz" + integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== + dependencies: + function-bind "^1.1.1" + +has-bigints@^1.0.1, has-bigints@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" + integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== + +has-flag@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz" + integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz" + integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== + dependencies: + get-intrinsic "^1.1.1" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.2, has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +has-tostringtag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz" + integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== + dependencies: + has-symbols "^1.0.2" + +hosted-git-info@^2.1.4: + version "2.8.9" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" + integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== + +hosted-git-info@^4.0.1: + version "4.1.0" + resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz" + integrity sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA== + dependencies: + lru-cache "^6.0.0" + +human-signals@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz" + integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== + +human-signals@^4.3.0: + version "4.3.1" + resolved "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz" + integrity sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ== + +husky@8.0.3: + version "8.0.3" + resolved "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz" + integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg== + +ignore@^5.2.0, ignore@^5.2.4: + version "5.2.4" + resolved "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz" + integrity sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ== + +import-fresh@^3.0.0, import-fresh@^3.2.1, import-fresh@^3.3.0: + version "3.3.0" + resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +indent-string@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz" + integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.3: + version "2.0.4" + resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +ini@^1.3.4: + version "1.3.8" + resolved "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + +internal-slot@^1.0.5: + version "1.0.5" + resolved "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.5.tgz" + integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ== + dependencies: + get-intrinsic "^1.2.0" + has "^1.0.3" + side-channel "^1.0.4" + +is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz" + integrity sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.0" + is-typed-array "^1.1.10" + +is-arrayish@^0.2.1: + version "0.2.1" + resolved "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz" + integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== + +is-bigint@^1.0.1: + version "1.0.4" + resolved "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz" + integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== + dependencies: + has-bigints "^1.0.1" + +is-boolean-object@^1.1.0: + version "1.1.2" + resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz" + integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: + version "1.2.7" + resolved "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz" + integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== + +is-core-module@^2.13.0, is-core-module@^2.5.0: + version "2.13.0" + resolved "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== + dependencies: + has "^1.0.3" + +is-date-object@^1.0.1: + version "1.0.5" + resolved "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz" + integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== + dependencies: + has-tostringtag "^1.0.0" + +is-docker@^2.0.0: + version "2.2.1" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + +is-docker@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz" + integrity sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-fullwidth-code-point@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz" + integrity sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ== + +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-inside-container@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz" + integrity sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA== + dependencies: + is-docker "^3.0.0" + +is-negative-zero@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz" + integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-number-object@^1.0.4: + version "1.0.7" + resolved "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz" + integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== + dependencies: + has-tostringtag "^1.0.0" + +is-obj@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz" + integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w== + +is-path-inside@^3.0.3: + version "3.0.3" + resolved "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz" + integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== + +is-plain-obj@^1.1.0: + version "1.1.0" + resolved "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz" + integrity sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg== + +is-regex@^1.1.4: + version "1.1.4" + resolved "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz" + integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== + dependencies: + call-bind "^1.0.2" + has-tostringtag "^1.0.0" + +is-shared-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz" + integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== + dependencies: + call-bind "^1.0.2" + +is-stream@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" + integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== + +is-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz" + integrity sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA== + +is-string@^1.0.5, is-string@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz" + integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== + dependencies: + has-tostringtag "^1.0.0" + +is-symbol@^1.0.2, is-symbol@^1.0.3: + version "1.0.4" + resolved "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz" + integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== + dependencies: + has-symbols "^1.0.2" + +is-text-path@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/is-text-path/-/is-text-path-1.0.1.tgz" + integrity sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w== + dependencies: + text-extensions "^1.0.0" + +is-typed-array@^1.1.10, is-typed-array@^1.1.12, is-typed-array@^1.1.9: + version "1.1.12" + resolved "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz" + integrity sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg== + dependencies: + which-typed-array "^1.1.11" + +is-weakref@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz" + integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== + dependencies: + call-bind "^1.0.2" + +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + +isarray@^2.0.5: + version "2.0.5" + resolved "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz" + integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +jest-diff@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz" + integrity sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw== + dependencies: + chalk "^4.0.0" + diff-sequences "^29.6.3" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-get-type@^29.6.3: + version "29.6.3" + resolved "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz" + integrity sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw== + +jest-matcher-utils@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz" + integrity sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g== + dependencies: + chalk "^4.0.0" + jest-diff "^29.7.0" + jest-get-type "^29.6.3" + pretty-format "^29.7.0" + +jest-message-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz" + integrity sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w== + dependencies: + "@babel/code-frame" "^7.12.13" + "@jest/types" "^29.6.3" + "@types/stack-utils" "^2.0.0" + chalk "^4.0.0" + graceful-fs "^4.2.9" + micromatch "^4.0.4" + pretty-format "^29.7.0" + slash "^3.0.0" + stack-utils "^2.0.3" + +jest-util@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz" + integrity sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA== + dependencies: + "@jest/types" "^29.6.3" + "@types/node" "*" + chalk "^4.0.0" + ci-info "^3.2.0" + graceful-fs "^4.2.9" + picomatch "^2.2.3" + +js-tokens@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-parse-even-better-errors@^2.3.0: + version "2.3.1" + resolved "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-schema-traverse@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz" + integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +json5@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz" + integrity sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA== + dependencies: + minimist "^1.2.0" + +jsonfile@^6.0.1: + version "6.1.0" + resolved "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz" + integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ== + dependencies: + universalify "^2.0.0" + optionalDependencies: + graceful-fs "^4.1.6" + +jsonparse@^1.2.0: + version "1.3.1" + resolved "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz" + integrity sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg== + +keyv@^4.5.3: + version "4.5.3" + resolved "https://registry.npmjs.org/keyv/-/keyv-4.5.3.tgz" + integrity sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug== + dependencies: + json-buffer "3.0.1" + +kind-of@^6.0.3: + version "6.0.3" + resolved "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +lilconfig@2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz" + integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== + +lines-and-columns@^1.1.6: + version "1.2.4" + resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz" + integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== + +lint-staged@14.0.1: + version "14.0.1" + resolved "https://registry.npmjs.org/lint-staged/-/lint-staged-14.0.1.tgz" + integrity sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw== + dependencies: + chalk "5.3.0" + commander "11.0.0" + debug "4.3.4" + execa "7.2.0" + lilconfig "2.1.0" + listr2 "6.6.1" + micromatch "4.0.5" + pidtree "0.6.0" + string-argv "0.3.2" + yaml "2.3.1" + +listr2@6.6.1: + version "6.6.1" + resolved "https://registry.npmjs.org/listr2/-/listr2-6.6.1.tgz" + integrity sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg== + dependencies: + cli-truncate "^3.1.0" + colorette "^2.0.20" + eventemitter3 "^5.0.1" + log-update "^5.0.1" + rfdc "^1.3.0" + wrap-ansi "^8.1.0" + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@4.17.21, lodash@^4.17.15: + version "4.17.21" + resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +lodash.camelcase@^4.3.0: + version "4.3.0" + resolved "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz" + integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== + +lodash.isfunction@^3.0.9: + version "3.0.9" + resolved "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz" + integrity sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.kebabcase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz" + integrity sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g== + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lodash.mergewith@^4.6.2: + version "4.6.2" + resolved "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz" + integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ== + +lodash.snakecase@^4.1.1: + version "4.1.1" + resolved "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz" + integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw== + +lodash.startcase@^4.4.0: + version "4.4.0" + resolved "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz" + integrity sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg== + +lodash.uniq@^4.5.0: + version "4.5.0" + resolved "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz" + integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ== + +lodash.upperfirst@^4.3.1: + version "4.3.1" + resolved "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz" + integrity sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg== + +log-update@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/log-update/-/log-update-5.0.1.tgz" + integrity sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw== + dependencies: + ansi-escapes "^5.0.0" + cli-cursor "^4.0.0" + slice-ansi "^5.0.0" + strip-ansi "^7.0.1" + wrap-ansi "^8.0.1" + +lru-cache@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz" + integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== + dependencies: + yallist "^4.0.0" + +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + +map-obj@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz" + integrity sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg== + +map-obj@^4.0.0: + version "4.3.0" + resolved "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz" + integrity sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ== + +meow@^8.0.0, meow@^8.1.2: + version "8.1.2" + resolved "https://registry.npmjs.org/meow/-/meow-8.1.2.tgz" + integrity sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q== + dependencies: + "@types/minimist" "^1.2.0" + camelcase-keys "^6.2.2" + decamelize-keys "^1.1.0" + hard-rejection "^2.1.0" + minimist-options "4.1.0" + normalize-package-data "^3.0.0" + read-pkg-up "^7.0.1" + redent "^3.0.0" + trim-newlines "^3.0.0" + type-fest "^0.18.0" + yargs-parser "^20.2.3" + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@4.0.5, micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + +mimic-fn@^2.1.0: + version "2.1.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz" + integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== + +mimic-fn@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz" + integrity sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw== + +min-indent@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz" + integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== + +minimatch@^3.0.5: + version "3.0.8" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.0.8.tgz" + integrity sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.1.1, minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.0, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +minimist-options@4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz" + integrity sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A== + dependencies: + arrify "^1.0.1" + is-plain-obj "^1.1.0" + kind-of "^6.0.3" + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +normalize-package-data@^2.5.0: + version "2.5.0" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz" + integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== + dependencies: + hosted-git-info "^2.1.4" + resolve "^1.10.0" + semver "2 || 3 || 4 || 5" + validate-npm-package-license "^3.0.1" + +normalize-package-data@^3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz" + integrity sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA== + dependencies: + hosted-git-info "^4.0.1" + is-core-module "^2.5.0" + semver "^7.3.4" + validate-npm-package-license "^3.0.1" + +npm-run-path@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz" + integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== + dependencies: + path-key "^3.0.0" + +npm-run-path@^5.1.0: + version "5.1.0" + resolved "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz" + integrity sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q== + dependencies: + path-key "^4.0.0" + +object-inspect@^1.12.3, object-inspect@^1.9.0: + version "1.12.3" + resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz" + integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== + +object-keys@^1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz" + integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== + +object.assign@^4.1.2, object.assign@^4.1.4: + version "4.1.4" + resolved "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz" + integrity sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.4" + has-symbols "^1.0.3" + object-keys "^1.1.1" + +object.entries@^1.1.5: + version "1.1.7" + resolved "https://registry.npmjs.org/object.entries/-/object.entries-1.1.7.tgz" + integrity sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.fromentries@^2.0.6: + version "2.0.7" + resolved "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.7.tgz" + integrity sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +object.groupby@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.1.tgz" + integrity sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + get-intrinsic "^1.2.1" + +object.values@^1.1.6: + version "1.1.7" + resolved "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz" + integrity sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +onetime@^5.1.0, onetime@^5.1.2: + version "5.1.2" + resolved "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + +onetime@^6.0.0: + version "6.0.0" + resolved "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz" + integrity sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ== + dependencies: + mimic-fn "^4.0.0" + +open@^9.1.0: + version "9.1.0" + resolved "https://registry.npmjs.org/open/-/open-9.1.0.tgz" + integrity sha512-OS+QTnw1/4vrf+9hh1jc1jnYjzSG4ttTBB8UxOwAnInG3Uo4ssetzC1ihqaIHjLJnA5GGlRl6QlZXOTQhRBUvg== + dependencies: + default-browser "^4.0.0" + define-lazy-prop "^3.0.0" + is-inside-container "^1.0.0" + is-wsl "^2.2.0" + +optionator@^0.9.3: + version "0.9.3" + resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" + integrity sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg== + dependencies: + prelude-ls "^1.2.1" + deep-is "^0.1.3" + "@aashutoshrathi/word-wrap" "^1.2.3" + type-check "^0.4.0" + levn "^0.4.1" + fast-levenshtein "^2.0.6" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +parse-json@^5.0.0, parse-json@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz" + integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== + dependencies: + "@babel/code-frame" "^7.0.0" + error-ex "^1.3.1" + json-parse-even-better-errors "^2.3.0" + lines-and-columns "^1.1.6" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.0.0, path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-key@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz" + integrity sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.2.3, picomatch@^2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pidtree@0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz" + integrity sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +prettier@3.0.3, prettier@>=3.0.0: + version "3.0.3" + resolved "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz" + integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg== + +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + +pretty-format@^29.0.0, pretty-format@^29.7.0: + version "29.7.0" + resolved "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz" + integrity sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ== + dependencies: + "@jest/schemas" "^29.6.3" + ansi-styles "^5.0.0" + react-is "^18.0.0" + +punycode@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz" + integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA== + +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + +quick-lru@^4.0.1: + version "4.0.1" + resolved "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz" + integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== + +react-is@^18.0.0: + version "18.2.0" + resolved "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz" + integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== + +read-pkg@^5.2.0: + version "5.2.0" + resolved "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz" + integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== + dependencies: + "@types/normalize-package-data" "^2.4.0" + normalize-package-data "^2.5.0" + parse-json "^5.0.0" + type-fest "^0.6.0" + +read-pkg-up@^7.0.1: + version "7.0.1" + resolved "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz" + integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== + dependencies: + find-up "^4.1.0" + read-pkg "^5.2.0" + type-fest "^0.8.1" + +readable-stream@3, readable-stream@^3.0.0: + version "3.6.2" + resolved "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + +redent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz" + integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== + dependencies: + indent-string "^4.0.0" + strip-indent "^3.0.0" + +regexp.prototype.flags@^1.5.1: + version "1.5.1" + resolved "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz" + integrity sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + set-function-name "^2.0.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +require-from-string@^2.0.2: + version "2.0.2" + resolved "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz" + integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== + +resolve@^1.10.0, resolve@^1.22.4: + version "1.22.6" + resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.6.tgz" + integrity sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +resolve-from@5.0.0, resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve-global@1.0.0, resolve-global@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/resolve-global/-/resolve-global-1.0.0.tgz" + integrity sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw== + dependencies: + global-dirs "^0.1.1" + +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + +rfdc@^1.3.0: + version "1.3.0" + resolved "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz" + integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA== + +rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +run-applescript@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/run-applescript/-/run-applescript-5.0.0.tgz" + integrity sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg== + dependencies: + execa "^5.0.0" + +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + +safe-array-concat@^1.0.1: + version "1.0.1" + resolved "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz" + integrity sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + has-symbols "^1.0.3" + isarray "^2.0.5" + +safe-buffer@~5.2.0: + version "5.2.1" + resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +safe-regex-test@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz" + integrity sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.1.3" + is-regex "^1.1.4" + +"semver@2 || 3 || 4 || 5": + version "5.7.2" + resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" + integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== + +semver@^6.3.0, semver@^6.3.1: + version "6.3.1" + resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz" + integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== + +semver@7.5.4, semver@^7.3.4, semver@^7.5.4: + version "7.5.4" + resolved "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz" + integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== + dependencies: + lru-cache "^6.0.0" + +set-function-name@^2.0.0: + version "2.0.1" + resolved "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz" + integrity sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA== + dependencies: + define-data-property "^1.0.1" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.0" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7: + version "3.0.7" + resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz" + integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== + +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + +slice-ansi@^5.0.0: + version "5.0.0" + resolved "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz" + integrity sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ== + dependencies: + ansi-styles "^6.0.0" + is-fullwidth-code-point "^4.0.0" + +spdx-correct@^3.0.0: + version "3.2.0" + resolved "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz" + integrity sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA== + dependencies: + spdx-expression-parse "^3.0.0" + spdx-license-ids "^3.0.0" + +spdx-exceptions@^2.1.0: + version "2.3.0" + resolved "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz" + integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== + +spdx-expression-parse@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz" + integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== + dependencies: + spdx-exceptions "^2.1.0" + spdx-license-ids "^3.0.0" + +spdx-license-ids@^3.0.0: + version "3.0.14" + resolved "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.14.tgz" + integrity sha512-U0eS5wcpu/O2/QZk6PcAMOA8H3ZuvRe4mFHA3Q+LNl1SRDmfQ+mD3RoD6tItqnvqubJ32m/zV2Z/ikSmxccD1Q== + +split2@^3.0.0, split2@^3.2.2: + version "3.2.2" + resolved "https://registry.npmjs.org/split2/-/split2-3.2.2.tgz" + integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg== + dependencies: + readable-stream "^3.0.0" + +stack-utils@^2.0.3: + version "2.0.6" + resolved "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz" + integrity sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ== + dependencies: + escape-string-regexp "^2.0.0" + +string-argv@0.3.2: + version "0.3.2" + resolved "https://registry.npmjs.org/string-argv/-/string-argv-0.3.2.tgz" + integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.0, string-width@^5.0.1: + version "5.1.2" + resolved "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + +string.prototype.trim@^1.2.8: + version "1.2.8" + resolved "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz" + integrity sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimend@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz" + integrity sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string.prototype.trimstart@^1.0.7: + version "1.0.7" + resolved "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz" + integrity sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + +strip-bom@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz" + integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== + +strip-final-newline@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz" + integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== + +strip-final-newline@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz" + integrity sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw== + +strip-indent@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz" + integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== + dependencies: + min-indent "^1.0.0" + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^5.3.0: + version "5.5.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz" + integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== + dependencies: + has-flag "^3.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +synckit@^0.8.5: + version "0.8.5" + resolved "https://registry.npmjs.org/synckit/-/synckit-0.8.5.tgz" + integrity sha512-L1dapNV6vu2s/4Sputv8xGsCdAVlb5nRDMFU/E27D44l5U6cw1g0dGd45uLc+OXjNMmF4ntiMdCimzcjFKQI8Q== + dependencies: + "@pkgr/utils" "^2.3.1" + tslib "^2.5.0" + +text-extensions@^1.0.0: + version "1.9.0" + resolved "https://registry.npmjs.org/text-extensions/-/text-extensions-1.9.0.tgz" + integrity sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ== + +text-table@^0.2.0: + version "0.2.0" + resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz" + integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== + +"through@>=2.2.7 <3": + version "2.3.8" + resolved "https://registry.npmjs.org/through/-/through-2.3.8.tgz" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + +through2@^4.0.0: + version "4.0.2" + resolved "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz" + integrity sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw== + dependencies: + readable-stream "3" + +titleize@^3.0.0: + version "3.0.0" + resolved "https://registry.npmjs.org/titleize/-/titleize-3.0.0.tgz" + integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +trim-newlines@^3.0.0: + version "3.0.1" + resolved "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== + +ts-api-utils@^1.0.1: + version "1.0.3" + resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz" + integrity sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg== + +ts-node@>=10, ts-node@^10.8.1: + version "10.9.1" + resolved "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz" + integrity sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + +tsconfig-paths@^3.14.2: + version "3.14.2" + resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.2.tgz" + integrity sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g== + dependencies: + "@types/json5" "^0.0.29" + json5 "^1.0.2" + minimist "^1.2.6" + strip-bom "^3.0.0" + +tslib@^2.5.0, tslib@^2.6.0: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + +type-check@^0.4.0: + version "0.4.0" + resolved "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +type-fest@^0.6.0: + version "0.6.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz" + integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== + +type-fest@^0.8.1: + version "0.8.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz" + integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== + +type-fest@^0.18.0: + version "0.18.1" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz" + integrity sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw== + +type-fest@^0.20.2: + version "0.20.2" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" + integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== + +type-fest@^1.0.2: + version "1.4.0" + resolved "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz" + integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA== + +typed-array-buffer@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz" + integrity sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-typed-array "^1.1.10" + +typed-array-byte-length@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz" + integrity sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz" + integrity sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + has-proto "^1.0.1" + is-typed-array "^1.1.10" + +typed-array-length@^1.0.4: + version "1.0.4" + resolved "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz" + integrity sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng== + dependencies: + call-bind "^1.0.2" + for-each "^0.3.3" + is-typed-array "^1.1.9" + +typescript@>=2.7, typescript@>=4, typescript@>=4.2.0, typescript@>=4.9.5, "typescript@^4.6.4 || ^5.0.0": + version "5.2.2" + resolved "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz" + integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== + +unbox-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" + integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== + dependencies: + call-bind "^1.0.2" + has-bigints "^1.0.2" + has-symbols "^1.0.3" + which-boxed-primitive "^1.0.2" + +universalify@^2.0.0: + version "2.0.0" + resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz" + integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ== + +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz" + integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== + +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + +validate-npm-package-license@^3.0.1: + version "3.0.4" + resolved "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz" + integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== + dependencies: + spdx-correct "^3.0.0" + spdx-expression-parse "^3.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.npmjs.org/which/-/which-2.0.2.tgz" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +which-boxed-primitive@^1.0.2: + version "1.0.2" + resolved "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz" + integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== + dependencies: + is-bigint "^1.0.1" + is-boolean-object "^1.1.0" + is-number-object "^1.0.4" + is-string "^1.0.5" + is-symbol "^1.0.3" + +which-typed-array@^1.1.11: + version "1.1.11" + resolved "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.11.tgz" + integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew== + dependencies: + available-typed-arrays "^1.0.5" + call-bind "^1.0.2" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.0" + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + +wrappy@1: + version "1.0.2" + resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yallist@^4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz" + integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== + +yaml@2.3.1: + version "2.3.1" + resolved "https://registry.npmjs.org/yaml/-/yaml-2.3.1.tgz" + integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== + +yargs@^17.0.0: + version "17.7.2" + resolved "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" + +yargs-parser@^20.2.3: + version "20.2.9" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yn@3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/zip.js b/zip.js deleted file mode 100644 index 905c82cec8..0000000000 --- a/zip.js +++ /dev/null @@ -1,22 +0,0 @@ -import unzip from './unzip.js' - -/** - * Creates an array of grouped elements, the first of which contains the - * first elements of the given arrays, the second of which contains the - * second elements of the given arrays, and so on. - * - * @since 0.1.0 - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @returns {Array} Returns the new array of grouped elements. - * @see unzip, unzipWith, zipObject, zipObjectDeep, zipWith - * @example - * - * zip(['a', 'b'], [1, 2], [true, false]) - * // => [['a', 1, true], ['b', 2, false]] - */ -function zip(...arrays) { - return unzip(arrays) -} - -export default zip diff --git a/zipObject.js b/zipObject.js deleted file mode 100644 index 309da74f44..0000000000 --- a/zipObject.js +++ /dev/null @@ -1,23 +0,0 @@ -import assignValue from './.internal/assignValue.js' -import baseZipObject from './.internal/baseZipObject.js' - -/** - * This method is like `fromPairs` except that it accepts two arrays, - * one of property identifiers and one of corresponding values. - * - * @since 0.4.0 - * @category Array - * @param {Array} [props=[]] The property identifiers. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @see unzip, unzipWith, zip, zipObjectDeep, zipWith - * @example - * - * zipObject(['a', 'b'], [1, 2]) - * // => { 'a': 1, 'b': 2 } - */ -function zipObject(props, values) { - return baseZipObject(props || [], values || [], assignValue) -} - -export default zipObject diff --git a/zipObjectDeep.js b/zipObjectDeep.js deleted file mode 100644 index 07eec2cda8..0000000000 --- a/zipObjectDeep.js +++ /dev/null @@ -1,22 +0,0 @@ -import baseSet from './.internal/baseSet.js' -import baseZipObject from './.internal/baseZipObject.js' - -/** - * This method is like `zipObject` except that it supports property paths. - * - * @since 4.1.0 - * @category Array - * @param {Array} [props=[]] The property identifiers. - * @param {Array} [values=[]] The property values. - * @returns {Object} Returns the new object. - * @see unzip, unzipWith, zip, zipObject, zipWith - * @example - * - * zipObjectDeep(['a.b[0].c', 'a.b[1].d'], [1, 2]) - * // => { 'a': { 'b': [{ 'c': 1 }, { 'd': 2 }] } } - */ -function zipObjectDeep(props, values) { - return baseZipObject(props || [], values || [], baseSet) -} - -export default zipObjectDeep diff --git a/zipWith.js b/zipWith.js deleted file mode 100644 index 30989ac591..0000000000 --- a/zipWith.js +++ /dev/null @@ -1,27 +0,0 @@ -import unzipWith from './unzipWith.js' - -/** - * This method is like `zip` except that it accepts `iteratee` to specify - * how grouped values should be combined. The iteratee is invoked with the - * elements of each group: (...group). - * - * @since 3.8.0 - * @category Array - * @param {...Array} [arrays] The arrays to process. - * @param {Function} iteratee The function to combine - * grouped values. - * @returns {Array} Returns the new array of grouped elements. - * @see unzip, unzipWith, zip, zipObject, zipObjectDeep, zipWith - * @example - * - * zipWith([1, 2], [10, 20], [100, 200], (a, b, c) => a + b + c) - * // => [111, 222] - */ -function zipWith(...arrays) { - const length = arrays.length - let iteratee = length > 1 ? arrays[length - 1] : undefined - iteratee = typeof iteratee === 'function' ? (arrays.pop(), iteratee) : undefined - return unzipWith(arrays, iteratee) -} - -export default zipWith