Files
lodash/test/number-coercion-methods.spec.js
2023-09-16 22:59:56 -07:00

261 lines
9.6 KiB
JavaScript

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