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:
Luiz Américo
2019-08-26 10:13:56 -03:00
committed by John-David Dalton
parent abb54cc49a
commit e51a424513
18 changed files with 111 additions and 57 deletions

View File

@@ -11,6 +11,10 @@ import stringToArray from './stringToArray.js'
*/
function createCaseFirst(methodName) {
return (string) => {
if (!string) {
return ''
}
const strSymbols = hasUnicode(string)
? stringToArray(string)
: undefined

View File

@@ -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)
}, '')

View File

@@ -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

View File

@@ -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

View File

@@ -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()
), '')
)

View File

@@ -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
View File

@@ -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 (

View File

@@ -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

View File

@@ -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

View File

@@ -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()
), '')
)

View File

@@ -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);
});
});
});

View 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);
});
});
});

View File

@@ -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)

View File

@@ -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

View File

@@ -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))

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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()
), '')
)