mirror of
https://github.com/whoisclebs/lodash.git
synced 2026-01-29 06:27:49 +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) {
|
||||
return (string) => {
|
||||
if (!string) {
|
||||
return ''
|
||||
}
|
||||
|
||||
const strSymbols = hasUnicode(string)
|
||||
? stringToArray(string)
|
||||
: undefined
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
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).
|
||||
@@ -21,7 +22,7 @@ import words from './words.js'
|
||||
* // => 'fooBar'
|
||||
*/
|
||||
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()
|
||||
return result + (index ? upperFirst(word) : word)
|
||||
}, '')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import upperFirst from './upperFirst.js'
|
||||
import toString from './toString.js'
|
||||
|
||||
/**
|
||||
* Converts the first character of `string` to upper case and the remaining
|
||||
@@ -13,7 +14,7 @@ import upperFirst from './upperFirst.js'
|
||||
* capitalize('FRED')
|
||||
* // => 'Fred'
|
||||
*/
|
||||
const capitalize = (string) => upperFirst(string.toLowerCase())
|
||||
const capitalize = (string) => upperFirst(toString(string).toLowerCase())
|
||||
|
||||
|
||||
export default capitalize
|
||||
|
||||
@@ -41,7 +41,7 @@ const reHasUnescapedHtml = RegExp(reUnescapedHtml.source)
|
||||
function escape(string) {
|
||||
return (string && reHasUnescapedHtml.test(string))
|
||||
? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr])
|
||||
: string
|
||||
: (string || '')
|
||||
}
|
||||
|
||||
export default escape
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import words from './words.js'
|
||||
import toString from './toString.js'
|
||||
|
||||
/**
|
||||
* Converts `string` to
|
||||
@@ -21,7 +22,7 @@ import words from './words.js'
|
||||
* // => 'foo-bar'
|
||||
*/
|
||||
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()
|
||||
), '')
|
||||
)
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import words from './words.js'
|
||||
import toString from './toString.js'
|
||||
|
||||
const reQuotes = /['\u2019]/g
|
||||
|
||||
@@ -22,7 +23,7 @@ const reQuotes = /['\u2019]/g
|
||||
* // => 'foo bar'
|
||||
*/
|
||||
const lowerCase = (string) => (
|
||||
words(`${string}`.replace(reQuotes, '')).reduce((result, word, index) => (
|
||||
words(toString(string).replace(reQuotes, '')).reduce((result, word, index) => (
|
||||
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) {
|
||||
const strLength = length ? stringSize(string) : 0
|
||||
if (!length || strLength >= length) {
|
||||
return string
|
||||
return (string || '')
|
||||
}
|
||||
const mid = (length - strLength) / 2
|
||||
return (
|
||||
|
||||
@@ -26,7 +26,7 @@ function padEnd(string, length, chars) {
|
||||
const strLength = length ? stringSize(string) : 0
|
||||
return (length && strLength < length)
|
||||
? (string + createPadding(length - strLength, chars))
|
||||
: string
|
||||
: (string || '')
|
||||
}
|
||||
|
||||
export default padEnd
|
||||
|
||||
@@ -26,7 +26,7 @@ function padStart(string, length, chars) {
|
||||
const strLength = length ? stringSize(string) : 0
|
||||
return (length && strLength < length)
|
||||
? (createPadding(length - strLength, chars) + string)
|
||||
: string
|
||||
: (string || '')
|
||||
}
|
||||
|
||||
export default padStart
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import words from './words.js'
|
||||
import toString from './toString.js'
|
||||
|
||||
/**
|
||||
* Converts `string` to
|
||||
@@ -24,7 +25,7 @@ import words from './words.js'
|
||||
* // => 'foo_2_bar'
|
||||
*/
|
||||
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()
|
||||
), '')
|
||||
)
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
if (!string || !chars) {
|
||||
return string
|
||||
return (string || '')
|
||||
}
|
||||
const strSymbols = stringToArray(string)
|
||||
const chrSymbols = stringToArray(chars)
|
||||
|
||||
@@ -26,7 +26,7 @@ function trimEnd(string, chars) {
|
||||
return string[methodName]()
|
||||
}
|
||||
if (!string || !chars) {
|
||||
return string
|
||||
return (string || '')
|
||||
}
|
||||
const strSymbols = stringToArray(string)
|
||||
const end = charsEndIndex(strSymbols, stringToArray(chars)) + 1
|
||||
|
||||
@@ -26,7 +26,7 @@ function trimStart(string, chars) {
|
||||
return string[methodName]()
|
||||
}
|
||||
if (!string || !chars) {
|
||||
return string
|
||||
return (string || '')
|
||||
}
|
||||
const strSymbols = stringToArray(string)
|
||||
const start = charsStartIndex(strSymbols, stringToArray(chars))
|
||||
|
||||
@@ -5,6 +5,7 @@ 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
|
||||
@@ -59,6 +60,9 @@ function truncate(string, options) {
|
||||
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)) {
|
||||
|
||||
@@ -32,7 +32,7 @@ const reHasEscapedHtml = RegExp(reEscapedHtml.source)
|
||||
function unescape(string) {
|
||||
return (string && reHasEscapedHtml.test(string))
|
||||
? string.replace(reEscapedHtml, (entity) => htmlUnescapes[entity])
|
||||
: string
|
||||
: (string || '')
|
||||
}
|
||||
|
||||
export default unescape
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import words from './words.js'
|
||||
import toString from './toString.js'
|
||||
|
||||
/**
|
||||
* Converts `string`, as space separated words, to upper case.
|
||||
@@ -20,7 +21,7 @@ import words from './words.js'
|
||||
* // => 'FOO BAR'
|
||||
*/
|
||||
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()
|
||||
), '')
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user