mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-02-12 03:47:50 +00:00
Fix string methods to handle empty values (#4442)
* Enable strings category methods tests * Ensure escape, pad, padEnd, padStart, trim, trimEnd, trimStart, unescape return an empty string for falsey values * Coerce value to string using toString in truncate, capitalize and case methods * Ensure createCaseFirst returns an empty string for falsey values
This commit is contained in:
committed by
John-David Dalton
parent
abb54cc49a
commit
e51a424513
@@ -11,6 +11,10 @@ import stringToArray from './stringToArray.js'
|
|||||||
*/
|
*/
|
||||||
function createCaseFirst(methodName) {
|
function createCaseFirst(methodName) {
|
||||||
return (string) => {
|
return (string) => {
|
||||||
|
if (!string) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
const strSymbols = hasUnicode(string)
|
const strSymbols = hasUnicode(string)
|
||||||
? stringToArray(string)
|
? stringToArray(string)
|
||||||
: undefined
|
: undefined
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import upperFirst from './upperFirst.js'
|
import upperFirst from './upperFirst.js'
|
||||||
import words from './words.js'
|
import words from './words.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
|
* Converts `string` to [camel case](https://en.wikipedia.org/wiki/CamelCase).
|
||||||
@@ -21,7 +22,7 @@ import words from './words.js'
|
|||||||
* // => 'fooBar'
|
* // => 'fooBar'
|
||||||
*/
|
*/
|
||||||
const camelCase = (string) => (
|
const camelCase = (string) => (
|
||||||
words(`${string}`.replace(/['\u2019]/g, '')).reduce((result, word, index) => {
|
words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => {
|
||||||
word = word.toLowerCase()
|
word = word.toLowerCase()
|
||||||
return result + (index ? upperFirst(word) : word)
|
return result + (index ? upperFirst(word) : word)
|
||||||
}, '')
|
}, '')
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import upperFirst from './upperFirst.js'
|
import upperFirst from './upperFirst.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts the first character of `string` to upper case and the remaining
|
* Converts the first character of `string` to upper case and the remaining
|
||||||
@@ -13,7 +14,7 @@ import upperFirst from './upperFirst.js'
|
|||||||
* capitalize('FRED')
|
* capitalize('FRED')
|
||||||
* // => 'Fred'
|
* // => 'Fred'
|
||||||
*/
|
*/
|
||||||
const capitalize = (string) => upperFirst(string.toLowerCase())
|
const capitalize = (string) => upperFirst(toString(string).toLowerCase())
|
||||||
|
|
||||||
|
|
||||||
export default capitalize
|
export default capitalize
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ const reHasUnescapedHtml = RegExp(reUnescapedHtml.source)
|
|||||||
function escape(string) {
|
function escape(string) {
|
||||||
return (string && reHasUnescapedHtml.test(string))
|
return (string && reHasUnescapedHtml.test(string))
|
||||||
? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr])
|
? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr])
|
||||||
: string
|
: (string || '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default escape
|
export default escape
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import words from './words.js'
|
import words from './words.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts `string` to
|
* Converts `string` to
|
||||||
@@ -21,7 +22,7 @@ import words from './words.js'
|
|||||||
* // => 'foo-bar'
|
* // => 'foo-bar'
|
||||||
*/
|
*/
|
||||||
const kebabCase = (string) => (
|
const kebabCase = (string) => (
|
||||||
words(`${string}`.replace(/['\u2019]/g, '')).reduce((result, word, index) => (
|
words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => (
|
||||||
result + (index ? '-' : '') + word.toLowerCase()
|
result + (index ? '-' : '') + word.toLowerCase()
|
||||||
), '')
|
), '')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import words from './words.js'
|
import words from './words.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
const reQuotes = /['\u2019]/g
|
const reQuotes = /['\u2019]/g
|
||||||
|
|
||||||
@@ -22,7 +23,7 @@ const reQuotes = /['\u2019]/g
|
|||||||
* // => 'foo bar'
|
* // => 'foo bar'
|
||||||
*/
|
*/
|
||||||
const lowerCase = (string) => (
|
const lowerCase = (string) => (
|
||||||
words(`${string}`.replace(reQuotes, '')).reduce((result, word, index) => (
|
words(toString(string).replace(reQuotes, '')).reduce((result, word, index) => (
|
||||||
result + (index ? ' ' : '') + word.toLowerCase()
|
result + (index ? ' ' : '') + word.toLowerCase()
|
||||||
), '')
|
), '')
|
||||||
)
|
)
|
||||||
|
|||||||
2
pad.js
2
pad.js
@@ -25,7 +25,7 @@ import stringSize from './.internal/stringSize.js'
|
|||||||
function pad(string, length, chars) {
|
function pad(string, length, chars) {
|
||||||
const strLength = length ? stringSize(string) : 0
|
const strLength = length ? stringSize(string) : 0
|
||||||
if (!length || strLength >= length) {
|
if (!length || strLength >= length) {
|
||||||
return string
|
return (string || '')
|
||||||
}
|
}
|
||||||
const mid = (length - strLength) / 2
|
const mid = (length - strLength) / 2
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function padEnd(string, length, chars) {
|
|||||||
const strLength = length ? stringSize(string) : 0
|
const strLength = length ? stringSize(string) : 0
|
||||||
return (length && strLength < length)
|
return (length && strLength < length)
|
||||||
? (string + createPadding(length - strLength, chars))
|
? (string + createPadding(length - strLength, chars))
|
||||||
: string
|
: (string || '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default padEnd
|
export default padEnd
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function padStart(string, length, chars) {
|
|||||||
const strLength = length ? stringSize(string) : 0
|
const strLength = length ? stringSize(string) : 0
|
||||||
return (length && strLength < length)
|
return (length && strLength < length)
|
||||||
? (createPadding(length - strLength, chars) + string)
|
? (createPadding(length - strLength, chars) + string)
|
||||||
: string
|
: (string || '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default padStart
|
export default padStart
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import words from './words.js'
|
import words from './words.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts `string` to
|
* Converts `string` to
|
||||||
@@ -24,7 +25,7 @@ import words from './words.js'
|
|||||||
* // => 'foo_2_bar'
|
* // => 'foo_2_bar'
|
||||||
*/
|
*/
|
||||||
const snakeCase = (string) => (
|
const snakeCase = (string) => (
|
||||||
words(`${string}`.replace(/['\u2019]/g, '')).reduce((result, word, index) => (
|
words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => (
|
||||||
result + (index ? '_' : '') + word.toLowerCase()
|
result + (index ? '_' : '') + word.toLowerCase()
|
||||||
), '')
|
), '')
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
import assert from 'assert';
|
|
||||||
import lodashStable from 'lodash';
|
|
||||||
import { _, stubString } from './utils.js';
|
|
||||||
|
|
||||||
describe('"Strings" category methods', function() {
|
|
||||||
var stringMethods = [
|
|
||||||
'camelCase',
|
|
||||||
'capitalize',
|
|
||||||
'escape',
|
|
||||||
'kebabCase',
|
|
||||||
'lowerCase',
|
|
||||||
'lowerFirst',
|
|
||||||
'pad',
|
|
||||||
'padEnd',
|
|
||||||
'padStart',
|
|
||||||
'repeat',
|
|
||||||
'snakeCase',
|
|
||||||
'toLower',
|
|
||||||
'toUpper',
|
|
||||||
'trim',
|
|
||||||
'trimEnd',
|
|
||||||
'trimStart',
|
|
||||||
'truncate',
|
|
||||||
'unescape',
|
|
||||||
'upperCase',
|
|
||||||
'upperFirst'
|
|
||||||
];
|
|
||||||
|
|
||||||
lodashStable.each(stringMethods, function(methodName) {
|
|
||||||
var func = _[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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
83
test/Strings-category-methods.test.js
Normal file
83
test/Strings-category-methods.test.js
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
2
trim.js
2
trim.js
@@ -25,7 +25,7 @@ function trim(string, chars) {
|
|||||||
return string.trim()
|
return string.trim()
|
||||||
}
|
}
|
||||||
if (!string || !chars) {
|
if (!string || !chars) {
|
||||||
return string
|
return (string || '')
|
||||||
}
|
}
|
||||||
const strSymbols = stringToArray(string)
|
const strSymbols = stringToArray(string)
|
||||||
const chrSymbols = stringToArray(chars)
|
const chrSymbols = stringToArray(chars)
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function trimEnd(string, chars) {
|
|||||||
return string[methodName]()
|
return string[methodName]()
|
||||||
}
|
}
|
||||||
if (!string || !chars) {
|
if (!string || !chars) {
|
||||||
return string
|
return (string || '')
|
||||||
}
|
}
|
||||||
const strSymbols = stringToArray(string)
|
const strSymbols = stringToArray(string)
|
||||||
const end = charsEndIndex(strSymbols, stringToArray(chars)) + 1
|
const end = charsEndIndex(strSymbols, stringToArray(chars)) + 1
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ function trimStart(string, chars) {
|
|||||||
return string[methodName]()
|
return string[methodName]()
|
||||||
}
|
}
|
||||||
if (!string || !chars) {
|
if (!string || !chars) {
|
||||||
return string
|
return (string || '')
|
||||||
}
|
}
|
||||||
const strSymbols = stringToArray(string)
|
const strSymbols = stringToArray(string)
|
||||||
const start = charsStartIndex(strSymbols, stringToArray(chars))
|
const start = charsStartIndex(strSymbols, stringToArray(chars))
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import isObject from './isObject.js'
|
|||||||
import isRegExp from './isRegExp.js'
|
import isRegExp from './isRegExp.js'
|
||||||
import stringSize from './.internal/stringSize.js'
|
import stringSize from './.internal/stringSize.js'
|
||||||
import stringToArray from './.internal/stringToArray.js'
|
import stringToArray from './.internal/stringToArray.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
/** Used as default options for `truncate`. */
|
/** Used as default options for `truncate`. */
|
||||||
const DEFAULT_TRUNC_LENGTH = 30
|
const DEFAULT_TRUNC_LENGTH = 30
|
||||||
@@ -59,6 +60,9 @@ function truncate(string, options) {
|
|||||||
length = 'length' in options ? options.length : length
|
length = 'length' in options ? options.length : length
|
||||||
omission = 'omission' in options ? baseToString(options.omission) : omission
|
omission = 'omission' in options ? baseToString(options.omission) : omission
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string = toString(string)
|
||||||
|
|
||||||
let strSymbols
|
let strSymbols
|
||||||
let strLength = string.length
|
let strLength = string.length
|
||||||
if (hasUnicode(string)) {
|
if (hasUnicode(string)) {
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ const reHasEscapedHtml = RegExp(reEscapedHtml.source)
|
|||||||
function unescape(string) {
|
function unescape(string) {
|
||||||
return (string && reHasEscapedHtml.test(string))
|
return (string && reHasEscapedHtml.test(string))
|
||||||
? string.replace(reEscapedHtml, (entity) => htmlUnescapes[entity])
|
? string.replace(reEscapedHtml, (entity) => htmlUnescapes[entity])
|
||||||
: string
|
: (string || '')
|
||||||
}
|
}
|
||||||
|
|
||||||
export default unescape
|
export default unescape
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import words from './words.js'
|
import words from './words.js'
|
||||||
|
import toString from './toString.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts `string`, as space separated words, to upper case.
|
* Converts `string`, as space separated words, to upper case.
|
||||||
@@ -20,7 +21,7 @@ import words from './words.js'
|
|||||||
* // => 'FOO BAR'
|
* // => 'FOO BAR'
|
||||||
*/
|
*/
|
||||||
const upperCase = (string) => (
|
const upperCase = (string) => (
|
||||||
words(`${string}`.replace(/['\u2019]/g, '')).reduce((result, word, index) => (
|
words(toString(string).replace(/['\u2019]/g, '')).reduce((result, word, index) => (
|
||||||
result + (index ? ' ' : '') + word.toUpperCase()
|
result + (index ? ' ' : '') + word.toUpperCase()
|
||||||
), '')
|
), '')
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user