Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: toBoolean does more; narrower predicates #125

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/converters/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
},
"dependencies": {
"@accelint/constants": "workspace:0.1.3",
"@accelint/predicates": "workspace:0.1.3",
"typescript": "^5.6.3"
},
"$schema": "https://json.schemastore.org/package",
Expand Down
62 changes: 44 additions & 18 deletions packages/converters/src/to-boolean/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,57 @@
import { expect, it, describe } from 'vitest';
import { toBoolean } from './';

const truthy = [1, '1', 'on', 'true', 'yes', true, 'ON', 'YES', 'TRUE'];
const falsey = [
// biome-ignore lint/style/useNumberNamespace: testing value
const INFINITY = Infinity;

const falsy = [
'',
0,
0.0,
'0',
'off',
'false',
'no',
'0.000',
'0000.000',
false,
'false',
' FaLsE ',
void 0,
Number.NaN,
null,
undefined,
];
const truthy = [
[],
1,
'1',
true,
'true',
{},
'OFF',
'NO',
'FALSE',
'any non-empty string',
// 'Yes',
// 'yes',
// 'No',
// 'no',
// 'off',
// 'Off',
// 'OFF',
// 'On',
// 'on',
INFINITY,
-INFINITY,
Number.POSITIVE_INFINITY,
Number.NEGATIVE_INFINITY,
/abc/,
new Date(),
new Error('Fun times.'),
() => void 0,
];

describe('toBoolean', () => {
for (const item of truthy) {
it(`should return true for ${item}`, () => {
expect(toBoolean(item)).toBeTruthy();
});
}
it.each(falsy)('%s', (val) => {
expect(toBoolean(val)).toBe(false);
});

for (const item of falsey) {
it(`should return false for ${item}`, () => {
expect(toBoolean(item)).not.toBeTruthy();
});
}
it.each(truthy)('%s', (val) => {
expect(toBoolean(val)).toBe(true);
});
});
33 changes: 16 additions & 17 deletions packages/converters/src/to-boolean/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,29 @@
* governing permissions and limitations under the License.
*/

import { isTrue } from '@accelint/predicates';

/**
* Compare the given value against a custom list of `truthy` values.
* Returns true for any value not found to be a "false" value.
*
* String values are not case sensitive.
* **"false" values**
* - inherently false values: '' (empty string), 0, false, undefined, null, NaN
* - numeric zero: '0.000' - any number of leading or trailing zeros
* - string literal: 'false' - any capitalizations or space-padding
*
* _1, '1', 'on', 'true', 'yes', true_
* For more restrictive comparisons against: true, false, on, off, yes, no; see
* the predicates package (\@accelint/predicates).
*
* @pure
*
* @example
* toBoolean('on');
* // true
*
* toBoolean('yes');
* // true
*
* toBoolean('off');
* // false
*
* toBoolean('no');
* // false
* toBoolean(1); // true
* toBoolean(' FaLsE '); // false
* toBoolean(' true'); // true
* toBoolean('000.000'); // false
*/
export function toBoolean(val: unknown) {
return isTrue(val);
return !(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit for cleanliness (and any other long term checks we might do):

function notCheck(val) {
  return !val;
}

function stringCheck(val) {
  return string.trim().toLowerCase() === 'false';
}

function numberCheck(val) {
  return Number.parseFloat(`${val}`) === 0;
}

export function toBoolean(val) {
  return !(notCheck(val) || stringCheck(val) || numberCheck(val))
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems verbose for not much value and additional overhead - cognitive and execution - in my opinion. I would more often lean on comments for further documentation of why each exists than additional code to execute.

!val ||
`${val}`.trim().toLowerCase() === 'false' ||
Number.parseFloat(`${val}`) === 0
);
}
42 changes: 24 additions & 18 deletions packages/predicates/src/is-noyes/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,32 @@
* governing permissions and limitations under the License.
*/

import { describe, it, expect } from 'vitest';
import { isTrue, isYes, isFalse, isNo, isOn, isOff } from './';
import { describe, expect, it } from 'vitest';
import { isFalse, isNo, isOff, isOn, isTrue, isYes } from './';

const truthy = [1, '1', 'on', 'true', 'yes', true, 'ON', 'YES', 'TRUE'];
const falsey = [0, '0', 'off', 'false', 'no', false, 'OFF', 'NO', 'FALSE'];
type Config = {
negative: unknown[];
positive: unknown[];
predicate: (a: unknown) => boolean;
};

describe('boolean validators', () => {
for (const item of truthy) {
it(`should return true for ${item}`, () => {
expect(isOn(item)).toBeTruthy();
expect(isTrue(item)).toBeTruthy();
expect(isYes(item)).toBeTruthy();
});
}
const falsy = [0, '', false, ' false ', null, undefined, Number.NaN];
const truthy = [1, true, ' true '];

for (const item of falsey) {
it(`should return false for ${item}`, () => {
expect(isFalse(item)).toBeTruthy();
expect(isOff(item)).toBeTruthy();
expect(isNo(item)).toBeTruthy();
describe('boolean predicates', () => {
describe.each`
predicate | positive | negative
${isFalse} | ${[...falsy, ' false', '0']} | ${[...truthy, 'o', 'O', 'true', 'string with false', '0.00']}
${isNo} | ${[...falsy, ' n', 'N ', 'no', 'NO']} | ${[...truthy, 'yes', 'string with no']}
${isOff} | ${[...falsy, ' off', 'OFF ']} | ${[...truthy, 'on', 'string with off', 'of']}
${isOn} | ${[...truthy, ' on', 'ON ']} | ${[...falsy, 'of', 'string with on', 'o']}
${isTrue} | ${[...truthy, ' true']} | ${[...falsy, 'any string', {}, [], /abc/]}
${isYes} | ${[...truthy, ' yes', 'YeS ', 'y']} | ${[...falsy, 'no', 'string with yes', 2]}
`('$predicate.name', ({ predicate, ...lists }: Config) => {
describe.each(['positive', 'negative'])('%s matches', (list) => {
it.each(lists[list] as unknown[])('%j', (val) => {
expect(predicate(val)).toBe(list === 'positive');
});
});
}
});
});
Loading
Loading